密码学 32:Schnorr 与 Taproot——签名结构怎样改变支出与披露
一个 Bitcoin 输出有“持有人直接签名”与“符合某个备用条件”两条支出路径。看到链上的付款地址,就能猜出备用条件吗?通常不能直接从 Taproot 输出看出未被用过的脚本分支;但实际走脚本分支支出时,交易必须公开被用的脚本及其承诺路径。有人据此宣称“Taproot 给所有交易完全匿名”,这是把某次支出披露的范围夸大成了对地址、金额、关联数据与未来历史的整体隐私保证。
先把 Schnorr 签名和支出条件分开
BIP 340 定义 secp256k1 上 x-only 公钥(32 字节)和 64 字节 Schnorr 签名;当前规范接受任意长度字节串作为消息,早期“必须 32 字节”的限制已经移除。Bitcoin Taproot 的交易签名仍会按 BIP 341 先得到相应 sighash,具体库接口也可能只接收 32 字节摘要;不要把应用层 BIP 340 能签任意消息,误读成 Taproot 可跳过交易摘要规则。它不是“用私钥加密消息”:交易内容仍可读,签名只对相应字节和密钥关系给出可公开核查的证据。nonce 生成须遵守规范,复用等实现错误可能泄露私钥;tagged hash 将不同用途的摘要输入分域,不能把 TapTweak 当成普通 SHA-256 任意替换。
Taproot 输出不直接等于内部公钥。BIP 341 从内部 x-only 公钥 P 与可选脚本树根 m 构造输出密钥 Q:t = tagged_hash("TapTweak", bytes(P) || m),将 t 解释成规定范围内的标量,计算 Q = P + t·G。输出承诺 Q。没有脚本树时 m 为空;存在脚本时,该根承诺脚本叶及分支的准确结构。实际花费的两种路径验证对象不同:
1 | |
当内部密钥可用时,key path 往往少披露备用脚本;一旦使用 script path,本次脚本和必要路径要对验证者可见。若撤掉控制块和输出密钥匹配检查,攻击者可以拿另一棵树下为真的脚本冒充锁定在这个输出上的条件;如果只检查脚本里签名是否为真,却不检查脚本与原输出的承诺关系,就没有证明它能花这个输出。这和 29 的“证明路径依赖可信根”是同一个信任问题,但 Bitcoin 使用自己的 Taproot 编码,不可照搬教学二叉树或 Ethereum trie。
Schnorr 的线性结构可以供经过正确协议设计的多人签名使用,可能让协作支出呈现为一份公钥与一份签名;不能简单相加任意公钥和签名就宣称安全,具体 nonce、恶意参与者与会话绑定另见 E04。更不能将聚合签名写作“自动达成共识”:区块包含与冲突历史仍由节点规则和链选择决定。
一个真向量与一个尚未跑的链实验
examples/cryptography/32_schnorr_taproot.py 使用 BIP 340 公布的第 0 行向量:公钥 F9308A…E036F9、全零 32 字节消息和辅助随机值,以及预期签名 E90783…536C0。这里的 32 字节来自向量和本实验实际调用的 coincurve 接口约束,不能反推 BIP 340 只允许签固定长度摘要。成熟 secp256k1 库验证并重新签出相同结果;改一个消息字节会失败。脚本另计算无脚本树的 TapTweak,经库执行输出密钥 tweak,检查它与内部密钥不同。这不是 P2TR 地址编码、实际 Bitcoin 交易 sighash、脚本路径支出或网络接受测试。
先按仓库内 examples/cryptography/README.md 完成环境准备;以下命令从仓库根目录执行。
1 | |
测试通过 1 项;Bitcoin Core regtest 的 key path、script path 与故意错误控制块实验仍 NOT_RUN(官方包下载校验未完成)。向量中的私钥为已公开测试数据,不能控制任何真实资产。结论只能落在“所指定 BIP 340 消息/签名可由该库核对”和“该输入的输出 tweak 可算出”,不能声称脚本已经被真实 Bitcoin 节点执行,或“没有看到脚本,所以永远不会披露”。
练习及答案
画图题: 在 P、脚本树根 m、Q、key-path 签名、script-path 控制块五处标出公开对象。攻击者改了被执行脚本而不改变锁定输出 Q 时,哪个关系必须失败?
答案: P 与可选 m 用来算 Q;Q 在输出的 witness program 中公开。key path 提交满足 Q 规则的 Schnorr 签名;script path 提交执行脚本和控制块以重建与原 Q 一致的承诺。私自换脚本后叶/路径、根、tweak 或 Q 核对不能同时保持一致(除非违反所依赖的哈希/曲线假设)。正确 Q 只是“此条件属于这个输出”,不决定整笔交易的合法性或历史。
实验变更题: 把第 0 行的 message 首字节改 01,不重签;再把 TapTweak 标签改为 other。两处各影响哪一条断言?
答案: 原签名对改后消息验签应失败;若把“错误消息应失败”的预期也一同改写就失去反例。标签改变会使 tweak 输入域改变,算出的 Q 通常不同,不能再拿它当同一 BIP 341 输出;单看“Q 与 P 不同”仍可能为真,必须加正确向量/输出核对,而不能拿模糊的非相等断言冒充规范兼容。
资料与衔接
- BIP 340 与公开测试向量 · BIP 341 支出规则;本章未实现完整 sighash 或脚本解释器。
- 27:Bitcoin sighash 只讲 v0 P2WPKH,不可当作 Taproot 的签名预映像;30:哈希链接与共识。





