验出的地址若是合约:EIP-8151 给 ecRecover 预编译加了一道代码检查 图 1
验出的地址若是合约:EIP-8151 给 ecRecover 预编译加了一道代码检查 · 图 1

以太坊上几乎所有链下签名验证,最后一步都落在同一个地方:地址 0x01 的 ecRecover 预编译。它从哈希和签名里倒推出签名地址,多年来不问任何问题——验出谁就返回谁。EIP-8151 想给这台”验签车间”装一道闸门:验出的地址如果是个合约,就当作没验出来。这是 2026 年 2 月提出的草案,下面把它改了什么、为什么合约签名是问题、以及对普通用户的签名操作有没有影响讲清楚。

今天的行为:只问签名,不问身份

预编译接收哈希与签名参数 (v, r, s),做 ECDSA 公钥恢复,把恢复出的地址原样交回调用方。它不关心这个地址背后是什么:可以是一个私钥随时可控的外部账户,也可以是一个根本没有对应私钥的合约地址。后一种情况就是漏洞的温床——合约地址不存在对应的 ECDSA 私钥,任何声称”由合约地址直接 ECDSA 签名”的断言在密码学上都不成立,但许多合约的授权逻辑只认 ecRecover 的返回值,不会再多看一眼那个地址到底能不能自己发交易。

验出的地址若是合约:EIP-8151 给 ecRecover 预编译加了一道代码检查 图 2
验出的地址若是合约:EIP-8151 给 ecRecover 预编译加了一道代码检查 · 图 2

为什么已有合约防不住这个问题

协议层早有 EIP-3607:代码非空的账户禁止直接发起交易,堵住了”合约冒充外部账户发交易”的路。可问题在于 ecRecover 不在交易验证路径上,它只是签名验证路径上的一环——EIP-3607 管不着它返回什么。更麻烦的是,大量使用 permit 一类免 gas 授权的老合约已经部署在链上、不可升级,它们内部的验证代码没法补一道”检查恢复地址是否真的有私钥可控”的判断。规范在理由部分把这点说得很直白:改协议层,是因为让每份已部署合约自我修复在工程上不可行。这道检查的完整含义,结合同一个地址两个主人?EIP-3607 怎么堵住地址碰撞的漏洞里对 EIP-3607 的讲解看会更清楚。

检查规则与 gas 变化

按 EIP-8151 的规范文本:恢复失败时返回 32 个零字节,消耗 3000 gas,与今天一致;恢复成功时,在 3000 之外按 EIP-2929 的冷热规则追加账户访问成本——恢复地址已在访问集合里加 100 gas,否则加 2600 gas 并转为 warm。随后读取该地址的原始代码(不跟随任何委托):代码为空,或恰好是 23 字节的 0xef0100 加委托地址——即 EIP-7702 的委托指示——才返回恢复地址;两种情况之外一律返回 32 个零字节。不存在的账户按空代码处理。也就是说,普通外部账户与挂了 7702 委托的账户签名照常放行,被拦下的只有自身带代码的合约地址。

对谁有影响

对最常见的场景——你的外部地址签一条 ERC-2612 permit 或一次链下投票——没有任何变化,签名照样有效。被改变的是合约钱包的方向:合约地址想证明”我批准了这笔操作”,不能靠伪造一个 ECDSA 恢复结果蒙混,需要走 EIP-1271 那样真正调用合约验证逻辑的路。对普通用户,这条提案等于把一条密码学常识写进协议:验签结果里的合约地址,本来就不该被当作签名人。还有一层工程细节值得提前知道:改动生效后,被拦下的调用拿到的是 32 个零字节——一个全零地址,而不是报错。合约侧若拿这个返回值继续和某个授权地址比对,比对自然不匹配,验证失败;但若有合约偷懒地把它当作合法地址写进映射表,就会留下一个没人能对应私钥的零值记录。评估具体合约时,可以顺着这个思路读它的失败分支:处理得当的写法会把零地址直接拒绝,这类细节也是判断一份使用 permit 的合约是否被认真审过的线索之一。

状态与边界

EIP-8151 目前是草案,意味着在规范走完升级流程之前,主网上 ecRecover 的行为仍是”验出谁返回谁”,读写合约逻辑或评估风险时不要按已生效处理。理解这个预编译本身的工作原理,可以先看链上的验签车间:ecrecover 预编译怎么从签名倒推公钥;它和 EIP-7951 等扩展验签预编译的分工,在预编译合约是什么?EVM 预装的加密车间清单的预编译清单里有一张全景图。你的哪些操作会用到链下签名、不花 gas 的签名又有什么风险,签名不花 Gas,但不等于免费:链下签名与链上交易的安全边界专门讨论过,可以作为本篇的应用侧补充。