Blog Logo

2026-01-02 ~ 7 min read

A Mathematical Understanding of Taproot


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·G
  • Q = 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 y is 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_version currently = 0xC0
  • script = raw Bitcoin script bytes
  • H_tapleaf is 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 of Q
  • 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:

  1. The script
  2. The Merkle proof
  3. The internal key
  4. 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:

  1. Extract P from control block
  2. Hash the revealed script → TapLeaf
  3. Recompute Merkle root using provided siblings
  4. Compute:
t = H_taptweak( P || merkle_root )
Q' = P + t·G
  1. 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

FeatureP2WSHTaproot
Reveal all scripts✅ no
Hide unused paths
Single-sig indistinguishable
Key aggregation
Signature sizeLargeFixed 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:

  1. Derive a Taproot output numerically step-by-step

    • actual hex
    • actual hashes
  2. Explain MuSig2 inside Taproot

  3. Explain Taproot sighash math (BIP341)

  4. Design a Taproot-based hardware wallet policy

  5. Show Rust / embedded-friendly code for tweak verification

If you want maximum payoff, say “1” or “4”.


Photo of Yinhuan Yuan

Hi, I'm Yinhuan Yuan. I'm a software engineer based in Toronto. You can read more about me on yuan.fyi.