协议演进 / BIP/比特币改进提案 / BIP-346
BIP-346
提出 OP_TXHASH,让脚本按字段选择器算出花费交易选定部分的 32 字节哈希并压栈,从而在脚本内约束交易本身。本 BIP 目前处于草案状态。
一、档案卡
| 编号 | BIP-346 |
|---|---|
| 标题 | OP_TXHASH |
| 作者 | Steven Roose <[email protected]> Brandon Black <[email protected]> |
| 指定日期 | 2024-04-24 |
| 层级 | Consensus (soft fork)(共识(软分叉)) |
| 类型 | Specification(规范类) |
| 状态 | Draft(草案) |
| 阅读版 | https://bips.dev/346 |
| 官方原文 | https://github.com/bitcoin/bips/blob/master/bip-0346.md |
二、放在哪一族
交易自省(transaction introspection)与契约族。比特币脚本今天几乎“看不见”正在花费自己的这笔交易:签名校验能间接约束交易,但脚本不能直接读版本号、输入输出等字段。BIP-346 给 Tapscript 加一个最基础的自省原语:按需算出交易选定字段的哈希。
它与几个邻居的关系很紧:
BIP-119:OP_CHECKTEMPLATEVERIFY。本 BIP 是它的严格推广——字段选择器取空值时的默认模板,语义上等同于 CTV 承诺的那组字段。
BIP-348:OP_CHECKSIGFROMSTACK。TXHASH 算出的哈希交给 CSFS 验签,就得到完全通用的签名哈希构造。
BIP-118:SIGHASH_ANYPREVOUT。其签名模式及 ANYPREVOUTANYSCRIPT 等变体,可以用 1 字节选择器 + CSFS 组合模拟出来,不必再单独改签名算法。
BIP-342:Tapscript 规则与 sigops 预算,本 BIP 的升级路径与资源计费都建在它之上。
原文致谢里也点明了血统:想法源自 Russell O’Connor 把 CTV 推广为 TXHASH 的思路,及 Jeremy Rubin 的 BIP-119 工作。
三、它要解决什么
脚本若想对花费交易施加条件——“这笔钱只能这样花”——现有手段只有两类:一是预签名交易(交互式、要保存数据、金额目标定死),二是签名哈希标志间接捎带的条件,粒度粗且不可组合。
OP_TXHASH 的定位是给一个通用的底层原语:脚本指定“把交易的哪些部分拿出来算哈希”,把哈希压回栈上,脚本随后可以:
- 与预设哈希比较,强制交易长成某个模板(等价于契约);
- 把哈希当作待签消息交给 BIP-348 的
OP_CHECKSIGFROMSTACK验签,等于让签名者对任意选定的交易字段组合签字,由此拼出包括现有全部 SIGHASH 标志、BIP-118 新标志在内的各种签名哈希模式; - 在多方协议里只约束与自己有关的输入输出,其余部分放行——原文举的例子是:多方共同出资的交易里,各方只盯自己那份,手续费追加也不必推翻整个协议。
原文还指出,TxFieldSelector 这套字段选择编码将来可复用于一个假想的 OP_TX(直接把字段压栈而不哈希),算出的 TransactionHash 也可能被未来的 SegWit 版本用作原生签名哈希。但这些是铺垫,不是本 BIP 的内容。
四、规则本身
4.1 操作码与基本语义
以软分叉方式重定义 Tapscript 的 OP_SUCCESS189(0xbd)为 OP_TXHASH,仅在 Tapscript 语境生效。执行时:
- 栈上至少要有一个元素,否则失败;
- 栈顶元素被解释为 TxFieldSelector(字段选择器)并弹出;
- 选择器必须合法,非法即失败;
- 按选择器计算当前输入下标处这笔交易的 TxHash(32 字节)并压栈。
它只负责“算出并压栈”,本身不做任何校验——是否与预期相等、是否拿去验签,都由后续脚本决定。
4.2 TxFieldSelector:选哪些字段进哈希
选择器是一个位图式的编码,能选的字段包括:版本号、锁定时间、当前输入下标、当前输入的控制块与被花脚本、上一个 OP_CODESEPARATOR 位置、当前输入的 Taproot annex、选择器本身(control 位);以及输入侧的前序输出(prevouts)、序列号、scriptSig、前序输出的 scriptPubKey 与金额、各输入的 annex;输出侧的 scriptPubKey 与金额。对输入输出还可指定选择范围:全部、仅当前下标对应的那个、前若干个,或逐个列出最多 32 个下标(绝对下标或相对当前输入的下标)。
三种写法:
- 空值:等同于默认模板
TXFS_SPECIAL_TEMPLATE——包含除 prevouts 与前序输出的 scriptPubKey、金额之外的全部内容,语义对应 BIP-119 的模板; - 1 字节简写:8 个位分别控制输入/输出选全部还是仅当前、前序信息、控制块等,并恒含版本号、锁定时间等全局字段。这套简写就是为了对应现行与 BIP-118 的各档签名哈希类型,原文给了一张 12 行的对照表(ALL / SINGLE / NONE 与 ANYONECANPAY、ANYPREVOUT、ANYPREVOUTANYSCRIPT 的组合);
- 多字节完整写法:第一字节选全局与当前输入字段,第二字节选输入输出的字段种类,其后每个方向再跟选择字节,逐项指定范围。
原文提醒:同一组被选数据可能有多种选择器写法,因此建议总是把 control 位置上、让哈希同时承诺所用的选择器本身,以免歧义。
4.3 资源计费
每次执行 OP_TXHASH,按 BIP-342 的 sigops 预算扣 25,预算扣成负数脚本立即失败。体积任意大的字段在哈希时都先取其哈希值、且结果可缓存,原文给出了领先模式下的 SHA256 上下文缓存策略,避免反复计算成为拒绝服务入口。
五、谁必须动
普通持币人:无直接变化。草案未激活,你的钱包与地址都不受影响。
钱包与协议开发者:多一个可编程原语。用它做契约或多方协议时,关键是把选择器设计对:选少了约束不住,选多了把不该锁死的字段(如手续费安排的余地)也锁死。原文建议恒承诺选择器本身。
全节点与矿工:若将来部署,这是一次共识软分叉:节点要实现选择器解析与 TxHash 计算、计入 sigops 预算。不升级的旧节点按 OP_SUCCESS 旧语义直接放行含该操作码的脚本,这正是占位操作码升级路径的设计。
共识层:除 Tapscript 语境外不生效;旧脚本语境中的 OP_SUCCESS189 不受影响。
六、现在算不算数,以及一个常见误读
状态为 Draft(草案),原文的部署一节只说可用 BIP-9 版本位部署、具体策略与位分配未定。也就是说,它没有激活时间表,主网节点今天不会按新语义验证 OP_TXHASH;仓库里附了 Rust 参考实现与测试向量,Bitcoin Core 与 rust-bitcoin 也有早期实现尝试,但原文注明那些实现基于较早版本的规范、正在更新。
常见误读:
“OP_TXHASH 就是 OP_CHECKTEMPLATEVERIFY 换了个名字。”
不对。CTV 是“算模板哈希并当场比对、不等就失败”的校验型操作码;TXHASH 只把哈希压栈,什么都不校验。取空选择器时它算出的哈希与 CTV 的模板语义相当,但后续既可以做相等比较,也可以交给 OP_CHECKSIGFROMSTACK 验签、或参与别的计算。用原文的话说,它严格推广了 BIP-119,而不是改名。
七、延伸
- 阅读版:https://bips.dev/346
- 官方原文:https://github.com/bitcoin/bips/blob/master/bip-0346.md
- 相关:BIP-119 OP_CHECKTEMPLATEVERIFY、BIP-348 OP_CHECKSIGFROMSTACK、BIP-118 SIGHASH_ANYPREVOUT、BIP-342 Tapscript
- 仓库总目录:https://github.com/bitcoin/bips
