BLAKE2 预编译合约:EVM 里的哈希函数外挂插槽 图 1
BLAKE2 预编译合约:EVM 里的哈希函数外挂插槽 · 图 1

以太坊虚拟机自带一套固定指令集,但密码学世界每隔几年就有新函数要进战场,指令集不能每来一个新哈希就加一条操作码。EVM 的解决方案是预编译合约:把一小段地址空间(0x01 到 0x06 是老牌居民,ecrecover、SHA-256、RIPEMD-160、模幂、加法模、乘法模、配对检验各占一坑)预留成『外挂插槽』,插槽里的逻辑不执行字节码,客户端直接跑原生代码,按定价表收 gas。新算法想上车,改一次客户端就行,不用动虚拟机本身。EIP-152 就是把 BLAKE2 压缩函数插进 0x09 号插槽的那次施工,2019 年 12 月随伊斯坦布尔硬分叉在以太坊主网生效。

先认识 BLAKE2 的 F:一台只跑一拍的压缩机

BLAKE2 家族是 2013 年公开的哈希函数,设计目标是跑在通用 CPU 上比 SHA 系列更快,后来成为不少项目的默认哈希(例如文件同步与内核子系统)。但要理解预编译装的东西,得先分清 BLAKE2 的『F 压缩函数』和完整哈希:F 不是一次性吃完整条消息的哈希——它是一次搅拌——输入当前 64 字节状态向量、128 字节消息块、字节计数器和最终块标志,输出搅拌后的新状态。完整哈希是一遍遍调 F 直到吃光消息。EIP-152 预编译暴露的就是这台单次搅拌器:输入编码固定 213 字节——4 字节轮数(大端无符号)、64 字节状态向量、128 字节消息块、8 字节计数器低半、8 字节计数器高半、1 字节最终块标志,顺序紧排、不许错位;输出 64 字节新状态。轮数由调用方给定:12 轮就是标准 BLAKE2b 的一拍,跑 1024 轮就是另一种参数。编码里任何一个字段长度不对、最终块标志非法,调用直接返回空并 revert 语义,这是预编译合约的标准防御姿态。

gas 逐轮计:为什么定价表长这样

预编译合约的花费跟字节码世界完全不同:不看指令条数,只看运算量。EIP-152 的规则干净得像一个公式——每轮搅拌收固定 gas,调用成本等于轮数乘以单价;客户端用 C 或 Rust 跑原生实现,一轮的工作量可预测,不像恶意字节码那样能被构造出『最坏情况爆炸』。逐轮计价顺带解决了一个治理难题:调用方自己决定轮数,等于自报工作量,协议不必为『别人选择的工作』兜底。这种按参数定价的模式后来成了预编译的主流——EIP-196/197 的配对检验、EIP-2537 的 BLS 操作都按输入规模分段收费。从工程视角看,这是虚拟机把『我不擅长这段运算』明说:把难题交给原生层,定价换确定性。

当年的目标:验证 Zcash 区块头,今天的用途:电路里的电路

EIP-152 的立项理由很年代感:Zcash 的工作量证明算法 Equihash 里要用到 BLAKE2,一次 Zcash 区块头验证要迭代 512 轮 BLAKE2F——当时 EVM 里唯一的哈希是 SHA-256 和 RIPEMD-160,用 Solidity 手写 BLAKE2 再乘 512 轮的 gas 贵到跨链验证根本跑不起。预编译装上后,在以太坊合约里验 Zcash 区块头进了可行区间,跨链原子交换的路线图里它被反复点名。故事的后半段没人预料到:真正让 BLAKE2F 预编译常年出现在 gas 报表上的,是 zk 证明的以太坊内验证——SNARK/STARK 聚合器在 EVM 里验证证明时,自己的电路哈希(很多 zk 系统选 BLAKE2 因为它在 SNARK 友好度上比 keccak 便宜)需要反过来调用预编译。也就是说,一条为跨链开的插槽,变成了零知识系统『在电路里套电路』的公共齿轮:在 SNARK 内用预编译跑 keccak 太贵、跑 BLAKE2F 便宜,很多桥和聚合器的方案文档因此把 BLAKE2 写进电路哈希选型。一个机制两种命运,倒是预编译插槽弹性的好注脚。

预编译的得与失

插槽模式并非没有争议。它的优点直白:新算法上车不用等一整套操作码提案,客户端原生实现性能有保证。代价也有:预编译对字节码是不可见的——extcodesize 看它是空的、静态分析工具读不到它的逻辑、不同客户端的边界行为必须靠规范逐字节钉死(编码顺序、错误返回值都是共识级别的细节,一个客户端多返回一个字节就能硬分叉)。此外每个预编译都是永久承诺:上了车就不能撤,定价错了只能靠后续 EIP 改参数。因此以太坊对新增预编译出了名的谨慎——0x07、0x08 两个坑位从创世空到今天,配对类扩展提案讨论多年、落地的是已验证需求明确的。对开发者而言,实操守则很朴素:预编译地址硬编码在系统里,永远查清楚当前链版本装了哪些地址,跨链复用合约时逐个确认。

快速问答

预编译合约也是合约吗?地址形态像,实现完全不同:没有字节码、没有存储、不走解释器,是客户端原生代码挂着以太坊地址,按定价表而非指令计费。

为什么不用 SHA-256 插槽验 Zcash?Equihash 的 PoW 定义里哈希函数就是 BLAKE2b,替换哈希等于替换算法定义,验证结果直接作废。

预编译返回空和 revert 是一回事吗?编码非法时预编译按规范失败:调用方拿到调用失败标志与空返回,语义上等同子帧 revert,改动回滚、gas 不退未跑部分。

风险提示:本文仅作以太坊机制科普,不构成任何投资建议。