In Chapter 11, we saw Taproot's elegant key path: a single Schnorr signature, verified against the output key, with no scripts revealed. But the output key \(Q\) is more than it appears. Recall that \(Q = P + tG\), where \(t = H(\texttt{"TapTweak"},\; P_x \| c)\) and \(c\) is the Merkle root of a tree of scripts. In the key path, that tree remained invisible—a silent promise of alternative spending conditions that were never needed.
This chapter explores what happens when the key path isn't available. Perhaps one party has lost their key. Perhaps a timelock has expired. Perhaps the cooperative case failed and a fallback condition must be exercised. In these situations, the spender reaches into the script tree and reveals exactly one branch—the script path—while every other branch remains hidden.
Our specimen comes from the same historic block as Chapter 11: block 709,635, just three blocks after Taproot activation. It contains two inputs spending from the same parent transaction—one via key path, one via script path—making it a perfect side-by-side comparison. The script path input reveals a simple TapScript, a control block with a Merkle proof, and the internal key that was hidden during key path spending.
Our specimen is from block 709,635 (November 14, 2021)—the same block as our Chapter 11 specimen. It spends two P2TR outputs from the same parent transaction, one via key path and one via script path.
Txid:†
37777defed8717c581b4c0509329550e344bdc14ac38f71fc050096887e535c8
| Metric | Value | Notes |
|---|---|---|
| Total size | 358 B | Including witness |
| Stripped size | 123 B | Without marker, flag, witness |
| Weight | 727 WU | \(123 \times 3 + 358\) |
| vsize | 182 vB | \(727/4\) |
| Fee | 18,200 sats | \(491,750 + 491,750 - 965,300\) |
| Fee rate | 100.0 sat/vB | Clean round number |
Both inputs spend from vanity P2TR addresses whose bech32m encoding begins with bc1ptapr00t—literally spelling "taproot" in the address. These were bruteforced by grinding key pairs until the bech32m encoding produced the desired prefix. On Taproot's activation day, these vanity addresses served as a celebration and demonstration of the new address format.
This transaction has two inputs, both spending from the same parent transaction but from different outputs (vout 0 and vout 1). The critical difference is in their witnesses:
| Input 0 | Input 1 | |
|---|---|---|
| Spending path | Key path | Script path |
| Witness items | 1 | 3 |
| Witness data | 64-byte Schnorr sig | 64-byte sig + 34-byte TapScript + 65-byte control block |
| Witness bytes | 66 | 167 |
Input 0 is a key path spend—identical to Chapter 11. The witness contains a single 64-byte Schnorr signature verified against the output key. We parsed this pattern thoroughly in the previous chapter.
Input 1 is a script path spend—the subject of this chapter. Its witness contains three items, and parsing them will reveal the internal key, the Merkle proof, and the actual script being executed.
The spending path is determined entirely by the witness structure (BIP 341†):
No flag byte, no version field—just the element count.
Input 1's witness contains three items:
Item count: 03 (3 items)
Item 0 — Schnorr signature (64 bytes):
7b 5d 61 4a 46 10 bf 91 96 77 57 91 fc c5 89 59
7c a0 66 dc d1 00 48 e0 04 cd 4c 73 41 bb 4b b9
0c ee 47 05 19 2f 3f 7d b5 24 e8 06 7a 52 22 c7
f0 9b af 29 ef 6b 80 5b 83 27 ec d1 e5 ab 83 ca
Item 1 — TapScript (34 bytes):
20 f5 b0 59 b9 a7 22 98 cc be ff f5 9d 9b 94 3f
7e 0f c9 1d 8a 3b 94 4a 95 e7 b6 39 0c c9 9e b5
f4 ac
Item 2 — Control block (65 bytes):
c0 d9 df df 0f e3 c8 3e 98 70 09 5d 67 ff f5 9a
80 56 da d2 8c 6d fb 94 4b b7 1c f6 4b 90 ac e9
a7 77 6b 22 a1 18 5f b2 dc 95 24 f6 b1 78 e2 69
31 89 bf 01 65 5d 7f 38 f0 43 92 36 68 dc 5a f4
5b
The three items are processed in reverse order during verification:
The 34-byte TapScript is:
| Hex | Opcode | Meaning |
|---|---|---|
20 | OP_PUSHBYTES_32 | Push next 32 bytes |
f5b059b9…9eb5f4 | x-only pubkey | 32-byte public key |
ac | OP_CHECKSIG | Verify Schnorr signature |
This is the simplest possible TapScript: push a public key, check a signature. It is functionally identical to the P2PK scripts of Chapter 4—but with x-only keys and Schnorr signatures instead of uncompressed keys and ECDSA.
Execution proceeds as follows: the 64-byte Schnorr signature (witness item 0) is on the stack. The TapScript pushes the 32-byte public key, then OP_CHECKSIG pops both and verifies the Schnorr signature against the key. If valid, OP_TRUE is pushed; the script succeeds.
Although both use the opcode 0xac, OP_CHECKSIG behaves differently in TapScript (BIP 342†):
OP_FALSE), and a signature of length \(\neq 64\) or \(65\) is an immediate script failure, not a push of false.The 65-byte control block carries everything the verifier needs to reconstruct the output key from the TapScript:
| Field | Size | Value |
|---|---|---|
| Leaf version + parity | 1 B | c0 |
| Internal key \(P\) | 32 B | d9dfdf0f… ace9a7 |
| Merkle sibling hash | 32 B | 776b22a1…5af45b |
The first byte c0 encodes two pieces of information:
The next 32 bytes are the internal key \(P\) (x-only):
d9dfdf0f e3c83e98 70095d67 fff59a80 56dad28c 6dfb944b b71cf64b 90ace9a7
This is the key that was hidden during key path spending. In a key path spend (Input 0), the internal key is never revealed—only the tweaked output key \(Q\) appears. In a script path spend, the internal key must be disclosed so the verifier can recompute the tweak and confirm that \(Q = P + tG\).
The remaining bytes are the Merkle proof: a sequence of 32-byte hashes, one per level of the tree. Our specimen has exactly one hash:
776b22a1 185fb2dc 9524f6b1 78e26931 89bf0165 5d7f38f0 43923668 dc5af45b
This tells us the tree has two leaves—the TapScript we are executing and one sibling script. The sibling script is never revealed. We know only its hash, which is all we need to reconstruct the Merkle root.
The control block is always \(33 + 32d\) bytes, where \(d\) is the depth of the executed leaf in the Merkle tree. For our specimen: \(33 + 32 \times 1 = 65\) bytes (depth 1, two-leaf tree). A deeper tree with 8 leaves would have \(d = 3\) and a control block of \(33 + 96 = 129\) bytes. The maximum depth is 128 (BIP 341), giving a theoretical control block of \(33 + 4,096 = 4,129\) bytes—but in practice, trees rarely exceed depth 4 or 5.
To verify the script path spend, the node must reconstruct the Merkle root from the TapScript and the Merkle proof, then verify that the output key \(Q\) commits to that root. The construction uses two tagged hashes.
Each leaf in the tree is hashed with the TapLeaf tag: \[ \text{TapLeaf}(s) = \text{tagged\_hash}(\texttt{"TapLeaf"},\; v \| \text{compact\_size}(|s|) \| s) \] where \(v\) is the leaf version byte and \(s\) is the TapScript.
For our specimen:
v = c0
s = 20f5b059… f4ac (34 bytes)
compact_size(34) = 22
TapLeaf(s) = tagged_hash("TapLeaf", c0 \| 22 \| s)
The resulting leaf hash is:
36471a88 c27bfaba f3f6ffd3 84fc7981 6b595d23 48f50f82 a08b4dde 15f2933e
Internal nodes combine two children using the TapBranch tag. To ensure a canonical ordering (so the same tree always produces the same root regardless of construction order), the two children are sorted lexicographically: \[ \text{TapBranch}(a, b) = \text{tagged\_hash}(\texttt{"TapBranch"},\; \text{sort}(a, b)) \]
For our specimen, the two children are:
a = TapLeaf hash = 3647…933e
b = sibling hash = 776b… f45b
Since \(a < b\) lexicographically (both start with 3 vs 7), we concatenate \(a \| b\) and hash:
Merkle root = TapBranch(a, b)
= fc547ac3 bb0bc2d6 bfd4c647 e8342ad6 2ed89a15 b005ccc2 dde9efc4 cb8573f0
With the Merkle root in hand, the verifier can now reconstruct the output key and confirm the commitment.
\[ t = \text{tagged\_hash}(\texttt{"TapTweak"},\; P_x \| \text{Merkle root}) \]
Using our internal key \(P_x\) = d9dfdf0f… ace9a7 and the Merkle root computed above: \[ t = \texttt{cba3d466\;138a3e99\;b46db0de\;59356ee2\;aedbcaa8\;b3862098\;4e0d0205\;3153b335} \]
\[ Q' = P + t \cdot G \]
Lifting \(P_x\) to a full curve point (even \(y\)) and computing the scalar multiplication: \[ Q'_x = \texttt{5f4237bd\;7f93c694\;03a30c6b\;641f27cc\;f5201090\;152fcf15\;96474221\;307831c3} \]
The prevout scriptPubKey for Input 1 is: 51 (OP_1) 20 (push 32) 5f4237bd … 307831c3 (output key \(Q\))
The output key \(Q\) from the scriptPubKey matches \(Q'\) exactly: \[ Q'_x = Q_x \]
The script was indeed committed to by the output key. The verifier now executes the TapScript with the provided witness inputs (the Schnorr signature) and, if the script succeeds, the spend is valid.
Our specimen reveals the executed TapScript (<pubkey> OP_CHECKSIG) and one Merkle sibling hash (776b… f45b). But what is the sibling script? We have no idea. The hash is all that was revealed. It could be:
OP_CHECKSIG (a backup key).OP_CHECKSIGADD.OP_CHECKLOCKTIMEVERIFY).This is Taproot's privacy innovation: unused branches remain invisible. Compare with P2SH (Chapter 6) and P2WSH (Chapter 10), where the entire redeem/witness script is revealed when any branch is executed. A P2WSH 2-of-3 multisig reveals all three public keys, even though only two signed. A Taproot script tree reveals only the leaf that was used.
| Type | What's revealed at spend | What remains hidden |
|---|---|---|
| P2SH | Entire redeem script (all keys, all conditions) | Nothing |
| P2WSH | Entire witness script (all keys, all conditions) | Nothing |
| P2TR key path | Nothing (just a signature) | Internal key, all scripts |
| P2TR script path | One script leaf + Merkle proof + internal key | All other script leaves |
For a tree with \(n\) leaves, a script path spend reveals 1 leaf and \(\log_2 n\) hashes. The remaining \(n - 1\) leaves stay hidden. An 8-leaf tree reveals 1 script and 3 hashes; the other 7 scripts remain private.
Our specimen uses a simple single-key TapScript, but Tapscript introduces a new opcode for multi-signature schemes: OP_CHECKSIGADD† (0xba).
In legacy and SegWit v0 scripts, \(m\)-of-\(n\) multisig used OP_CHECKMULTISIG, which had two problems:
OP_0 (Chapter 7).OP_CHECKSIGADD takes a different approach. It pops three items: a public key, a signature, and a running counter. It verifies the signature against the key; if valid, it increments the counter. A 2-of-3 multisig TapScript looks like:
| Push first public key (32 B x-only) |
|---|---|
OP_CHECKSIG | Check sig against key_1; push 1 or 0 |
| Push second public key |
OP_CHECKSIGADD | Check sig against key_2; add result to counter |
| Push third public key |
OP_CHECKSIGADD | Check sig against key_3; add result to counter |
OP_2 | Push threshold \(m = 2\) |
OP_NUMEQUAL | Counter \(?=\) 2 |
Each key is checked independently against its corresponding signature. Invalid signatures push an empty byte array (which callers must provide explicitly—no implicit skipping). The counter accumulates the valid signature count, and the final OP_NUMEQUAL checks whether enough signatures were collected.
If all \(n\) parties are available to cooperate, MuSig2 key aggregation (Chapter 11) compresses the \(n\)-of-\(n\) case into a single key path spend—no script needed at all. OP_CHECKSIGADD is for the \(m\)-of-\(n\) case where \(m < n\), or for complex policies where key aggregation isn't applicable (e.g., combining timelocks with multisig). The script path is the fallback when cooperative key aggregation fails or doesn't apply.
Our specimen has a two-leaf tree, but real-world P2TR outputs can encode far more complex policies. Consider a wallet with three spending conditions:
The tree structure:
If Leaf A is executed (Alice spending after 30 days), the control block contains:
Total control block: \(33 + 64 = 97\) bytes. Leaf B and Leaf C remain hidden—their scripts are never revealed.
The position of a leaf in the tree affects the size of its Merkle proof. Leaves closer to the root have shorter proofs (fewer sibling hashes). Wallet designers should place the most likely spending conditions near the root and unlikely fallbacks deeper in the tree. This minimizes fees in the common case while keeping fallback options available.
How does a script path spend compare to a key path spend in weight?
| Witness component | Key path | Script path (depth 1) |
|---|---|---|
| Item count | 1 B | 1 B |
| Signature (+ length prefix) | 1 + 64 = 65 B | 1 + 64 = 65 B |
| TapScript (+ length prefix) | — | 1 + 34 = 35 B |
| Control block (+ length prefix) | — | 1 + 65 = 66 B |
| Total witness | 66 B | 167 B |
The script path witness is 101 bytes larger—all in witness weight (1 WU per byte). For a single-input transaction, this adds 25 vbytes. The extra cost is the "insurance premium" for the ability to fall back to a script when the key path fails.
In the key path case, the entire script tree is free—it exists only as a commitment in the tweak, consuming zero bytes on-chain. This is the fundamental insight of Taproot: you only pay for the complexity you actually use.
OP_CHECKSIGADD replaces OP_CHECKMULTISIG in TapScript, fixing the off-by-one bug and enabling linear signature checking.With Parts I through IV complete, we have parsed every major Bitcoin output type—from Satoshi's original P2PK to Taproot's script path. Part V turns to special transactions: coinbase rewards, data embedding, timelocks, fee management, and the protocol-layer constructions (Lightning, Ordinals, Runes) that build on the primitives we have studied.
Exercises
OP_CHECKSIG in TapScript use Schnorr verification instead of ECDSA?776b… f45b. What does this hash represent? What can we learn about the corresponding script?36471a88…15f2933e.Solutions
L1. By the witness stack size. If the witness contains exactly 1 element (64 or 65 bytes), it is a key path spend—the element is a Schnorr signature. If the witness contains 2 or more elements, it is a script path spend—the last element is the control block, the second-to-last is the TapScript, and all preceding elements are inputs to the script.
L2. The three items are: (1) script inputs (e.g., signatures), (2) the TapScript being executed, (3) the control block (internal key + Merkle proof). During verification, the control block is processed first (to reconstruct and verify the output key commitment), then the TapScript is executed with the script inputs on the stack.
L3. \(33 + 32d\) bytes, where \(d\) is the depth of the executed leaf. The first byte is the leaf version + parity bit (1 B), the next 32 bytes are the internal key, and each of the \(d\) Merkle proof levels contributes 32 bytes.
L4. Because TapScript (BIP 342) uses the Taproot execution context, which is defined by BIP 340/341 to use Schnorr signatures. The legacy OP_CHECKSIG used ECDSA because that was the only signature scheme available. Schnorr signatures are fixed-length (64 bytes), non-malleable, and support batch verification and key aggregation—properties ECDSA lacks.
L5. The hash represents a second TapScript leaf in the Merkle tree—a sibling of the executed script. We can learn nothing about the actual script it contains. The hash is a SHA-256 commitment (via the TapLeaf tagged hash); without a preimage, the script is completely hidden. This is by design: only the executed branch is revealed.
H1. From the raw hex (358 bytes total):
H2. The 65-byte control block:
c0.
d9dfdf0f e3c83e98 … 4b90ace9a7.776b22a1 185fb2dc … 68dc5af45b.The control block has one 32-byte hash, indicating a tree of depth 1 (two leaves).
H3. The tagged hash formula is:
\[ \text{tagged\_hash}(\text{tag}, m) = \text{SHA-256}(\text{SHA-256}(\text{tag}) \| \text{SHA-256}(\text{tag}) \| m) \]
With \(\text{tag} = \texttt{"TapLeaf"}\) and \(m = \texttt{c0} \| \texttt{22} \| \texttt{20f5b059\ldots f4ac}\) (36 bytes total: 1 + 1 + 34):
Result: 36471a88 c27bfaba f3f6ffd3 84fc7981 6b595d23 48f50f82 a08b4dde 15f2933e.
This was verified computationally using Python's hashlib.sha256.
H4. BIP 341 limits the Merkle proof to 128 hashes (depth 128). Maximum leaves: \(2^{128}\) (astronomically large—far more than any practical application). Maximum control block: \(33 + 32 \times 128 = 33 + 4,096 = 4,129\) bytes.
P1. Let \(a\) and \(b\) be two leaf hashes. TapBranch computes \(H((a,b) \| (a,b))\).
Case 1: Tree built with \(a\) as left child, \(b\) as right: \(H((a,b) \| (a,b))\).
Case 2: Tree built with \(b\) as left, \(a\) as right:
\(H((b,a) \| (b,a)) = H((a,b) \| (a,b))\).
Since \(\text{sort}\) and \(\|\) are symmetric operations, both cases produce identical inputs to the hash. The root is the same regardless of construction order. This extends inductively to all levels: at each TapBranch node, the two children are sorted before hashing.
P2. The verifier reconstructs \(Q' = P + tG\) from the internal key \(P\) and the tweak \(t\). The resulting point \(Q'\) has some \(y\)-coordinate that is either even or odd. The output key \(Q\) on-chain is stored as an x-only key (32 bytes, no parity information). Two distinct curve points share the same \(x\)-coordinate: \((x, y)\) and \((x, p - y)\).
Without the parity bit, the verifier would not know which of these two points is \(Q\). If the verifier chose the wrong one, the comparison \(Q' ?= Q\) would fail even for a legitimate spend. The parity bit (bit 0 of the control block's first byte) resolves this ambiguity: it tells the verifier which square root to select when lifting \(Q_x\) to a full point. If parity is 0, \(Q\) has even \(y\); if 1, odd \(y\).
P3. The Merkle proof for leaf \(i\) reveals the hashes along the path from leaf \(i\) to the root. At each level, the sibling hash is disclosed. However, each sibling hash is the output of \(\text{tagged\_hash}(\texttt{"TapBranch"}, \ldots)\) or \(\text{tagged\_hash}(\texttt{"TapLeaf"}, \ldots)\).
Finding the preimage of a tagged hash requires breaking the preimage resistance of SHA-256: given \(h = \text{SHA-256}(m)\), find \(m\). This is computationally infeasible under the assumption that SHA-256 is a secure hash function (preimage resistance: \(2^{256}\) work).
Therefore, knowing a sibling hash reveals nothing about the sibling's script content. The privacy guarantee holds under the preimage resistance of SHA-256.
C1. Optimal tree design for four conditions (A is key path, B/C/D are scripts):
Suggested tree (depth-weighted by probability):
Control block sizes: Leaf B: \(33 + 32 = 65\) bytes (depth 1). Leaf C or D: \(33 + 64 = 97\) bytes (depth 2). The most likely fallback (B) has the cheapest proof.
C2. The Ordinals protocol creates a TapScript containing an "envelope":
OP_FALSE OP_IF
The OP_FALSE OP_IF … OP_ENDIF block is never executed (dead code), but the data is part of the TapScript and thus committed to by the Merkle tree. The inscription is "revealed" by spending via the script path, which discloses the TapScript (including the embedded data) in the witness.
The witness discount is critical: witness data costs 1 WU per byte vs 4 WU for non-witness. An inscription of 100 KB costs 100,000 WU in the witness vs 400,000 WU if it were in the non-witness part. This \(4\times\) discount makes large data embeddings economically viable within the 4,000,000 WU block weight limit.
B1. A coinbase transaction creates outputs but has no regular inputs to spend. The coinbase output can be any valid scriptPubKey type, including P2TR. A P2TR coinbase output would be 34 bytes: OP_1 <32-byte output key>. The miner would later spend it via key path (single Schnorr sig) or script path. As Taproot adoption increased, many mining pools switched their coinbase outputs from P2PKH or P2SH to P2TR for the privacy and flexibility benefits.
B2. Lightning commitment transactions use P2WSH outputs that reveal the full witness script when spent. A force-close reveals the complete HTLC structure, including timelocks, revocation keys, and payment hashes—exposing the channel's internal mechanics. P2TR script path reveals only the executed leaf; unused conditions (other timelocks, other HTLCs) remain hidden. This is why Lightning implementations are migrating to Taproot channels ("simple taproot channels")—force-closes reveal less about the channel structure, improving privacy even in adversarial scenarios.