协议演进 / BIP/比特币改进提案 / BIP-18
BIP-18
与 BIP-16 协议相同,换的是一套说法:用 dataSig、scriptCheck、hashScriptCheck 三件套重新表述 P2SH,并主张把旧的 scriptSig 与 scriptPubKey 宣布为过时名目。本 BIP 目前处于已完成状态。
一、档案卡
| 编号 | BIP-18 |
|---|---|
| 标题 | hashScriptCheck |
| 作者 | Luke Dashjr <[email protected]> |
| 指定日期 | 2012-01-27 |
| 层级 | Consensus (soft fork)(共识(软分叉)) |
| 类型 | Specification(规范类) |
| 状态 | Complete(已完成) |
| 阅读版 | https://bips.dev/18 |
| 官方原文 | https://github.com/bitcoin/bips/blob/master/bip-0018.mediawiki |
二、放在哪一族
P2SH 族里最特殊的一份:它不提出新协议,而是给 BIP-16 的同一套协议换一副术语骨架。
原文 Rationale 写得很直白:本提案取代 BIP-12 与 BIP-17(两者都是给脚本系统做扩展、让 scriptPubKey 把验证外包出去);同时也取代 BIP-16——因为 BIP-16“在协议上与本提案完全相同”,区别只是 BIP-16 给出了一种具体实现写法,且为了保持协议表述的一致性而没有宣布 scriptPubKey 过时;本提案则认为应当把旧名目正式废弃、新交易类型一律用新结构表达。
配套仍是同一组:应用场景来自 BIP-11 的 M-of-N 多签,地址格式用 BIP-13。读本页要抓住一句话:链上发生的事实与 BIP-16 是同一件,变的只是文档如何命名与组织这件事。
三、它要解决什么
动机部分与 BIP-16、BIP-17 相同:P2SH 的目的,是把提供赎回条件的责任从付款人转移到赎回人,让付款人用一个固定长度的 20 字节哈希(可扫 QR 码、可复制粘贴)为任意复杂的条件付款。
在此之上,本提案多解决一个“表述问题”:旧的交易结构里,输入端叫 scriptSig、输出端叫 scriptPubKey,两者都是“被执行的脚本”。而 P2SH 的实际结构已经不是这个形状了——输入端本质上是一包数据加上最后一段待检脚本,输出端本质上只是一个哈希承诺。继续套旧名字,会让规范读起来与实际验证流程对不上。因此本提案主张:旧名目宣布过时(客户端为兼容仍须支持,但新交易类型不再使用),改用三个新元素把结构说清楚。
四、规则本身
4.1 三个新元素
- dataSig:放在交易输入中原 scriptSig 的位置,内容是多个序列化的数据元素,按“只压数据”的脚本方式编码;
- scriptCheck:dataSig 的最后一个元素。它被执行时,dataSig 中在它之前的元素已预先压入栈(紧挨着它前面的元素位于栈顶);
- hashScriptCheck:放在交易输出中原 scriptPubKey 的位置,给出允许赎回该输出的 scriptCheck 的哈希。
相应地:兼容客户端必须继续支持 scriptSig 与 scriptPubKey(原文用大写 MUST),但新交易类型不应再使用它们;付款的服务与个人暂时仍应支持旧的基于公钥的地址,收款方也可以继续生成使用旧地址。
4.2 hashScriptCheck 的精确编码
必须恰好编码为:
0xa9 0x14 (20-byte-hash-value) 0x87
旧(BIP-18 之前)的客户端可以把这串字节解释成一条熟悉的旧脚本:
OP_HASH160 [20-byte-hash-value] OP_EQUAL
——这正是 BIP-16 定义的 P2SH 输出形式,两份文档在字节层面严丝合缝的原因就在这里。若模板不精确匹配,或交易所在区块的时间戳早于 hashScriptCheck 激活日,则必须走向后兼容模式,仍按 scriptSig + scriptPubKey 验证。
4.3 有效条件
一笔符合 hashScriptCheck 的输入有效,当且仅当:
- dataSig 中不得有任何“压数据”以外的操作(它是数据、不是脚本;不得把 scriptSig 与 hashScriptCheck 混用);
- scriptCheck 用比特币的 Hash160 算法算出的哈希,必须等于输出中的 hashScriptCheck;
- scriptCheck 必须以上述 dataSig 栈(不含 scriptCheck 本身)执行验证(这并不意味着客户端被要求验证所有交易);
- scriptCheck 执行不得中止,且结束时栈顶须留下真值——与 scriptSig + scriptPubKey 的现行行为一致。
矿工还应拿 scriptCheck 去对照“标准交易”模板。单签例:scriptCheck 为 [pubkey] OP_CHECKSIG,dataSig 为 [signature] {[pubkey] OP_CHECKSIG},hashScriptCheck 为该脚本的 20 字节哈希。
4.4 签名操作计数与前向兼容
scriptCheck 的签名操作不沿用旧规则,改为与 BIP-16 相同的静态计数:OP_CHECKSIG 类计 1;被 OP_1 至 OP_16 紧邻在前的 OP_CHECKMULTISIG 类计 1 至 16;其他 OP_CHECKMULTISIG 类计 20;总上限仍为每区块 20,000 次,理由同为防恶意区块用海量签名验证拖住全网。
前向兼容上,hashScriptCheck 的前两字节指明所用哈希算法与长度;本提案只允许 Hash160,但给将来其他算法留了位置。
五、谁必须动
普通持币人 / 付款人:无区别于 BIP-16 的变化,只向短哈希付款。
钱包与服务开发者:用新结构组织 P2SH 交易(dataSig / scriptCheck / hashScriptCheck),同时必须继续支持旧的 scriptSig / scriptPubKey 交易与旧公钥地址,不能因“宣布过时”就拒绝旧格式。
全节点与矿工(共识层):按新模式验证符合模板的输出(哈希须对上、scriptCheck 须执行通过),并把 scriptCheck 对照标准交易模板;原文同样给出了与 BIP-16 相同的 1 确认攻击说明,评定成本高、实操难。
旧实现:通常把这类交易视为非标准,不转发、不打包;验证他人区块时只核对 scriptCheck 的哈希是否对得上,不做其余验证。
六、现在算不算数,以及一个常见误读
状态为 Complete(已完成):这是仓库给它的标注,与 BIP-16 的 Deployed 不同——本页不要读成“另有一套 P2SH 被单独部署过”。原文设计的部署安排本就与 BIP-16 共用同一套动员办法:矿工在 coinbase 写 “/P2SH/” 表态,2012 年 3 月 15 日检查此前 7 天区块,达 550 个则 4 月 1 日后全面验证;多数不支持则推迟乃至否决。P2SH 最终是按这一套安排、以 BIP-16 的定义激活的;本提案完成的,是把同一协议重新表述成一套更贴合实际结构的新术语。
常见误读:
“BIP-18 是和 BIP-16 不兼容的另一套 P2SH 协议。”
不对。原文 Rationale 明确说两者“在协议上完全相同”,分歧只在两点:是否给出某种具体实现写法、以及是否宣布 scriptPubKey 过时。把它当成第二套互斥协议,会误读整段 P2SH 之争——真正的机制之争在 BIP-16 与 BIP-17 之间。
七、延伸
- 阅读版:https://bips.dev/18
- 官方原文:https://github.com/bitcoin/bips/blob/master/bip-0018.mediawiki
- 相关:BIP-16 P2SH(同一协议的通行表述)、BIP-17 CHV、BIP-11 M-of-N 标准交易、BIP-13 P2SH 地址格式
- 仓库总目录:https://github.com/bitcoin/bips
