协议演进 / BIP/比特币改进提案 / BIP-146

BIP-146 处理签名编码可锻性

以 LOW_S 与 NULLFAIL 两条规则,堵住 ECDSA 签名编码层面的两种可锻性来源。本 BIP 目前处于已关闭状态。

一、档案卡

档案卡
编号BIP-146
标题Dealing with signature encoding malleability
作者Johnson Lau <[email protected]>, Pieter Wuille <[email protected]>
指定日期2016-08-16
层级Consensus (soft fork)(共识(软分叉))
类型Specification(规范类)
状态Closed(已关闭)
阅读版https://bips.dev/146
官方原文https://github.com/bitcoin/bips/blob/master/bip-0146.mediawiki

二、放在哪一族

可锻性(malleability)修复族的一员,处理的是“签名编码”这一路。它的前身是 BIP-62(处理可锻性)——原文致谢部分写明,本篇正是从那份已放弃的大提案中拆分出来的;先行的 BIP-66(严格 DER 签名)已以软分叉方式强制了签名编码格式。

同族关系:

BIP-62:处理可锻性的总提案,本篇与 BIP-147 都是从中抽出的单项

BIP-66:严格 DER 签名,本篇的两条规则都以它已经生效为前提

BIP-147:处理哑栈元素可锻性,与本篇并列的另一路修复

BIP-141:隔离见证;它让非见证交易之外的问题以另一种方式收敛,本篇的适用范围则同时覆盖见证前与见证脚本

三、它要解决什么

签名可锻性,是指网络上的任意中继节点在没有私钥的情况下,也能改动一笔交易里的签名、且改完仍有效。对非隔离见证交易,这会改变 txid,使建立在其上的未确认子交易随之失效;对隔离见证交易,txid 虽然不再受第三方可锻性影响,但 wtxid 仍会被改变,并可能降低紧凑区块中继(BIP-152)的效率。

BIP-66 强制严格 DER 编码之后,原文指出 ECDSA 签名还剩两个已知的可锻性来源:

3.1 ECDSA 本身的高 S 对称性

一个 ECDSA 签名里的 S 值,取其对曲线阶的“负数”(即用曲线阶减去 S)仍然是有效签名。也就是说,同一笔花费天然存在两个等价签名,任何人都可以把高 S 换成低 S(或反过来),交易随之变样。

3.2 失败签名的任意性

当签名在 OP_CHECKSIG 或 OP_CHECKMULTISIG 中验证失败时,脚本只是往栈上返回 FALSE 并继续执行。这个注定失败的签名可以填任何值——只要符合 BIP-66 的编码规则——于是它又成了一个可以随意改动的字节串。

本篇要用两条新共识规则,分别封死这两条路。

四、规则本身

新规则同时适用于见证前脚本与见证脚本中的签名验证。

4.1 LOW_S:S 必须落在低半区

要求 ECDSA 签名中的 S 值至多为曲线阶的一半,即在 0x1 到 0x7FFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF 5D576E73 57A4501D DFE92F46 681B20A0(含端点)之间,并满足严格 DER 编码。该要求适用于一切传入 OP_CHECKSIG、OP_CHECKSIGVERIFY、OP_CHECKMULTISIG、OP_CHECKMULTISIGVERIFY 做 ECDSA 验证的签名(包括 BIP-141 的 P2WPKH)。

执行方式很硬:若某个待验签名的 S 不合规、且它不是空字节数组,整个脚本立即判定为失败(false),不再继续。

高 S 很容易无损地改成低 S:S' = 曲线阶 − S,签名效力不变,因此这条规则不损失任何功能。

4.2 NULLFAIL:失败就必须为空

否则,整个脚本立即判定为失败。

道理很直白:既然结果注定是“不通过”,就不允许再用一段任意字节充当失败签名;想表达失败,只能交空。

4.3 部署在原文中并未定案

本篇的部署一节只写了“通过版本位 BIP-9 部署,细节待定(TBD)”,主网与测试网的开始时间、超时时间全部留空。这与它最终 Closed 的状态互为印证:作为独立软分叉,它从未走完部署流程。

4.4 兼容性背景(原文所述)

原文给出的背景是:参考客户端自 v0.9.0 起生成的签名就符合 LOW_S,LOW_S 自 v0.11.1 起已作为中继策略执行;截至 2016 年 8 月,违反要求的交易上链已极少。NULLFAIL 则自 v0.13.1 起作为中继策略执行。也就是说,两条规则在“策略层”长期存在,但把它们升为共识规则的这一步,本篇没有完成。原文同时提醒:设计奇异脚本时必须格外注意这些规则。

五、谁必须动

普通持币人:无直接动作。主流钱包生成的签名本来就是低 S;本篇若激活,影响的主要是构造特殊脚本的人。

钱包开发者:按原文背景,签名生成应始终使用低 S;构造会“故意验签失败”的脚本(如某些分支逻辑)时,失败位置只能放空签名,不能放任意占位字节。

全节点与矿工:若本篇按软分叉激活,节点需在共识验证中执行 LOW_S 与 NULLFAIL,违反者整脚本失败。但它最终未激活,节点实际执行的仍是策略层面的同名检查,二者效力不同:策略只影响转发与打包,共识才决定区块是否有效。

共识层:本篇拟议的是共识规则收紧(软分叉方向);因未部署,共识规则本身并未因本篇而改变。

六、现在算不算数,以及一个常见误读

状态为 Closed(已关闭)。含义是:这份独立提案已被关闭,不再推进——它的 LOW_S 与 NULLFAIL 规则没有经由本篇成为共识规则。日常交易中之所以几乎见不到高 S 或非空失败签名,是因为同样的要求长期作为节点的中继与打包策略存在(见 4.4 的原文背景),而不是因为本篇已经生效。

常见误读:

“BIP-146 已经修复了签名可锻性。”

不准确。它把修复方案写成了规范,并给出了完整的判定规则与例子,但部署参数始终待定、状态最终为 Closed。作为共识升级,它没有落地;把它与真正已激活的 BIP-66、已部署的 BIP-147 混为一谈,就会误判现行规则的来源。

七、延伸

本页内容整理自 bitcoin/bips 仓库与 bips.dev,事实已与仓库核对;解读仅供学习参考。