协议演进 / BIP/比特币改进提案 / BIP-16
BIP-16
把任意复杂的花费条件压缩成一个 20 字节脚本哈希:付款人只付给哈希,真正花费的人到时再出示完整脚本并证明满足条件。本 BIP 目前处于已激活状态。
一、档案卡
| 编号 | BIP-16 |
|---|---|
| 标题 | Pay to Script Hash |
| 作者 | Gavin Andresen <[email protected]> |
| 指定日期 | 2012-01-03 |
| 层级 | Consensus (soft fork)(共识(软分叉)) |
| 类型 | Specification(规范类) |
| 状态 | Deployed(已激活) |
| 阅读版 | https://bips.dev/16 |
| 官方原文 | https://github.com/bitcoin/bips/blob/master/bip-0016.mediawiki |
二、放在哪一族
P2SH(Pay to Script Hash,付给脚本哈希)族的核心文档。它要解决的问题承接自多签一脉:
BIP-11:M-of-N 标准交易,让多签交易能被默认转发与打包;
BIP-13:P2SH 的地址格式,让脚本哈希能写成普通人可用的地址。
BIP-16 本身取代了 BIP-12(OP_EVAL)——后者想用一个新操作码实现同样的事、且能做的更多,最终被认为过于复杂、扩张脚本语言能力的风险太大。
同一目标当时还有两个竞争或平行的写法:BIP-17(OP_CHECKHASHVERIFY,CHV)主张换一个操作码实现 P2SH;BIP-18(hashScriptCheck)则在协议相同的前提下重写了表述方式。最后被采用的是 BIP-16 这一版定义,BIP-17 状态为 Closed。读这段历史,关键是分清“目标相同”与“机制不同”:三份文档都想让收款方定义花费条件,分歧在具体怎么改验证规则。
三、它要解决什么
原文给出的目的只有一句话,却决定了之后十几年钱包的形态:把“提供赎回条件”的责任,从付款人转移到赎回人(收款方自己)。
在 P2SH 之前,付款人要往一个复杂脚本(比如 2-of-3 多签)付款,就必须拿到完整的花费条件、构造对应的输出脚本。条件越复杂,付款一方的负担越重,地址也越长、越难处理。
P2SH 之后,付款人只需要一个固定长度的 20 字节哈希:短到可以扫 QR 码、可以轻松复制粘贴。无论背后的条件多复杂——多签、时间锁、组合条件——对付款人来说都只是同一个长度的哈希。复杂的脚本由收款方保管,等他真正花费时再出示。
作者也承认这一动机当时有争议:有人认为没有必要,直接把完整脚本给付款人即可。作者的回应是:哈希化能最大限度复用已经建好的“向 Base58 编码的 20 字节地址付款”的整套基础设施(商户、交易所、钱包软件),让多签等功能更快落地。
四、规则本身
4.1 新的标准交易类型
定义一种会被转发、会被矿工打包的新标准输出形式:
OP_HASH160 [20-byte-hash-value] OP_EQUAL
其中 [20-byte-hash-value] 必须是压入 20 字节的操作码(0x14)后跟恰好 20 字节。这个 20 字节值,就是赎回脚本(redeemScript)序列化后的哈希。
4.2 怎么花费
花费这种输出的 scriptSig 形如:
...signatures... {serialized script}
即:前面是满足条件所需的数据(如签名),最后一个元素是被序列化的完整脚本本身。并且,花费交易只有在 redeemScript 本身属于其他标准交易类型之一时,才算标准交易。
4.3 新增验证规则(三步)
节点在转发或考虑打包时,对这类输出的花费要多做验证:
- scriptSig 里只允许“压数据”操作,出现任何其他操作即验证失败;
- 先按常规验证:用签名与序列化脚本构成初始栈,计算脚本的哈希,与输出中记录的哈希不符则立即失败;
- 把序列化脚本从栈上弹出、反序列化为 scriptPubKey,用弹出的栈再验证一遍。
也就是说,哈希先对上,脚本再真正执行一遍——两关都过才算有效花费。
4.4 生效时间与签名操作计数
新规则只适用于区块时间戳 ≥ 1333238400(2012 年 4 月 1 日)的区块中的交易;早于此的交易链上已有按新规则会失败的例子,必须继续按旧规则验证。
redeemScript 里的签名操作要计入每区块 20,000 次的上限,静态扫描计数:
- OP_CHECKSIG、OP_CHECKSIGVERIFY 各计 1 次,无论是否实际求值;
- 被 OP_1 至 OP_16 紧邻在前的 OP_CHECKMULTISIG / OP_CHECKMULTISIGVERIFY 计 1 至 16 次;
- 其他情形的 OP_CHECKMULTISIG 类计 20 次。
设置上限是为了防拒绝服务:没有上限,恶意矿工可以广播一个需要几十万次签名验证的区块,自己抢先算下一块,让全网卡在验证上。
4.5 520 字节的连带限制
为保持向后兼容,序列化脚本本身受压栈数据不得超过 520 字节的旧规则约束。所以 redeemScript 超过 520 字节的 P2SH 输出无法花费。原文举例:OP_CHECKMULTISIG 本可接受 20 个公钥,但用 33 字节压缩公钥时,P2SH 最多只能装下 15 个公钥(3 + 15×34 = 513 字节)。
五、谁必须动
普通持币人 / 付款人:只需向一个短哈希(经 BIP-13 编码的地址)付款,不必知道、也不必构造背后的完整脚本。
收款方 / 钱包开发者:负责生成并保管 redeemScript,花费时完整出示;脚本必须属于标准类型,否则交易不被转发。
全节点与矿工(共识层):升级后对时间戳达标的区块执行 4.3 的新增验证;并通过在 coinbase 中写入 “/P2SH/” 字符串表态支持(见第六节的部署办法)。
旧实现:把这类交易视为非标准,通常不转发、不打包;验证他人区块时只核对脚本哈希是否对得上,不做其余验证。
原文点名的风险:对旧实现存在 1 确认攻击——攻击者构造一笔旧软件看有效、新软件看无效的 P2SH 交易并自挖出块,骗仍在用旧软件、只等 1 次确认就放货的受害者。原文评定其成本高、实操难,并提醒高额交易不应只等 1 次确认。
六、现在算不算数,以及一个常见误读
状态为 Deployed,站点标注“已激活”:P2SH 已按软分叉方式进入共识规则,是今天仍在使用的输出类型。
它的部署办法本身就是早期软分叉动员的样本:2012 年 2 月 1 日检查此前 7 天的区块,若 coinbase 中含 “/P2SH/” 的区块达到 550 个(约占一周约 1,000 个区块的 55%),则 2012 年 2 月 15 日之后的区块全面执行新验证;若算力多数不支持,则推迟、乃至否决。要求过半矿工同时切换,是为了避免“新规则判无效、旧规则判有效”的交易造成持久分叉。
与 BIP-17 的关系也要说清:两者是同一场 P2SH 之争的双方。BIP-17 用重定义 OP_NOP2 的 OP_CHECKHASHVERIFY 实现同一目标,最终状态 Closed;实际生效的 P2SH 以 BIP-16 的 OP_HASH160 … OP_EQUAL 形式为准。
常见误读:
“付款给 P2SH 地址,就等于把完整脚本写上了链。”
不对。链上输出里只有 20 字节哈希,完整脚本在花费之前不公开——由收款方自己保管,花费时才随 scriptSig 出示。这既是负担转移,也是隐私上的附带效果:付款时外界看不出背后是单签、多签还是更复杂的条件。
七、延伸
- 阅读版:https://bips.dev/16
- 官方原文:https://github.com/bitcoin/bips/blob/master/bip-0016.mediawiki
- 相关:BIP-11 M-of-N 标准交易、BIP-13 P2SH 地址格式、BIP-12 OP_EVAL(被取代)、BIP-17 CHV(竞争方案)、BIP-18 hashScriptCheck(同协议的另一表述)
- 仓库总目录:https://github.com/bitcoin/bips
