协议演进 / BIP/比特币改进提案 / BIP-62
BIP-62
提议新增七条共识规则,逐一封堵交易可锻性的主要来源,使发送方不主动规避时交易无法再被第三方改形。文档自述未完成,提案最终关闭,未部署。本 BIP 目前处于已关闭状态。
一、档案卡
| 编号 | BIP-62 |
|---|---|
| 标题 | Dealing with malleability |
| 作者 | Pieter Wuille <[email protected]> |
| 指定日期 | 2014-03-12 |
| 层级 | Consensus (soft fork)(共识(软分叉)) |
| 类型 | Specification(规范类) |
| 状态 | Closed(已关闭) |
| 阅读版 | https://bips.dev/62 |
| 官方原文 | https://github.com/bitcoin/bips/blob/master/bip-0062.mediawiki |
二、放在哪一族
交易可锻性(malleability)治理族,也是共识规则收紧型(软分叉)提案的一份早期样本。它在引入机制上借用 BIP-34 的办法(用区块版本号达到阈值后激活新规则),原文的 Block validity 一节明确如此说明。
原文开头有一行醒目的声明:本文档仍在进行中(work in progress),未完成、未实现、也不适合部署。读这份文档,要带着这个前提——它是 2014 年时点上对可锻性来源的一次系统清点与逐项封堵尝试,而不是一份生效过的规范。
三、它要解决什么
截至 2014 年 2 月,比特币交易在多个方面是可锻的:一笔有效交易可以在传播途中被修改而不失效,修改者并不需要相关私钥。原文列出它带来的麻烦:
- 发送方可能认不出被改过的自己的交易;
- 发送方可能已经用原交易的找零构造了后续交易;若被改过的版本被挖出,后续交易随之无效;
- 被改过的交易事实上构成双花,即使发送方并无恶意,也能被利用来让其他攻击更容易得手。
原文清点的可锻性来源共有九项:
- 非 DER 编码的 ECDSA 签名:参考客户端用 OpenSSL 验签,而 OpenSSL 接受比 DER 标准更宽松的序列化。自 v0.8.0 起,非 DER 签名已不再被中继;
- scriptSig 中的非压栈操作:任何能得到相同压栈结果、但不只是压入数据的脚本操作序列,都给出另一笔同样有效的交易;
- scriptSig 中非标准大小类型的压栈操作:压栈操作有多种(OP_0、单字节压栈、至多 75 字节的数据压栈、OP_PUSHDATA1/2/4),后者与前者结果相同,于是产生额外可能;
- 数字压栈的零填充:被当作数字解释的栈值可以被零填充出多种编码;
- ECDSA 签名本身的可锻性:把签名中的 S 取负(模曲线阶)仍然有效;
- scriptSig 中多余的操作:在脚本开头添加不被对应 scriptPubKey 消费的额外压栈;
- 被脚本忽略的输入:例如 scriptPubKey 以 OP_DROP 开头时,scriptSig 最后一个压栈总被忽略;
- 基于 sighash 标志的掩蔽:sighash 标志可以让签名忽略脚本的某些部分;
- 发送方重新签名:发送方(或任何持有相关私钥的人)总能为同一组输入输出造新签名。
原文的判断是:前六项与第七项的一部分可以用新增共识规则修复,后两项修不了。即使有了新规则,只要输出脚本不属于“安全集合”,其花费交易仍可能被锻造;但若限定使用安全的输出脚本集合,新规则可以让花费交易“可选地”不可锻——由花费者自己选择,他本人总能借第 8、9 项绕开。
四、规则本身
4.1 七条新规则
与可修复的来源逐项对应:
- 签名须规范 DER 编码:传给 OP_CHECKSIG、OP_CHECKSIGVERIFY、OP_CHECKMULTISIG、OP_CHECKMULTISIGVERIFY 的 ECDSA 签名必须严格 DER 编码;空字节数组(OP_0 的结果)也被允许,以便紧凑地构造一个故意无效的签名。用非 DER 签名验证会让整个脚本求值为假,而不只是该次验签失败;
- scriptSig 只许压栈:scriptSig 中只允许数据压栈,执行任何其他操作会让脚本求值为假;
- 压栈须用最小操作:必须尽可能使用最短的压栈操作;能用更短方式编码却用了更长操作的,脚本求值为假;
- 数字须最短编码:凡脚本操作码把栈值当作数字消费时,其编码必须是最短形式,且不允许“负零”;
- 签名 S 值取低半区:ECDSA 签名中的 S 必须至多为曲线阶的一半,把 S 限制在其低半取值范围;
- 不许留多余栈元素:scriptPubKey 求值结束时栈上必须只剩单个非零值;留下任何额外数据元素,脚本求值为假;
- CHECKMULTISIG 的占位元素须为空:OP_CHECKMULTISIG 与 OP_CHECKMULTISIGVERIFY 消耗的那个(不必要的)额外栈元素必须为空字节数组(OP_0 的结果),否则脚本求值为假。
原文还附了三组参考细则:低 S 值的具体范围与把高 S 换算为 S′ = 曲线阶 − S 的方法;DER 编码的逐字节格式(并指出该规则自 0.8.0 起已作为中继策略执行、只是还不是共识规则,与低 S 要求结合后签名总长至多 72 字节);压栈操作的最短形式对照表(空序列用 OP_0、0x01–0x10 用 OP_n、0x81 用 OP_1NEGATE、76–255 字节用 OP_PUSHDATA1、256–520 字节用 OP_PUSHDATA2、OP_PUSHDATA4 永不使用)以及数字最短编码的区间表。
4.2 引入方式:v3 区块与 v3 交易
新规则随 v3 区块与 v3 交易一同引入(交易跳过 v2,是为与区块规则的版本号保持同步;v2 交易与 v1 交易同等对待)。机制与 BIP-34 相同:
- 当过去 1000 个区块中 75% 为 v3 时,新共识规则激活:v3 区块中的所有交易须遵守规则 1–2;v3 区块中的 v3(及更高版本)交易还须遵守规则 3–7;
- 当过去 1000 个区块中 95% 为 v3 或更高时,v2 区块整体变为无效;
- v1(与 v2)交易则永远保持有效。
4.3 兼容性设想
原文的兼容性一节包括:新版节点软件使 v3 交易成为标准交易,并在其 scriptSig 满足上述规则时中继它们;v1 交易的中继不受影响,花费 v3 交易输出的 v1 交易也不受影响。钱包方面:v3 交易当时尚非标准,不能立即开始使用;应等网络中相当一部分节点升级到兼容代码后再启用。v3 的含义是“我想让这笔交易免受可锻性影响”,是否使用由钱包软件选择。
五、谁必须动
普通持币人:本提案从未部署,无需任何动作。按原文设想,将来即便启用,是否使用 v3 交易也是钱包层面的选择。
钱包开发者:设想中的职责是构造符合七条规则的 v3 交易(规范 DER、低 S、最小压栈、最短数字编码、干净的栈),并在网络升级比例足够前不抢先启用。
全节点与矿工:设想中需按 v3 区块的激活阈值执行新验证规则,并按新标准中继 v3 交易。
共识层:这是一次明确的共识规则收紧,涉及签名编码、脚本求值与区块/交易版本号。但它最终关闭,上述全部停留在设想层面。
六、现在算不算数,以及一个常见误读
状态为 Closed(已关闭)——不算数,从未部署。原文自己在篇首就声明它是进行中的草稿、未完成未实现;其价值在于把可锻性的来源清点得最全、把“共识规则能修到哪一步、修不到哪一步”划得很清楚。第 8、9 两项来源连作者本人都判定为共识规则修不了,这一点比七条规则本身更值得记住。
常见误读:
“BIP-62 曾经让比特币交易变得不可锻,后来才被撤销。”
不对。它从未生效过:文档自述未完成、未实现,档案状态是 Closed。可锻性在这份文档的年代主要靠策略层缓解——例如非 DER 签名自 v0.8.0 起不予中继、DER 要求先行作为中继策略执行——而不是靠这七条共识规则。把“有人提出过完整的共识方案”误记成“这套方案运行过”,是读这份文档最容易犯的错。
七、延伸
- 阅读版:https://bips.dev/62
- 官方原文:https://github.com/bitcoin/bips/blob/master/bip-0062.mediawiki
- 相关:BIP-34 区块 v2、高度写入 coinbase
- 仓库总目录:https://github.com/bitcoin/bips
