协议演进 / BIP/比特币改进提案 / BIP-342

BIP-342 Taproot 脚本验证

定义叶子版本 0xc0 的 Tapscript 执行语义:签名操作码改验 Schnorr、禁用 CHECKMULTISIG 并代之以 CHECKSIGADD、改用按见证大小计算的 sigops 预算。本 BIP 目前处于已激活状态。

一、档案卡

档案卡
编号BIP-342
标题Validation of Taproot Scripts
作者Pieter Wuille <[email protected]>; Jonas Nick <[email protected]>; Anthony Towns <[email protected]>
指定日期2020-01-19
讨论日期2019-05-06
层级Consensus (soft fork)(共识(软分叉))
类型Specification(规范类)
状态Deployed(已激活)
依赖BIP-340、BIP-341
阅读版https://bips.dev/342
官方原文https://github.com/bitcoin/bips/blob/master/bip-0342.mediawiki

二、放在哪一族

Taproot 三件套的收尾一篇:BIP-340 定 Schnorr 签名,BIP-341 定输出结构与两条花费路径,BIP-342 则回答“脚本路径被走时,叶子里的脚本按什么语义执行”——这套语义称为 Tapscript。

它的基线是 P2WSH 的脚本规则(BIP-141),连同 BIP-65 的 OP_CHECKLOCKTIMEVERIFY 与 BIP-112 的 OP_CHECKSEQUENCEVERIFY;BIP-342 在此之上作增删改。部署方式与 BIP-341 完全相同,两篇是同一次软分叉的两半:341 管“结构”,342 管“脚本内部的规则”。

三、它要解决什么

BIP-341 只改进了脚本的结构(默克尔树、密钥/脚本双路径)。但它要的一部分好处,与脚本语言内部某些操作码的旧语义直接冲突:若不与 341 同时处理,这些好处就无法保证落到脚本路径的花费上。

原文 Motivation 点名的三个目标,都必须在脚本内部兑现:

所以 BIP-342 不是可选的后续优化,而是与 341 捆绑的同一批共识改动:目标是在 Tapscript 诞生的第一天,就把验签体系切换干净,而不是先沿用旧语义、日后再改。

四、规则本身

4.1 适用范围

以下规则同时满足才适用:输入是 SegWit 花费、是 BIP-341 定义的 Taproot 花费(版本 1、程序 32 字节、非 P2SH 包装)、是脚本路径花费(去掉可选 annex 后见证栈至少剩两个元素)、且叶子版本为 0xc0(控制块首字节为 0xc0 或 0xc1)——这样的脚本即 Tapscript。

验证顺序被原文写死:先查 BIP-141 / BIP-341 层面的有效性;再逐一解码 Tapscript 中的操作码;检查初始栈是否触犯资源上限;执行脚本;最终栈上必须恰好留一个为真的元素,否则失败。

4.2 OP_SUCCESSx:为升级预留的操作码

解码时若遇到编号属于 80、98、126–129、131–134、137–138、141–142、149–153、187–254 的操作码,它们被改名为 OP_SUCCESSx,整个脚本无条件验证成功,后续规则一概不再适用——哪怕脚本后段根本无法解码也一样。设计意图是升级通道:将来某场软分叉可以把某个 OP_SUCCESSx 定义为真正的操作码(只能把原本有效的脚本变无效,符合软分叉方向),甚至借它引入多字节操作码或全新脚本语言。反面警示同样写在原文注脚里:在其含义被软分叉定义之前,把 OP_SUCCESSx 写进脚本是不安全的,会造成资金损失——因为现在它等于无条件放行。

4.3 被禁与被改的操作码

4.4 新操作码 OP_CHECKSIGADD 与多签的写法

为补偿 CHECKMULTISIG 的移除,新增 OP_CHECKSIGADD(操作码 186,0xba):从栈上取公钥、计数 n 与一份签名,签名有效则把 n+1 压回,无效则原样把 n 压回。旧式 m <pubkey_1> … <pubkey_n> n OP_CHECKMULTISIG 可等价改写为 <pubkey_1> OP_CHECKSIG <pubkey_2> OP_CHECKSIGADD … <pubkey_n> OP_CHECKSIGADD m OP_NUMEQUAL,与批量验证兼容。原文还列了另外两种门限写法:把每个 k-of-k 组合拆成独立叶子(隐私更好),或每片叶子放一个 MuSig 聚合公钥(更省,但需要交互式签名协议)。

签名操作码的细则:公钥长度为 0 则脚本立即失败;为 32 字节则按 BIP-340 公钥真实验签,验不过立即失败;为其他非零长度则属于“未知公钥类型”——不真正验签、一律按验签成功处理,这是给未来软分叉预留的新签名规则入口。签名为空向量时,OP_CHECKSIGVERIFY 失败、OP_CHECKSIG 压入空向量、OP_CHECKSIGADD 把 n 原样压回;签名非空且需要计数的,才消耗下文的 sigops 预算。

4.5 签名消息的 Tapscript 扩展

Tapscript 的签名消息复用 BIP-341 的共同签名消息(SigMsg),以 ext_flag = 1 追加三项扩展:所执行叶子的 tapleaf_hash(32 字节,把签名绑定到具体叶子与脚本,不再受 OP_CODESEPARATOR 切割脚本的影响)、key_version(1 字节,现为 0x00)、codesep_pos(4 字节,最近一次执行的 OP_CODESEPARATOR 的操作码位置,无则为 0xffffffff)。签名本身的接受形态与密钥路径相同:64 字节视为 SIGHASH_DEFAULT,65 字节时末字节不得为 0x00,其余失败。

4.6 资源上限换算法

五、谁必须动

普通持币人:无直接动作。只有在用脚本路径花费 Taproot 输出时,才会实际跑到这套规则。

钱包与脚本开发者:写 Tapscript 时严守新语义——多签用 OP_CHECKSIGADD 模式或聚合公钥,不得再用 OP_CHECKMULTISIG;条件分支的入参满足共识级 MINIMALIF;绝不把未定义的 OP_SUCCESSx 写进真实脚本。

全节点与矿工:实现 Tapscript 的完整执行与资源计量(尤其是 sigops 预算制),与 BIP-341 同批升级。

共识层:软分叉的一部分,与 341 一起部署;它只改变 v1 脚本路径花费的验证,不影响任何旧输出类型。

六、现在算不算数,以及一个常见误读

状态为 Deployed,且部署方式与 BIP-341 完全相同——原文 Deployment 一节只有一句:“与 Taproot(BIP-341)以相同方式部署。”因此凡说 341 已随 Taproot 在主网激活(区块高度 709632)之处,342 同样适用:现行 Tapscript 的权威语义就是本文件。

常见误读:

“Tapscript 是旧脚本的超集,老多签脚本搬过来就能用。”

不对。Tapscript 以 P2WSH 规则为基线作了减法与替换:OP_CHECKMULTISIG 与 OP_CHECKMULTISIGVERIFY 被禁用,搬来的老多签脚本一旦执行到它们就直接失败;MINIMALIF 也从策略规则升格为共识规则。它更像一套“借旧脚本外壳、换验签内核”的新方言:结构眼熟,验签、多签与计量规则都已换新。

七、延伸

本页内容整理自 bitcoin/bips 仓库与 bips.dev,事实已与仓库核对;解读仅供学习参考。