Menu
BIP-bip-341 Final

BIP-341: Taproot

Taproot specification. Key-path and script-path spending with improved privacy and efficiency.

Type Bitcoin Improvement Proposal
Number bip-341
Status Final
Authors Pieter Wuille, Jonas Nick, Anthony Towns
Original https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki
Requires

BIP-341: Taproot

Taproot is Bitcoin’s most significant upgrade since SegWit. It combines Schnorr signatures with Merkelized Abstract Syntax Trees (MAST) for improved privacy and flexibility.

Overview

Taproot allows spending via:

  1. Key path: Single Schnorr signature (most efficient)
  2. Script path: Reveal and satisfy one script from a tree
                    ┌─────────────┐
                    │ Tweaked Key │
                    │   Q = P + H │
                    └─────────────┘

        ┌─────────────────┴─────────────────┐
        │                                   │
   Key Path                            Script Path
   (signature)                    (reveal + satisfy)

Output Structure

scriptPubKey:

OP_1 <32-byte tweaked pubkey>

Just 34 bytes—one of the smallest output types.

Address Format

Bech32m with prefix bc1p:

bc1p5d7rjq7g6rdk2yhzks9smlaqtedr4dekq08ge8ztwac72sfr9rusxg3297

Key Tweaking

The output key Q is derived from:

  • Internal key P
  • Merkle root of scripts (or nothing)
t = tagged_hash("TapTweak", P || merkle_root)
Q = P + t × G

Key-Only Tweak

No scripts, just key spending:

t = tagged_hash("TapTweak", P)
Q = P + t × G

Key Path Spending

When all parties agree, spend with single signature:

Witness:

<schnorr signature>

That’s it—64 bytes.

Signing

def sign_key_path(internal_key, merkle_root, message):
    # Compute tweaked private key
    t = tagged_hash("TapTweak", internal_key.pubkey || merkle_root)
    tweaked_key = internal_key + t

    # Sign with Schnorr
    return schnorr_sign(message, tweaked_key)

Benefits

  • Privacy: Looks identical to single-sig
  • Efficiency: Smallest witness possible
  • Flexibility: Can still use script path if needed

Script Path Spending

Reveal one script from merkle tree:

Witness:

<script arguments>
<script>
<control block>

Control Block

<leaf version + parity> || <internal key P> || <merkle proof>
FieldSizeDescription
Leaf version1 byte0xc0 for Tapscript
Internal key32 bytesP (x-only)
Merkle proof32×n bytesPath to script

Merkle Tree

Scripts organized in binary tree:

            Root
           /    \
         AB      CD
        /  \    /  \
       A    B  C    D
       ↓    ↓  ↓    ↓
      s1   s2 s3   s4

Reveal only the script you’re using + proof path.

Tapscript (BIP-342)

Modified script rules for Taproot:

Changes from SegWit v0

FeatureSegWit v0Tapscript
SignatureECDSASchnorr
MultisigOP_CHECKMULTISIGOP_CHECKSIGADD
Max script10,000 bytesUnlimited
Max ops201Unlimited
AnnexesN/ASupported

OP_CHECKSIGADD

Replaces OP_CHECKMULTISIG for efficient k-of-n:

<sig1> <pk1> OP_CHECKSIG
<sig2> <pk2> OP_CHECKSIGADD
<sig3> <pk3> OP_CHECKSIGADD
<3> OP_EQUAL

Fails fast on invalid signatures.

Example: 2-of-3 Multisig

Old Way (P2WSH)

OP_2 <pk1> <pk2> <pk3> OP_3 OP_CHECKMULTISIG
  • Always reveals all 3 pubkeys
  • 71-byte signatures × 2
  • ~250 bytes witness

Taproot Way

Key path: MuSig2 aggregate signature (64 bytes)

Script path fallback: Three 2-of-2 scripts

Script 1: <pk1> CHECKSIG <pk2> CHECKSIGADD 2 EQUAL
Script 2: <pk1> CHECKSIG <pk3> CHECKSIGADD 2 EQUAL
Script 3: <pk2> CHECKSIG <pk3> CHECKSIGADD 2 EQUAL

Benefits:

  • Cooperative case: 64 bytes (like single-sig)
  • Non-cooperative: Reveals only 2 keys
  • Better privacy in common case

Implementation

Creating Taproot Output

const bitcoin = require('bitcoinjs-lib');

// Internal key
const internalKey = Buffer.from('...', 'hex');

// No scripts (key-only)
const { address, output } = bitcoin.payments.p2tr({
  internalPubkey: internalKey,
  network: bitcoin.networks.bitcoin
});

With Script Tree

const { Taptree } = require('bitcoinjs-lib');

// Define scripts
const script1 = bitcoin.script.compile([...]);
const script2 = bitcoin.script.compile([...]);

// Build tree
const tree = [
  { output: script1 },
  { output: script2 }
];

const { address } = bitcoin.payments.p2tr({
  internalPubkey: internalKey,
  scriptTree: tree
});

Key Path Spend

const psbt = new bitcoin.Psbt();

psbt.addInput({
  hash: txid,
  index: vout,
  witnessUtxo: { script: output, value: amount },
  tapInternalKey: internalKey
});

psbt.addOutput({ address: recipient, value: sendAmount });

// Sign with tweaked key
psbt.signTaprootInput(0, tweakedSigner);
psbt.finalizeAllInputs();

Script Path Spend

psbt.addInput({
  hash: txid,
  index: vout,
  witnessUtxo: { script: output, value: amount },
  tapInternalKey: internalKey,
  tapLeafScript: [{
    leafVersion: 0xc0,
    script: script1,
    controlBlock: controlBlock
  }]
});

// Provide script arguments in witness
psbt.updateInput(0, {
  tapScriptSig: [{
    pubkey: signerPubkey,
    signature: schnorrSignature,
    leafHash: leafHash
  }]
});

Privacy Benefits

All Spends Look Similar

Spend TypeOn-Chain Appearance
Single-sigbc1p… + 64-byte witness
2-of-2 MuSigbc1p… + 64-byte witness
Complex contract (key path)bc1p… + 64-byte witness

Only script-path reveals contract details.

MAST Hides Unused Paths

Complex contract with 10 conditions:

  • Only reveal the one you use
  • Other 9 remain private
  • Observer can’t tell they exist

Fee Comparison

Transaction TypeWitness Size
P2PKH~108 bytes
P2WPKH~108 bytes
P2TR key path64 bytes
P2TR script (simple)~100+ bytes

Recommendation: Use key path when possible.

For Agents

When to Use Taproot

  • ✅ New wallets (default choice)
  • ✅ Multi-party setups (MuSig2)
  • ✅ Complex contracts with likely key-path
  • ✅ Privacy-sensitive applications

When P2WPKH May Be Better

  • Legacy system compatibility
  • Simple single-sig only needed
  • Existing infrastructure constraints

Libraries

LanguageLibrary
JavaScriptbitcoinjs-lib v6+, @noble/secp256k1
Rustrust-bitcoin, bdk
Pythonpython-bitcoinlib (limited)

Machine-Readable Summary

{
  "bip": 341,
  "title": "Taproot: SegWit version 1 spending rules",
  "status": "final",
  "activated": "2021-11-14",
  "block_height": 709632,
  "witness_version": 1,
  "address_prefix": "bc1p",
  "spending_paths": ["key-path", "script-path"],
  "features": ["schnorr", "mast", "tapscript"],
  "related_bips": [340, 342]
}