TeachMeBitcoin

P2TR Script Path - Full Taproot Execution

From TeachMeBitcoin, the free encyclopedia Reading time: 3 min

10. P2TR Script Path — Full Taproot Execution

Overview

The script path spend in P2TR allows spending via one of potentially many alternative scripts embedded in the Taproot tree — without revealing the other scripts or the internal public key. This is achieved through a Merkle proof system called the Tapscript tree.

Taproot Tree Structure

Scripts are organized in a binary Merkle tree of "tapleaves," each containing a script:

Root
         /    \
      Branch   Leaf C
      /    \
   Leaf A  Leaf B

Each Leaf = tagged_hash("TapLeaf", version || compact_size(script) || script)
Each Branch = tagged_hash("TapBranch", left || right)  (sorted lexicographically)
Root = top-level hash

The Merkle root is incorporated into the output key tweak, committing to all scripts.

Script Path Witness Structure

Witness:
  <script inputs satisfying the chosen script>
  <chosen tapscript>
  <control block>

Control block = leaf_version || internal_pubkey || merkle_path
  leaf_version: 1 byte (0xc0 for Tapscript, with parity bit)
  internal_pubkey: 32 bytes (x-only)
  merkle_path: 0–128 × 32-byte sibling hashes

Script Path Verification

Step 1: Verify the merkle path


- Recompute tapleaf hash from tapscript
  - Traverse up the tree using the merkle_path hashes
  - Verify the resulting root matches the one used to compute Q

Step 2: Verify the output key commitment

t = tagged_hash("TapTweak", internal_pubkey || merkle_root)
  Q == lift_x(internal_pubkey) + t·G
  (check x-coordinate parity via control block's parity bit)

Step 3: Execute the tapscript

Uses Tapscript rules (BIP 342), which differ slightly from legacy script:
    - OP_CHECKSIG uses Schnorr, not ECDSA
    - OP_CHECKMULTISIG is DISABLED (use OP_CHECKSIGADD instead)
    - Script resource limits are different (no 201-opcode limit)
    - OP_SUCCESS opcodes for soft-fork extensibility

Tapscript-Specific Rules (BIP 342)

Disabled opcodes in Tapscript:
  OP_CHECKMULTISIG, OP_CHECKMULTISIGVERIFY → cause script failure if encountered

New opcode: OP_CHECKSIGADD
  Usage: <pubkey> OP_CHECKSIGADD
  Pops: pubkey, n (accumulator), sig
  Pushes: n+1 if sig valid, n if sig empty, fails if sig invalid-but-non-empty
  Enables: threshold signatures via OP_NUMEQUAL at the end

OP_SUCCESS opcodes:
  Any opcode in range [0x50, 0x60, 0x62, 0x66–0x67, 0x68–0x6a, 0x6d–0x6f,
                       0x71, 0x73, 0x75, 0x77–0x7a, 0x7c–0x7e, 0x80, 0x82–0x98, 0xa6–0xaf]
  If encountered: script succeeds unconditionally (for future soft-fork use)

Example: 3-of-5 Tapscript Multisig

Tapscript using OP_CHECKSIGADD:

<pk1> OP_CHECKSIG
<pk2> OP_CHECKSIGADD
<pk3> OP_CHECKSIGADD
<pk4> OP_CHECKSIGADD
<pk5> OP_CHECKSIGADD
OP_3 OP_NUMEQUAL

Spending witness inputs (3 valid signatures, 2 empty):
<sig1> <sig2> OP_0 <sig4> OP_0

This is more efficient than OP_CHECKMULTISIG for large M-of-N and allows exact threshold enforcement with fail-on-invalid-signature semantics.

☕ Help support TeachMeBitcoin

TeachMeBitcoin is an ad-free, open-source educational repository curated by a passionate team of Bitcoin researchers and educators for public benefit. If you found our articles helpful, please consider supporting our hosting and ongoing content updates with a clean donation:

Ethereum: 0x578417C51783663D8A6A811B3544E1f779D39A85
Bitcoin: bc1q77k9e95rn669kpzyjr8ke9w95zhk7pa5s63qzz
Solana: 4ycT2ayqeMucixj3wS8Ay8Tq9NRDYRPKYbj3UGESyQ4J
Address copied to clipboard!