Companion notes to my book Mastering Taproot.
HODLing is the beginning.
But Bitcoin was meant to be programmed.
"Not Just HODLing: Real Bitcoin Script Engineering" starts here.
The Universal Truth
Every Bitcoin addressβββfrom the genesis block to the latest Taproot outputβββfollows one fundamental pattern:
Lock: Commit to a condition
Unlock: Reveal proof that you satisfy it
This isnβt a feature of SegWit or Taproot. Itβs the DNA of Bitcoin itself.
Evolution of Commit-Reveal

The abstraction deepens. The privacy improves. The core logic remains unchanged.
Locking ScriptΒ Formats
P2PKH
OP_DUP OP_HASH160 <20-byte pubkey_hash> OP_EQUALVERIFY OP_CHECKSIG
P2SH
OP_HASH160 <20-byte script_hash> OP_EQUAL
P2WPKH
OP_0 <20-byte pubkey_hash>
P2WSH
OP_0 <32-byte script_hash>
P2TR
OP_1 <32-byte output_pubkey>
How nodes differentiate SegWit types:
-
OP_0+ 20 bytes β P2WPKH -
OP_0+ 32 bytes β P2WSH -
OP_1+ 32 bytes β P2TR
The Verification Flow
Universal Pattern
Step 1: Verify Commit β Does the revealed content match what was promised?
Step 2: Execute Script β Do the conditions evaluate to true?
P2SH Verification
HASH160(redeem_script) == hash in locking script?
Match β Execute redeem_script
P2WSH Verification
SHA256(witness_script) == hash in locking script?
Match β Execute witness_script
P2TR Script Path Verification
1. Extract internal_pubkey from control block
2. Compute tapleaf_hash from script
3. Calculate merkle_root using Merkle path
4. tweak = TaggedHash("TapTweak", internal_pubkey || merkle_root)
5. computed_pubkey = internal_pubkey + tweak Γ G
6. computed_pubkey == output_pubkey in locking script?
7. Match β Execute tapscript
Stack Execution Visualized
Example 1:Β P2PKH
Locking Script: OP_DUP OP_HASH160 <pubkey_hash> OP_EQUALVERIFY OP_CHECKSIG
Unlock Data: <signature> <pubkey>
Initial Stack OP_DUP OP_HASH160 PUSH hash
βββββββββββ βββββββββββ βββββββββββ βββββββββββ
β pubkey β β pubkey β β pk_hash β β pk_hash β β expected
βββββββββββ€ βββββββββββ€ βββββββββββ€ βββββββββββ€
β sig β β pubkey β β pubkey β β pk_hash β β computed
βββββββββββ βββββββββββ€ βββββββββββ€ βββββββββββ€
β sig β β sig β β pubkey β
βββββββββββ βββββββββββ βββββββββββ€
β sig β
βββββββββββ
OP_EQUALVERIFY OP_CHECKSIG Final
βββββββββββ βββββββββββ βββββββββββ
β pubkey β β 1(true) β β 1(true) β β
βββββββββββ€ βββββββββββ βββββββββββ
β sig β
βββββββββββ
Example 2: P2SH withΒ Timelock
Redeem Script: OP_3 OP_CSV OP_DROP OP_DUP OP_HASH160 <hash> OP_EQUALVERIFY OP_CHECKSIG
Unlock Data: <signature> <pubkey>
Initial Stack PUSH 3 OP_CSV OP_DROP
βββββββββββ βββββββββββ βββββββββββ βββββββββββ
β pubkey β β 3 β β 3 β β pubkey β
βββββββββββ€ βββββββββββ€ βββββββββββ€ βββββββββββ€
β sig β β pubkey β β pubkey β β sig β
βββββββββββ βββββββββββ€ βββββββββββ€ βββββββββββ
β sig β β sig β
βββββββββββ βββββββββββ
Check nSeq β₯ 3
Continues with standard P2PKH logic...
Example 3:Β P2WPKH
Locking Script: OP_0 <20-byte pubkey_hash>
Witness: <signature> <pubkey>
Node sees OP_0 + 20 bytes, implicitly executes P2PKH logic:
Verification: HASH160(pubkey) == hash in locking script? Match β Continue
Execution (implicit P2PKH logic):
Initial Stack OP_DUP OP_HASH160 PUSH hash
βββββββββββ βββββββββββ βββββββββββ βββββββββββ
β pubkey β β pubkey β β pk_hash β β pk_hash β β expected
βββββββββββ€ βββββββββββ€ βββββββββββ€ βββββββββββ€
β sig β β pubkey β β pubkey β β pk_hash β β computed
βββββββββββ βββββββββββ€ βββββββββββ€ βββββββββββ€
β sig β β sig β β pubkey β
βββββββββββ βββββββββββ βββββββββββ€
β sig β
βββββββββββ
OP_EQUALVERIFY OP_CHECKSIG Final
βββββββββββ βββββββββββ βββββββββββ
β pubkey β β 1(true) β β 1(true) β β
βββββββββββ€ βββββββββββ βββββββββββ
β sig β
βββββββββββ
Difference from P2PKH: Identical logic, unlock data moved from scriptSig to witness.
Example 4: P2WSH (2-of-3 Multisig)
Locking Script: OP_0 <32-byte script_hash>
Witness Script: OP_2 <pubkey1> <pubkey2> <pubkey3> OP_3 OP_CHECKMULTISIG
Witness: OP_0 <sig1> <sig2> <witness_script>
Verification: SHA256(witness_script) == hash? Match β Execute witness_script
Execution:
Initial Stack (witness data excluding witness_script)
βββββββββββ
β sig2 β
βββββββββββ€
β sig1 β
βββββββββββ€
β 0 β β CHECKMULTISIG bug placeholder
βββββββββββ
PUSH 2 PUSH pk1 PUSH pk2 PUSH pk3
βββββββββββ βββββββββββ βββββββββββ βββββββββββ
β 2 β β pk1 β β pk2 β β pk3 β
βββββββββββ€ βββββββββββ€ βββββββββββ€ βββββββββββ€
β sig2 β β 2 β β pk1 β β pk2 β
βββββββββββ€ βββββββββββ€ βββββββββββ€ βββββββββββ€
β sig1 β β sig2 β β 2 β β pk1 β
βββββββββββ€ βββββββββββ€ βββββββββββ€ βββββββββββ€
β 0 β β sig1 β β sig2 β β 2 β
βββββββββββ βββββββββββ€ βββββββββββ€ βββββββββββ€
β 0 β β sig1 β β sig2 β
βββββββββββ βββββββββββ€ βββββββββββ€
β 0 β β sig1 β
βββββββββββ βββββββββββ€
β 0 β
βββββββββββ
PUSH 3 OP_CHECKMULTISIG Final
βββββββββββ βββββββββββ βββββββββββ
β 3 β β 1(true) β β 1(true) β β
βββββββββββ€ βββββββββββ βββββββββββ
β pk3 β
βββββββββββ€ Verify: at least 2 of 3
β pk2 β signatures are valid
βββββββββββ€
β pk1 β
βββββββββββ€
β 2 β
βββββββββββ€
β sig2 β
βββββββββββ€
β sig1 β
βββββββββββ€
β 0 β β consumed (legacy bug)
βββββββββββ
The CHECKMULTISIG bug: Due to an off-by-one error in the original implementation, an extra stack element is consumed. Hence the dummy OP_0 at the start of witness.
Example 5: Taproot ScriptΒ Path
Tapscript: OP_2 OP_CSV OP_DROP <pubkey> OP_CHECKSIG
Witness: <signature> <script> <control_block>
Verification: control_block + script β reconstruct output_pubkey β match locking script
Execution:
Initial Stack PUSH 2 OP_CSV OP_DROP
βββββββββββ βββββββββββ βββββββββββ βββββββββββ
β sig β β 2 β β 2 β β sig β
βββββββββββ βββββββββββ€ βββββββββββ€ βββββββββββ
β sig β β sig β
βββββββββββ βββββββββββ
Check nSeq β₯ 2
PUSH pubkey OP_CHECKSIG Final
βββββββββββ βββββββββββ βββββββββββ
β pubkey β β 1(true) β β 1(true) β β
βββββββββββ€ βββββββββββ βββββββββββ
β sig β
βββββββββββ
What SegWit ActuallyΒ Changed
SegWit wasnβt a revolution. It was a relocation:

What Taproot ActuallyΒ Changed
Taproot optimized the commit-reveal paradigm:

Control Block Structure:
[1 byte: leaf_version + parity] [32 bytes: internal_pubkey] [32 bytes Γ N: Merkle path]
Verification Formula:
output_pubkey = internal_pubkey + TaggedHash("TapTweak", internal_pubkey || merkle_root) Γ G
Key Takeaways
-
Every address type is commit-revealβββLock with a hash/pubkey commitment, unlock by revealing the preimage
-
Locking scripts are routing labelsβββNodes use the format to determine validation rules; they donβt execute on stack
-
Verification precedes executionβββFirst prove βyou have the right to run this scriptβ, then verify βdoes the script passβ
-
Stack execution is universalβββRegardless of address type, itβs all push, pop, verify signatures, check timelocks
-
The trajectory is clear:
-
Commitments grow more abstract (pubkey β hash β tweak)
-
Privacy improves (expose everything β expose only whatβs used)
-
Efficiency increases (fee optimization, verification optimization)
Understanding these primitives is the foundation for everything that followsβββLightning channels, covenant proposals, and whatever comes next.
By Aaron Recompile on November 28, 2025.
Exported from Medium on July 3, 2026.