协议演进 / BIP/比特币改进提案 / BIP-114
BIP-114
定义一种用默克尔树编码互斥脚本分支的新 witness 程序(版本 1、32 字节的 MAST 根),花费时只揭示实际执行的那一支,未执行分支不公开,赎回体积从与分支数成正比降为对数级。本 BIP 目前处于已关闭状态。
一、档案卡
| 编号 | BIP-114 |
|---|---|
| 标题 | Merkelized Abstract Syntax Tree |
| 作者 | Johnson Lau <[email protected]> |
| 指定日期 | 2016-04-02 |
| 层级 | Consensus (soft fork)(共识(软分叉)) |
| 类型 | Specification(规范类) |
| 状态 | Closed(已关闭) |
| 阅读版 | https://bips.dev/114 |
| 官方原文 | https://github.com/bitcoin/bips/blob/master/bip-0114.mediawiki |
二、放在哪一族
脚本表达能力演进族,站在两步改革的延长线上:
BIP-16:P2SH——把赎回条件从 scriptPubKey 换成一个 20 字节哈希,花费时才由赎回者给出脚本,解决了“条件难指定、大脚本占 UTXO、发送方付费”三个问题
BIP-141:隔离见证(共识层)——P2WSH 把脚本移入 witness,恢复 10,000 字节脚本上限;本 BIP 明确依赖(depends on)BIP-141 部署
BIP-114:再进一步——P2SH 与 P2WSH 在花费时都必须公开脚本的全部分支,包括没有执行的那些;MAST 则只公开执行的一支
它的例题直接取自时间锁族:把 BIP-112 的带超时托管、HTLC 脚本“压平”为若干互斥分支,展示分支化之后的可读性与体积收益。思想来源上,原文致谢称 MAST 的想法来自 Russell O’Connor、Pieter Wuille 与 Peter Todd。
后来 Taproot 的脚本树(taptree,见 BIP-341)以不同机制实现了“只揭示所走的那一支”这一核心直觉;但那是另一套设计,不能与本提案混为一谈——本提案本身从未激活。
三、它要解决什么
3.1 原始脚本模型的五个问题
原文回顾了比特币脚本系统的演进,并列出原始设计对复杂脚本的不足:
- 接收方难以指定赎回条件;
- 大脚本占用更多 UTXO 空间;
- 追加的区块空间由发送方付费;
- 为防 DoS,脚本被限制在 10,000 字节、201 个操作码以内;
- 脚本中未执行的分支、以及非共识强制的数据,对全网公开,既占区块空间,又损害隐私。
BIP-16 修好了前三条,但受脚本中数据推送 520 字节上限约束,P2SH 脚本不能超过 520 字节,且花费时仍须公开全部未执行分支。BIP-141 的 P2WSH 把上限恢复回 10,000 字节,但同样要公开未执行分支。
3.2 MAST 的解法
用默克尔树编码脚本的各个分支。花费时,用户只需提供所执行的分支,以及把这些分支连接到固定大小的默克尔根所需的哈希。赎回栈的体积由此从 O(n) 降到 O(log n)(n 为分支数)。随之而来的好处,原文列了三项:
- 实现目前受脚本体积与操作码上限所限、根本不可能的复杂赎回条件;
- 隐藏未执行脚本,改善隐私;
- 以极低甚至零额外成本,纳入非共识强制的数据承诺。
四、规则本身
4.1 新的 witness 程序类型
在 BIP-141 下,版本字节为 1 或更大的 witness 程序被视为任何人可花费(anyone-can-spend)。本 BIP 新增验证规则:当 witness 程序的版本字节为 1、且程序大小为 32 字节时,该程序即 MAST Root;若版本为 1 但程序不是 32 字节,则不对程序或 witness 栈作进一步解释,留作未来扩展。
4.2 花费时 witness 的构成
赎回此类输出时,witness 自底向上依次为:
- 若干输入栈元素(Script_stack,个数 X ≥ 0);
- 1 至 255 个子脚本(Subscript,个数记 Y);
- Position:该脚本哈希在默克尔树中的位置,0 表示最左;为小端序无符号整数,不超过 4 字节,须最小化编码,其值不得超过该树深度所允许的最大项数(例如深度 4 时有效范围是 0 至 15);
- Path:默克尔路径的序列化,大小须为 32 字节的倍数且不超过 1024 字节,每个 32 字节字是一个双重 SHA256 的默克尔节点;树的深度(Depth,0 至 32)即 Path 大小除以 32;
- Metadata:最后一项,1 至 5 字节的向量。首字节是 1 至 255 的无符号整数,表示 Subscript 的个数 Y;其后 0 至 4 字节是小端序无符号整数,表示 MAST 版本(MAST Version),须最小化编码。
4.3 哈希的计算
- Script Hash = H(Y | H(Subscript_1) | H(Subscript_2) | … | H(Subscript_Y)),其中 H() 为双重 SHA256;
- Script Root:以 Script Hash 为叶,用 Path 与 Position 通过 ComputeMerkleRootFromBranch 计算出的默克尔根;
- MAST Root = H(MAST Version | Script Root),其原像固定为 36 字节:4 字节小端序的 MAST Version 加 32 字节的 Script Root。
计算出的 MAST Root 与 witness 程序不符,脚本即失败。
4.4 版本 0 的执行规则
- 若 MAST Root 匹配、且 MAST Version 大于 0:脚本直接返回成功、不再进一步求值,SigOpsCost 计为 0——这为未来的脚本升级预留。
- 若 MAST Version 为 0:各 Subscript 依次序列化拼成最终的 MAST Script;位于 Subscript_1 之前的未用 witness 元素作为 Input Stack 喂给它求值(与 BIP-141 的 P2WSH 类似)。
- 以下任一情形,脚本失败:MAST Script 畸形(最后一次压入数据不足);MAST Script 大小超过 10,000 字节;任一 Input Stack 元素大于 520 字节;非推送操作数 nOpCount 超过 201——nOpCount 为 MAST Script 中非推送操作数、加上 Subscript 个数 Y、再加上默克尔树深度 Depth 之和(把算 MAST Root 的额外哈希也计入上限,是为防滥用)。
- 求值必须不失败,且结果须为恰好空栈——不同于 P2WSH 要求的单个 TRUE 值。这样设计,是让合约各方可以各自提供 Subscript 并自行清理其栈。
4.5 受限的 MAST:先压平,再上树
原文说明,本提案是一般化 MAST 的一个受限情形:只允许执行一条分支,用户须把复杂条件先变换为若干互斥分支。例如 (A or B) and (C or D or E) and (F or G),在一般化设计里是 7 个分支加一份“总条件”;在本提案里须展开为 12 个互斥分支,构成 4 层默克尔树,花费时只出示其中一条。
这样取舍的收益是:Subscript 在 witness 栈中的位置固定,可以做静态分析(如静态计算 SigOpsCost、对含禁用操作码的脚本提前终止);合约各方若不想互相暴露脚本,只需彼此提供 H(Subscript),把原文留到赎回时。代价只在条件非常复杂时出现:分支数可能更多、赎回 witness 可能更占空间,建树与存储成本由合约相关方自己承担。
原文举了量级感很强的例子:现行 CHECKMULTISIG 最多 20 个公钥,用 MAST 可把一个 3-of-2000 多签展开为 1,331,334,000 个 3-of-3 的 CHECKMULTISIGVERIFY 分支、构成 31 层 MAST,而 scriptPubKey 仍保持固定 34 字节,赎回 witness 不到 1,500 字节。又如把“消息签名密钥”作为一个分支作数据承诺,最多只多花 32 字节或零成本。
4.6 尚未闭合之处
原文有两处细节留作占位:MAST Script 求值时配套的新增或重定义操作码,写为 “BIPXXX”;SigOpsCost 的计数方式,写为 “BIPYYY”。部署一节也只写:本 BIP 依赖 BIP-141,将在 BIP-141 强制执行后以 BIP-9(version bits)部署,具体细节待定(Exact details TBD)。参考实现标注为进行中(WIP)。
五、谁必须动
普通持币人:无直接操作。不使用 MAST 输出的资金不受影响。
钱包开发者:构造 MAST 输出的一方要负责把条件压平为互斥分支、建树、保管各分支脚本与路径;花费时只组装所走分支的 witness。原文在向后兼容一节特别提醒:钱包应始终警惕任何人可花费的脚本——在未升级节点眼里,MAST 程序正是这种形态。
全节点与矿工:升级节点按新规则验证版本 1、32 字节的 witness 程序;未升级节点把这类程序当作任何人可花费脚本,仍能正常运行——这是软分叉的形态。
共识层:新增一类 witness 程序的解释规则。它不改既有脚本的语义,但一旦上线,未升级节点对 MAST 输出的“有效性”判断会与升级节点不同,这正是它须走 BIP-9 部署的原因。
六、现在算不算数,以及一个常见误读
状态为 Closed(已关闭)。它从未在比特币主网激活,部署细节在原文中始终是“待定”,配套操作码与 SigOpsCost 的引用也停留在占位符。今天在链上实际运行的“只揭示一支”的机制是 Taproot 的脚本树,其结构、哈希与版本安排都与本提案不同。读 BIP-114,是为了理解 MAST 这一思想最初被完整写成规范时的样子:它想解决的问题、它受限的设计、以及它未走完的最后一步。
常见误读:
“Taproot 就是 BIP-114 的实现,所以 BIP-114 其实已经上线了。”
不对。两者共享“用默克尔树组织脚本分支、花费时只揭示一支”的核心直觉,但 Taproot 是一套独立的新设计(见 BIP-341、BIP-342),其 witness 程序版本、树的构造与脚本版本安排都不是按 BIP-114 的规范来的。BIP-114 本身的状态是 Closed,把它等同于 Taproot,会同时抹掉两份文档各自的具体规则。
七、延伸
- 阅读版:https://bips.dev/114
- 官方原文:https://github.com/bitcoin/bips/blob/master/bip-0114.mediawiki
- 相关:BIP-16 P2SH、BIP-112 CHECKSEQUENCEVERIFY、BIP-141 隔离见证(共识层)、BIP-341 Taproot
- 仓库总目录:https://github.com/bitcoin/bips
