协议演进 / BIP/比特币改进提案 / BIP-443
BIP-443
为 Tapscript 新增 OP_CHECKCONTRACTVERIFY:让 UTXO 携带对数据的动态承诺,脚本可内省该数据,并约束花费交易输出的内部公钥、taptree 与金额分配,由此搭出带状态的 UTXO 与状态机。本 BIP 目前处于草案状态。
一、档案卡
| 编号 | BIP-443 |
|---|---|
| 标题 | OP_CHECKCONTRACTVERIFY |
| 作者 | Salvatore Ingala <[email protected]> |
| 指定日期 | 2025-05-08 |
| 讨论日期 | 2022-11-08 |
| 层级 | Consensus (soft fork)(共识(软分叉)) |
| 类型 | Specification(规范类) |
| 状态 | Draft(草案) |
| 阅读版 | https://bips.dev/443 |
| 官方原文 | https://github.com/bitcoin/bips/blob/master/bip-0443.mediawiki |
二、放在哪一族
契约(covenant)与“带状态的 UTXO”一族。它不针对某一个应用做专用操作码,而是提供一个原文所称的基础构件(fundamental building block):让一枚 UTXO 带着一份数据承诺往下传,脚本在花费时能验这份数据、并规定下一枚 UTXO 的程序与数据。
它与近亲的区别:
BIP-119:OP_CHECKTEMPLATEVERIFY,把花费交易约束到一个固定输出模板;本 BIP 约束的是输出的程序(内部公钥与 taptree)与所嵌数据,数据还可以动态计算
BIP-345:OP_VAULT,面向两步提款的专用金库构造;原文把它列为“约束币的未来”这一共同诉求下的专门方案之一
BIP-442:OP_PAIRCOMMIT 等向量承诺操作码;原文说,与这类操作码配合,本 BIP 才能把任意状态机组合出来——状态转移是否合法,取决于 taptree 中脚本能表达的条件
BIP-341:Taproot;本 BIP 的数据承诺完全寄生在 Taproot 的密钥 tweak 结构里,不另造输出格式
三、它要解决什么
“约束一枚币的未来能去哪、能变成什么”超出预签名交易能做的范围,是许多改进尝试的共同核心。原文列举:
- Ark、CoinPools、Timeout Trees 等 UTXO 共享方案,靠各种输出限制让多方共享一枚 UTXO 的控制权,同时保证每人管住自己的余额;
- OP_VAULT 式的链上反应式安全(两步提款);
- OP_CHECKTEMPLATEVERIFY 式把交易约束到固定模板;
- 侧链与 rollup:用递归契约约束的 UTXO 承载状态根,每次花费更新一次。
而 BitVM 等构造绕开“UTXO 不能带状态”这一缺失,用 Lamport 签名与乐观执行硬搭智能合约,代价是复杂度、交互轮次,以及某些执行路径上的区块占用都极高,完全免信任的桥接设计仍未实现。
BIP-443 的主张是:不为某个应用做专用优化,而是补上那个共同缺失的原语——UTXO 携带状态(state-carrying UTXOs),让上述构造有统一的底层构件可用。
四、规则本身
4.1 数据怎么嵌进 UTXO
把一个 P2TR UTXO 的“程序”看作两部分:一个 x-only 公钥(原文称 naked key,裸公钥)与一棵脚本 Merkle 树(taptree)。
- UTXO 不带数据时,裸公钥就是 BIP-341 的内部公钥;
- UTXO 携带对数据的承诺时,先用数据的哈希 tweak 裸公钥(数据 tweak 为 sha256(pk ‖ data)),得到的结果再作内部公钥、按 BIP-341 做 taptweak。
由此带来三个性质:被承诺的数据不让 UTXO 变大;知道裸公钥私钥、且知道所嵌数据(或其哈希)的一方,仍可走密钥路径花费;taptree 里只有真正需要访问数据的叶子,才为此付见证字节的成本。
4.2 操作码的栈格式与语义
OP_CHECKCONTRACTVERIFY(简称 OP_CCV)占用 Tapscript 的 OP_SUCCESS187(0xbb)。执行时栈自底向上应为:
<data> <index> <pk> <taptree> <mode>
一句话语义:验证下标为 index 的那个输入或输出,是一个 P2TR UTXO,其 Taproot 输出公钥由 pk 出发——数据非空时先用数据哈希 tweak,再在 taptree 非空时做 taptweak——推导得到。各参数的特殊取值:
- index 为 -1:指当前输入本身;
- pk 为 -1:指当前输入的 Taproot 内部公钥;pk 为 0:指 BIP-341 定义的 NUMS 公钥;
- taptree 为 -1:指当前输入的 taptree Merkle 根;为空:跳过 taptweak;
- data 为空:不做数据 tweak。
其余不合格式的取值使验证立即失败。对输入用它,可内省该输入所嵌的数据与程序;对输出用它,可定义该输出的程序与所嵌数据——这正是“把状态传给下一枚 UTXO”的入口。
4.3 四种模式与金额语义
mode 决定检查对象与金额处理:
- -1(CHECK_INPUT):检查某个输入的脚本,不查金额;
- 0(CHECK_OUTPUT,默认):检查某个输出的脚本,并要求当前输入的(剩余)金额全额保留到该输出;
- 1(CHECK_OUTPUT_IGNORE_AMOUNT):检查输出脚本,忽略金额;
- 2(CHECK_OUTPUT_DEDUCT_AMOUNT):检查输出脚本,并把该输出金额从当前输入的剩余金额中扣除,供后续检查分配余款。
其他 mode 值使操作码立即成功——这是为将来软分叉引入新模式预留的口子。防重复计账的规矩:同一输出在同一交易中不能既被默认模式又被扣除模式检查,也不能被扣除模式检查多次;但多个输入可以用默认模式共同汇入同一个输出。
原文特别提醒两点:扣除模式并不校验该输出的确切金额,这类合约的金额还需用签名或其他内省手段另行约束,否则金额可被篡改;又因为金额语义以“全额保留”为基调,手续费须从外部支付——另加一个输入、用 anchor 输出等办法解决,不能指望忽略金额模式(该模式下输出金额无约束,矿工理论上可吞掉该输入的全部价值,该模式只是为未来兼容预留)。
五、谁必须动
普通持币人:现在不必做任何事。本 BIP 仍是草案,未激活;普通 P2TR 收付款完全不涉及它。
钱包开发者:现在不必动。将来若激活,也只有构造这类契约的钱包/应用才需要按 4.1 的方式嵌数据、按 4.3 分配金额,并为手续费另备输入。
脚本与二层协议开发者:这是主要受众。Ark、共享 UTXO、金库、状态机等构造可用它作底层构件;但要清醒:任意状态机还需要向量承诺类操作码配合,且原文的 Applications 一节尚为 TODO,应用清单不是已定稿的承诺。
全节点与矿工:若将来激活,需实现交易级与输入级的金额记账初始化及 0xbb 的新语义。它把原本无条件成功的 OP_SUCCESS187 收紧为带条件的验证,原文定性为软分叉:未升级软件除挖矿与区块验证外仍可正常工作。
共识层:只改 Tapscript 中一个 OP_SUCCESS 操作码的语义,不动其他脚本环境;激活机制与同批部署的其他 BIP,原文写为待定(to be determined)。
六、现在算不算数,以及一个常见误读
状态为 Draft(草案)。原文中 sigops 预算、Policy changes、Applications、Acknowledgements 等多节仍标 TODO,说明文本本身尚未定稿;参考实现以 Bitcoin Core 拉取请求的形式给出。今天的主网上没有 OP_CCV:0xbb 仍是 OP_SUCCESS,UTXO 也不能按本 BIP 的方式携带可内省的数据承诺。
常见误读:
“BIP-443 就是 OP_CHECKTEMPLATEVERIFY 或 OP_VAULT 的改名。”
不对。三者同属“约束币的未来”这一族,但机制不同:BIP-119 约束的是整笔交易的固定输出模板,BIP-345 是专为两步提款设计的金库操作码,而 BIP-443 约束的是单个输入/输出的程序与所嵌数据,数据可以动态计算、可被脚本内省,并自带金额分配语义——原文明确说它的目标是通用构件,而不是为某个应用优化的专用构造。
七、延伸
- 阅读版:https://bips.dev/443
- 官方原文:https://github.com/bitcoin/bips/blob/master/bip-0443.mediawiki
- 相关:BIP-119 OP_CHECKTEMPLATEVERIFY、BIP-341 Taproot、BIP-345 OP_VAULT、BIP-442 OP_PAIRCOMMIT
- 仓库总目录:https://github.com/bitcoin/bips
