第5章:Taproot:Bitcoin 脚本系统的演化#

参考: examples/ch05_simple_taproot.py
最后更新: 2025-12-06


Taproot 代表了 Bitcoin 脚本演化的顶点,展示了最复杂的智能合约如何看起来与简单支付完全相同。这种革命性方法将 Schnorr 签名与密码学密钥调整(key tweaking)相结合,创建 Bitcoin 最先进和私密的授权系统。

5.1 Taproot 承诺:统一隐私#

Taproot 的根本突破是支付统一性。无论交易代表:

  • 简单的单签名支付

  • 复杂的多方合约

  • Lightning Network 通道

  • 具有多个授权级别的企业资金库

它们在花费之前在区块链上看起来完全相同。这种统一性由两个数学创新实现:Schnorr 签名和密钥调整。

5.2 Schnorr 签名:数学基础#

在理解 Taproot 架构之前,我们需要掌握使一切成为可能的数学优雅:Schnorr 签名及其变革属性,这些属性革命性地改变了 Bitcoin 的授权系统。

为什么选择 Schnorr?ECDSA 的局限性#

Bitcoin 最初使用 ECDSA(椭圆曲线数字签名算法)进行数字签名,但这一选择带来了 Schnorr 完全消除的重大局限性:

ECDSA 问题:

  • 可延展性:签名可以在不使其失效的情况下被修改

  • 无法聚合:多个签名无法组合

  • 更大尺寸:签名通常为 71-72 字节

  • 复杂验证:需要更多计算资源

  • 无线性性:数学运算不保持关系

Schnorr 的革命性优势:

  • 不可篡改:在 BIP340 下,确定性 nonce、x-only 公钥和严格编码规则消除了 ECDSA 中看到的第三方可延展性向量

  • 密钥聚合:多个公钥可以组合为一个

  • 单签名输出:产生单个聚合签名

  • 紧凑尺寸:固定 64 字节签名

  • 高效验证:更快更简单的验证过程

  • 数学线性性:支持高级密码学构造

改变游戏规则的属性:线性性#

使 Taproot 成为可能的数学突破是 Schnorr 的线性性属性

If Alice has signature A for message M
And Bob has signature B for the same message M  
Then A + B creates a valid signature for (Alice + Bob)'s combined key

这个简单的数学关系支持三个革命性能力:

  1. 密钥聚合:多个人可以将他们的公钥组合为一个

  2. 单签名输出:多方可以协作产生一个统一的签名

  3. 密钥调整:密钥可以通过承诺确定性修改

注意:”单签名输出”指的是通过 MuSig2(钱包级协议)在链上产生一个 BIP340 签名,而不是跨输入的共识级签名聚合。

视觉对比:ECDSA vs Schnorr#

ECDSA Multisig (3-of-3):
┌─────────────────────────────────────┐
│           Transaction               │
├─────────────────────────────────────┤
│ Alice Signature:   [71 bytes]       │
│ Bob Signature:     [72 bytes]       │
│ Charlie Signature: [70 bytes]       │
├─────────────────────────────────────┤
│ Total Size: ~213 bytes              │
│ Verifications: 3 separate           │
│ Privacy: REVEALS 3 participants     │
│ Appearance: 👥👥👥 (obviously multi) │
└─────────────────────────────────────┘

Schnorr Aggregated (3-of-3):
┌─────────────────────────────────────┐
│           Transaction               │
├─────────────────────────────────────┤
│ Aggregated Signature: [64 bytes]    │
├─────────────────────────────────────┤
│ Total Size: 64 bytes                │
│ Verifications: 1 single check       │
│ Privacy: REVEALS nothing about #    │
│ Appearance: 👤 (looks like single)  │
└─────────────────────────────────────┘

隐私魔法:

External Observer sees:
┌─────────────────┬─────────────────┐
│   Transaction A │   Transaction B │
├─────────────────┼─────────────────┤
│ 64-byte signature│ 64-byte signature│
│ Looks like: 👤  │ Looks like: 👤  │
└─────────────────┴─────────────────┘

Reality:
┌─────────────────┬─────────────────┐
│   Transaction A │   Transaction B │
├─────────────────┼─────────────────┤
│ Actually: 👤    │ Actually: 👥👥👥 │
│ (1 person)      │ (3 people)      │
└─────────────────┴─────────────────┘

🔮 Impossible to distinguish from outside!

5.3 密钥调整:通往 Taproot 的桥梁#

Taproot 通过密钥调整(key tweaking)(在 BIP340/341/342 哲学中也称为可调整承诺(tweakable commitment))利用 Schnorr 的线性性。

概念上:

t = H("TapTweak" || internal_pubkey || merkle_root)

形式上(BIP341):

t  = int(HashTapTweak(xonly_internal_key || merkle_root_or_empty)) mod n

P' = P + t * G
d' = d + t

Even-Y 要求(BIP340):
Taproot 使用 x-only 公钥——但 secp256k1 上的实际点仍然有两个可能的 y 值(偶数/奇数)。
BIP340 规则是:最终调整的输出密钥必须对应于 even-y 点
如果点最终是 odd-y,实现将私钥翻转为 d' = n d',使得 P' = d'*G 落在偶数分支上。

(为什么这很重要:在 script-path 支出中,这个奇偶性被编码到控制块的最低位。如果现在不跟踪这个,script-path 以后将无法验证。)

密钥调整结构的视觉表示#

Internal Key (P) ─────────► + tweak ─────────► Output Key (P')
                              ▲                      │
                              │                      │
                       Merkle Root ◄────────────────┘
                    script_path_commitment

密钥关系图:

┌─────────────────┐    ┌─────────────────┐    ┌─────────────────┐
│   Internal Key  │    │   Tweak Value   │    │   Output Key    │
│       (P)       │    │   t = H(P||M)   │    │      (P')       │
│                 │───►│                 │───►│                 │
│ User's original │    │ Deterministic   │    │ Final address   │
│ private key     │    │ from commit     │    │ seen on chain   │
└─────────────────┘    └─────────────────┘    └─────────────────┘
        │                        ▲                        │
        │                        │                        │
        └─── Can compute d' ─────┘                        │
                                                          │
                                 ┌─────────────────────────┘
                                 │
                                 ▼
                      ┌─────────────────┐
                      │   Merkle Root   │
                      │       (M)       │
                      │                 │
                      │ Commitment to   │
                      │ all possible    │
                      │ spending paths  │
                      └─────────────────┘

其中:

  • P = 内部密钥(Internal Key)(原始公钥,用户控制)

  • M = Merkle 根(Merkle Root)(对所有可能花费条件的承诺)

  • t = 调整值(Tweak Value)(从 P 和 M 确定性计算)

  • P' = 输出密钥(Output Key)(最终 Taproot 地址,出现在区块链上)

  • d' = 调整后的私钥(Tweaked Private Key)(用于 key-path 支出)

这种数学关系确保:

  1. 任何人都可以从 P 和承诺计算 P’(给定内部密钥 P 和(可选)Merkle 根 M)

  2. 只有密钥持有者可以从 d 和调整值计算 d’

  3. 关系 d’ × G = P’ 得到保持(签名验证有效)

关键代码#

# Key-path-only: tree 为空,t = HashTapTweak(internal_pubkey || b''), P' = P + t*G
address = pubkey.get_taproot_address([])
# 示例 1: Key-path-only Taproot 地址(btcaaron)
# 参考: examples/ch05_simple_taproot.py

from btcaaron import Key, TapTree

sender = Key.from_wif("cPeon9fBsW2BxwJTALj3hGzh9vm8C52Uqsce7MzXGS1iFJkPF4AT")
program = TapTree(internal_key=sender).build()

print("=== KEY-PATH-ONLY TAPROOT 地址 ===")
print(f"内部密钥 (x-only): {sender.xonly}")
print(f"Taproot 地址:     {program.address}")
print(f"叶子脚本: {program.leaves}(空)")
print("btcaaron 内部完成: t=HashTapTweak(x-only||merkle_root), P'=P+t×G")
=== KEY-PATH-ONLY TAPROOT 地址 ===
内部密钥 (x-only): 898711e6bf63f5cbe1b38c05e89d6c391c59e9f8f695da44bf3d20ca674c8519
Taproot 地址:     tb1pjyjeruun8pc5ln3wtv2d6lsxqn55frpyc83kn473h7848d0kj73sxy3ku8
叶子脚本: [](空)
btcaaron 内部完成: t=HashTapTweak(x-only||merkle_root), P'=P+t×G

密钥调整的关键洞察:

  1. 双重花费路径:调整后的密钥创建两种花费方法:

    • Key Path:使用调整后的私钥直接签名(协作)

    • Script Path:揭示内部公钥并证明脚本执行(回退)

  2. 密码学绑定:调整值将输出密钥密码学绑定到特定脚本承诺

  3. 确定性验证:任何人都可以验证调整后的密钥正确承诺到特定条件

  4. 通过不可区分性实现隐私:调整后的公钥在数学上与任何其他 Schnorr 公钥不可区分

5.4 为什么这能实现统一外观#

Schnorr 签名和密钥调整的结合创造了”统一外观”魔法:

Simple Payment:
├── Internal Key: Just a regular private key
├── Script Commitment: Empty (no conditions)
├── Tweaked Key: Internal key + H(key || empty)
└── Spending: 64-byte Schnorr signature

Complex Contract:
├── Internal Key: Same regular private key
├── Script Commitment: Merkle root of 100 conditions
├── Tweaked Key: Internal key + H(key || merkle_root)
└── Spending: 64-byte Schnorr signature (if cooperative)

🔍 External View: IDENTICAL 64-byte signatures!

5.5 简单 Taproot 交易:整合所有内容#

现在让我们通过基本的 Taproot 到 Taproot 交易看看这在实践中如何工作:

# 示例 2: 简单 Taproot 交易(btcaaron)
# 参考: examples/ch05_simple_taproot.py

from btcaaron import Key, TapTree

sender = Key.from_wif("cPeon9fBsW2BxwJTALj3hGzh9vm8C52Uqsce7MzXGS1iFJkPF4AT")
program = TapTree(internal_key=sender).build()

tx = (program.keypath()
    .from_utxo("b0f49d2f30f80678c6053af09f0611420aacf20105598330cb3f0ccb8ac7d7f0", 0, sats=29200)
    .to("tb1p53ncq9ytax924ps66z6al3wfhy6a29w8h6xfu27xem06t98zkmvsakd43h", 29000)
    .sign(sender)
    .build())

print("=== TAPROOT 交易 ===")
print(f"从:   {program.address}")
print(f"到:   tb1p53ncq9ytax924ps66z6al3wfhy6a29w8h6xfu27xem06t98zkmvsakd43h")
print(f"金额: 29,000 sats (手续费: 200 sats)")
print(f"TXID: {tx.txid}")
print()
print("见证: 64 字节 Schnorr 签名,与任何 Taproot 支付无法区分")
=== TAPROOT 交易 ===
从:   tb1pjyjeruun8pc5ln3wtv2d6lsxqn55frpyc83kn473h7848d0kj73sxy3ku8
到:   tb1p53ncq9ytax924ps66z6al3wfhy6a29w8h6xfu27xem06t98zkmvsakd43h
金额: 29,000 sats (手续费: 200 sats)
TXID: 2a1312702ca1bb5f69d4099dd9f1da74c9601e885512e19a99ef018544c47c24

见证: 64 字节 Schnorr 签名,与任何 Taproot 支付无法区分

关键观察:

  1. Taproot 地址生成get_taproot_address() 自动应用调整过程

  2. Schnorr 签名sign_taproot_input() 产生 64 字节签名

  3. 最小见证:见证栈只需签名(SIGHASH_DEFAULT 时 64 字节)

  4. 相同外观:与任何 Taproot 交易不可区分

5.6 真实交易分析#

TXID: a3b4d0382efd189619d4f5bd598b6421e709649b87532d53aecdc76457a42cb6

Input:  ScriptPubKey OP_1 912591f3...5f697a3, Witness [7d25fbc9...da99f3]
Output: tb1p53ncq9...

见证:64 字节 Schnorr 签名(r 32B + s 32B),无公钥。

5.7 Taproot 栈执行(Key Path 简要)#

│ (empty)              │  →  OP_1  →  │ 912591f3...5f697a3 (output_key) │
└───────────┘                         └───────────────────────────────────┘
     →  见证压栈  →  │ 7d25fbc9...da99f3 (schnorr_signature) │
                    │ 912591f3...5f697a3 (output_key)        │
                    └───────────────────────────────────────┘
     →  Schnorr 验证  →  │ 1 (TRUE) │

5.8 不可区分性#

视觉对比#

Legacy P2PKH:
├── ScriptPubKey: OP_DUP OP_HASH160 <20-byte-hash> OP_EQUALVERIFY OP_CHECKSIG
├── ScriptSig: <signature> <public_key>
└── Size: ~225 bytes
   Information Revealed: Single signature spending

SegWit P2WPKH:
├── ScriptPubKey: OP_0 <20-byte-hash>
├── Witness: [signature, public_key]
└── Size: ~165 bytes
   Information Revealed: Single signature spending

Taproot P2TR (Simple):
├── ScriptPubKey: OP_1 <32-byte-output-key>
├── Witness: [schnorr_signature]
└── Size: ~135 bytes
   Information Revealed: Nothing about internal complexity

Taproot P2TR (Complex Contract):
├── ScriptPubKey: OP_1 <32-byte-output-key>
├── Witness: [schnorr_signature]
└── Size: ~135 bytes
   Information Revealed: Nothing about internal complexity

魔法:简单和复杂的 Taproot 交易在花费之前完全不可区分

# 可运行:解析 64 字节 Schnorr 签名为 r/s(标准库)
sig_hex = "7d25fbc9b98ee0eb09ed38c2afc19127465b33d6120f4db8d4fd46e532e30450d7d2a1f1dd7f03e8488c434d10f4051741921d695a44fb774897020f41da99f3"
sig = bytes.fromhex(sig_hex)
r, s = sig[:32], sig[32:]
print(f"r ({len(r)}B): {r.hex()[:16]}...{r.hex()[-8:]}")
print(f"s ({len(s)}B): {s.hex()[:16]}...{s.hex()[-8:]}")

5.9 编程差异:SegWit vs Taproot#

# SegWit P2WPKH: address = pk.get_segwit_address(); witness = [sig, pubkey]
# Taproot P2TR:  address = pubkey.get_taproot_address([]); witness = [sig]

要点:Taproot 地址需公钥、见证只需签名(无公钥)。

5.10 协作优势#

Taproot 为协作创造强大激励:

Cooperative Spending (Key Path):
├── Parties: Alice, Bob, Charlie (all agree)
├── Witness: [64-byte signature]
├── Size: ~135 bytes
├── Privacy: Maximum (looks like single-sig)
└── Efficiency: Optimal

Non-Cooperative Spending (Script Path):
├── Parties: Alice, Bob, Charlie (dispute)
├── Witness: [script_data, revealed_script, control_block]
├── Size: ~200-500 bytes
├── Privacy: Partial (reveals one condition)
└── Efficiency: Reduced but still functional

经济激励:

  • 协作奖励:更小的费用,更好的隐私

  • 冲突成本:更大的交易,减少隐私

  • 对齐:技术优化与经济协作对齐

章节总结#

Taproot 通过两个关键数学创新代表了 Bitcoin 交易的范式转变:

Schnorr 签名:线性性属性支持密钥聚合、单签名输出,最重要的是密钥调整。这创造了固定 64 字节签名,可以表示任何复杂程度,同时看起来完全相同。

密钥调整(可调整承诺):数学关系 P' = P + t×G 允许密钥通过脚本承诺确定性修改,创建双重花费路径,同时保持密码学安全性。

结果:复杂的智能合约在计算和观察上与简单支付完全相同,在不牺牲功能的情况下提供前所未有的隐私。

隐私革命:所有 Taproot 交易在花费之前看起来完全相同,使得无法区分:

  • 简单的单签名支付

  • 复杂的多方合约

  • Lightning Network 操作

  • 企业资金库交易

效率提升

  • 更小的交易大小(64 字节签名)

  • 更快的验证(单签名检查)

  • 减少区块链膨胀(未使用的条件保持私有)

协作激励:Taproot 使经济激励与技术优化对齐——协作成为最有效的选择。

随着密钥调整奠定密码学基础,下一步是探索任意智能合约条件如何在 Merkle 树内紧凑承诺——在揭示之前保持不可见。

在下一章中,我们将探索 Merkle 树如何在统一外观背后组织复杂的脚本条件,展示无限的花费条件如何被承诺和证明,而不揭示未使用的替代方案。