Control Block (Taproot)
Learn what a Taproot control block is and how it proves a script's position in the Merkle tree.
Key Takeaways
- A control block is the witness element that proves a script belongs to a Taproot output's committed tree. It is required for every script-path spend and is not needed for key-path spends.
- It contains three pieces of data: a leaf version byte (with the output key parity bit), the 32-byte x-only internal public key, and a series of 32-byte sibling hashes forming the Merkle inclusion proof.
- The verifier uses the control block to reconstruct the Merkle root, compute the key tweak, and confirm it matches the on-chain output key: if it matches, the script is proven to be part of the taptree.
What Is a Control Block?
A Taproot control block is a data structure included in the witness of a script-path spend. Its purpose is to prove that the script being executed was committed to when the output was created, without revealing any other scripts in the tree. Defined in BIP 341, the control block is what makes Taproot's selective disclosure possible: a spender reveals only the branch they use, keeping every other spending condition private.
Think of it as a receipt with a chain of cryptographic evidence. The spender hands the network a script and says "this script was part of the tree committed to this output." The control block provides exactly the data a verifier needs to confirm that claim independently.
Control blocks are only relevant for script-path spends. When a Taproot output is spent via the key path, the witness contains just a single Schnorr signature and no control block is needed at all.
How It Works
A Taproot output commits to both an internal public key and (optionally) a taptree of spending scripts. The output key seen on chain is the internal key tweaked by the Merkle root of that tree. When someone wants to spend via a script, they must prove the script was part of the original tree. The control block carries the data for that proof.
Control Block Structure
The control block is a byte string of length 33 + 32m, where m is the number of Merkle path hashes (from 0 to 128). It has three parts:
- First byte: the leaf version in the top 7 bits and the parity of the output key in the lowest bit. For Tapscript (the current script system), the leaf version is 0xc0, so the first byte is either 0xc0 (even parity) or 0xc1 (odd parity).
- Bytes 1 through 32: the 32-byte x-only internal public key. This is the key that was tweaked to produce the output key.
- Bytes 33 onward: zero or more 32-byte hashes forming the Merkle inclusion path. Each hash is the sibling node needed at one level of the tree to reconstruct the root.
// Control block layout (33 + 32m bytes)
// where m = number of Merkle path hashes
[ leaf_version | parity_bit ] // 1 byte
[ internal_public_key ] // 32 bytes (x-only)
[ merkle_path_hash_1 ] // 32 bytes (optional)
[ merkle_path_hash_2 ] // 32 bytes (optional)
...
[ merkle_path_hash_m ] // 32 bytes (optional)A single-leaf tree has no siblings, so the control block is just 33 bytes: the version/parity byte plus the internal key. A tree of depth 3 produces a control block of 33 + (3 x 32) = 129 bytes. BIP 341 caps tree depth at 128 levels, giving a maximum control block size of 33 + (128 x 32) = 4,129 bytes.
Verification Process
When a node validates a script-path spend, it performs the following steps using the control block, the revealed script, and the on-chain output key:
- Compute the tapleaf hash: apply the tagged hash function with the tag "TapLeaf" over the leaf version byte, a compact-size encoding of the script length, and the script bytes.
- Walk the Merkle path: starting from the tapleaf hash, iterate through each 32-byte sibling hash in the control block. At each step, sort the current hash and the sibling hash lexicographically, then compute a tagged hash with the tag "TapBranch" over the sorted pair. This sorting rule eliminates the need for left/right direction bits.
- After consuming all path hashes, the result is the Merkle root of the taptree.
- Compute the tweak: apply the tagged hash "TapTweak" over the concatenation of the internal public key and the Merkle root.
- Tweak the internal public key by this value and compare the result (including parity from the control block's first byte) against the output key stored on chain. If they match, the script is proven to be part of the committed tree.
// Verification pseudocode
leaf_hash = TaggedHash("TapLeaf", leaf_version || compact_size(script) || script)
// Walk the Merkle path
k = leaf_hash
for each sibling in merkle_path:
if k < sibling:
k = TaggedHash("TapBranch", k || sibling)
else:
k = TaggedHash("TapBranch", sibling || k)
// k is now the Merkle root
tweak = TaggedHash("TapTweak", internal_key || k)
// Check: internal_key + tweak * G == output_key
// Parity must match the control block's lowest bitThis process uses only tagged hashes and elliptic curve point addition. No signature verification happens during the commitment check itself: signature and script execution are separate steps.
Key-Path Spend vs. Script-Path Spend
Taproot outputs support two spending methods, and the control block is what distinguishes them on chain:
| Property | Key-Path Spend | Script-Path Spend |
|---|---|---|
| Witness data | Single Schnorr signature (64 bytes) | Script inputs + script + control block |
| Control block | Not present | Required (33 + 32m bytes) |
| Privacy | Indistinguishable from any other key-path spend | Reveals the executed script (but not other branches) |
| Cost | Cheapest (minimal witness) | Higher (control block + script data adds weight) |
| Typical use | Cooperative spending (all parties agree) | Fallback conditions (timelocks, hash locks, multisig branches) |
In practice, well-designed Taproot outputs use the key path for the most common spending scenario and reserve script-path branches for fallback or dispute conditions. This means the control block only appears on chain when the cooperative path fails, which is the minority of transactions. For a deeper look at how Taproot changed Bitcoin's signature and spending model, see the Taproot and Schnorr signatures explained research article.
Leaf Versions and Future Upgrades
The leaf version byte in the control block is a forward-compatibility mechanism. Currently, the only defined leaf version is 0xc0, which signals that the script should be interpreted as Tapscript (defined in BIP 342). Scripts with an unrecognized leaf version are treated as "anyone can spend" under current consensus rules, allowing future soft forks to define new scripting languages without changing the control block format.
This design means the control block structure itself is stable: new opcodes, new signature schemes, or entirely new script interpreters can be introduced by assigning a new leaf version, while the Merkle proof and tweak verification logic remain unchanged.
Use Cases
Multi-Branch Spending Conditions
A Taproot output can commit to dozens of spending scripts in a balanced Merkle tree. When one branch is used, the control block proves that branch's membership with only log2(n) sibling hashes. For a tree with 8 leaves, the control block needs just 3 hashes (96 extra bytes) regardless of which branch is executed. The other 7 branches remain completely hidden.
Complex Custody Arrangements
Institutions might construct a taptree with branches for different authorization levels: a key-path for routine operations (using MuSig2 or FROST aggregated keys), a script branch for time-locked recovery, and another branch for emergency spending with a different key set. The control block keeps the unused branches private until they are needed.
Lightning and Layer 2 Protocols
Lightning Network channels built on simple Taproot channels use script-path spends for force-close scenarios. The control block proves that the force-close script was part of the channel's funding output commitment. In cooperative closes, the key path is used instead, and the control block never appears on chain. Layer 2 protocols like Spark also benefit from Taproot's script commitment model for exit transactions, where the control block proves a user's claim to their funds.
Risks and Considerations
On-Chain Footprint
While the control block is efficient relative to revealing an entire script tree, deep trees still produce large control blocks. Each level adds 32 bytes of witness data. For most real-world use cases, trees are shallow (1 to 4 levels), but protocol designers should consider the cost trade-off when building deep taptrees.
Privacy Implications
A script-path spend reveals both the executed script and the control block, which discloses the tree depth and the internal public key. While other branches remain hidden, the depth itself can be a fingerprint. A control block with 5 Merkle hashes tells an observer the tree has between 6 and 32 leaves, which may narrow down the type of contract being used.
Internal Key Exposure
The control block reveals the internal public key in plaintext. In a key-path spend, only the tweaked output key is visible. Protocol designers who want to keep the internal key private should ensure script-path spending is the fallback, not the default path.
Validation Overhead
Verifying a control block requires computing tagged hashes at each level of the Merkle path plus an elliptic curve point operation for the tweak check. For shallow trees this is negligible, but the 128-level depth limit means a worst-case control block requires 128 hash computations. Batch validation of Schnorr signatures can offset some of this cost at the block level.
This glossary entry is for informational purposes only and does not constitute financial or investment advice. Always do your own research before using any protocol or technology.