Taproot is where Bitcoin script, elliptic-curve math, and cryptographic commitments finally come together cleanly.
I’ll build this from first principles, then assemble the full Taproot construction. No hand-waving, but I’ll keep the math conceptual, not abstract-symbol heavy.
0. Prerequisites (what I’ll assume you know)
I’ll assume you’re comfortable with:
- Elliptic curves conceptually: scalar × generator → point
- secp256k1 group arithmetic
- Hash functions as random oracles
- Bitcoin scripts at a high level
I won’t assume Schnorr familiarity beyond basics.
1. The core Taproot idea (one sentence)
Taproot commits to multiple spending conditions using a single public key, while revealing only the condition you actually use.
Mathematically:
- All spend paths are cryptographically committed
- Only one path is revealed
- Observers cannot tell whether alternatives existed
2. Why Taproot needed new math
Before Taproot:
-
P2SH / P2WSH:
- Reveal the entire script
- Reveal all branches
-
Multisig vs single-sig are distinguishable
-
Privacy and efficiency suffer
Taproot solves this by:
- Replacing script hashes with key tweaks
- Replacing ECDSA with Schnorr
- Using Merkle trees of scripts
3. Schnorr signatures (minimal but critical)
Taproot relies on BIP340 Schnorr signatures, which give us:
Key property we need
Public keys add linearly
If:
P = x·GQ = y·G
Then:
P + Q = (x + y)·G
ECDSA does not support this cleanly. Schnorr does.
This single fact enables Taproot.
4. X-only public keys (why 32 bytes works)
Traditional pubkey:
(x, y)
Taproot uses:
x-only pubkey
Why safe?
- For every
x, there are only two possible y values - Schnorr signatures define a canonical choice
- The parity of
yis encoded elsewhere (control block)
So:
- Public key = 32 bytes
- Parity is handled implicitly
5. Internal key: the starting point
Taproot begins with an internal public key:
P = x·G
-
Chosen by wallet
-
Can represent:
- Single key
- Aggregated MuSig key
-
By itself, it already allows a key-path spend
But Taproot wants optional scripts too.
6. Scripts become Merkle leaves
Suppose you want multiple spending conditions:
- Timelock recovery
- Multisig backup
- Emergency clause
Each script becomes a TapLeaf:
TapLeaf = H_tapleaf( leaf_version || script )
Where:
leaf_versioncurrently =0xC0script= raw Bitcoin script bytesH_tapleafis a tagged hash:
H_tapleaf(x) = SHA256(SHA256("TapLeaf") || SHA256("TapLeaf") || x)
(Tagged hashes prevent cross-protocol collisions.)
7. Build the Taproot Merkle tree
All TapLeaf hashes are combined pairwise:
TapBranch(a, b) = H_tapbranch( min(a,b) || max(a,b) )
Important:
- Ordering is lexicographic
- Tree shape doesn’t matter
- Only the final root matters
Final result:
merkle_root
If you have no scripts:
merkle_root = ∅
8. The Taproot tweak (THIS is the key math)
Now comes the heart of Taproot.
Compute tweak scalar
t = H_taptweak( P || merkle_root )
If no scripts:
t = H_taptweak( P )
Where:
H_taptweak(x) = SHA256(SHA256("TapTweak") || SHA256("TapTweak") || x)
Interpret t as a scalar mod n.
Compute output key
This is the Taproot output key:
Q = P + t·G
This is what goes on-chain.
Critical insight
-
The blockchain only sees
Q -
No one can tell:
- whether scripts exist
- how many scripts exist
-
Unless a script path is actually used
This is cryptographic commitment via elliptic-curve addition.
9. Address creation (P2TR)
The output script is:
OP_1 <x(Q)>
x(Q)= 32-byte x-coordinate ofQ- Address encodes this as
bc1p...
10. Spending via key path (most common)
If you control the private key x:
Private key tweak
q = x + t (mod n)
Now:
Q = q·G
So you can produce a normal Schnorr signature using q.
Witness:
<64-byte schnorr signature>
That’s it.
Observers learn:
- Only that a valid Schnorr signature exists
- No scripts
- No branches
- No policy
This is Taproot’s privacy jackpot.
11. Spending via script path (when needed)
Now suppose you use a script.
You must reveal:
- The script
- The Merkle proof
- The internal key
- The parity bit
This is encoded in the control block.
12. Control block: exact structure
Control block bytes:
[1 byte] control
[32 bytes] internal public key P
[32 bytes] merkle sibling #1
[32 bytes] merkle sibling #2
...
Control byte (very important)
Bits:
- bit 0: parity of output key Q (even/odd y)
- bits 1–7: leaf version (currently 0xC0)
So:
control = leaf_version | parity_bit
13. How script-path verification works
The verifier does:
- Extract
Pfrom control block - Hash the revealed script → TapLeaf
- Recompute Merkle root using provided siblings
- Compute:
t = H_taptweak( P || merkle_root )
Q' = P + t·G
- Check:
x(Q') == x(Q_from_scriptPubKey)
If equal → the script was committed at output creation time.
Then:
- Script is executed
- Witness stack items are applied
- Script must evaluate to true
14. Why control blocks don’t leak privacy
- Only one leaf is revealed
- Other branches are hidden by hashes
- Tree size and structure are unknown
- Even existence of alternative scripts is unknowable
15. Why Taproot is strictly better than P2WSH
| Feature | P2WSH | Taproot |
|---|---|---|
| Reveal all scripts | ❌ | ✅ no |
| Hide unused paths | ❌ | ✅ |
| Single-sig indistinguishable | ❌ | ✅ |
| Key aggregation | ❌ | ✅ |
| Signature size | Large | Fixed 64B |
16. Mental model (this helps a lot)
Think of Taproot as:
“A public key with optional encrypted clauses attached.”
- Key-path spend = don’t open the envelope
- Script-path spend = open exactly one envelope
- Control block = proof the envelope existed
17. One extremely important consequence (often missed)
Because:
Q = P + H(P || scripts)·G
👉 Scripts influence the public key
This means:
- You cannot change scripts later
- You cannot lie about policy
- Wallets must commit carefully
Taproot is binding, not just hiding.
18. Where this matters in real systems (your world)
Given your interest in hardware wallets / signers:
-
Key-path spends:
- Signer only needs tweaked private key
- No script parsing
- Very MCU-friendly
-
Script-path spends:
- Host provides script + control block
- Signer verifies tweak math
- Excellent for air-gapped PSBT flows
This is why Taproot is ideal for:
- Cold wallets
- Multisig with recovery paths
- Policy wallets (miniscript → taproot)
19. Next steps (I recommend this order)
If you want, next I can:
-
Derive a Taproot output numerically step-by-step
- actual hex
- actual hashes
-
Explain MuSig2 inside Taproot
-
Explain Taproot sighash math (BIP341)
-
Design a Taproot-based hardware wallet policy
-
Show Rust / embedded-friendly code for tweak verification
If you want maximum payoff, say “1” or “4”.