预编译合约的过路费比办事本身还贵:EIP-1109 专用调用指令的算盘 图 1
预编译合约的过路费比办事本身还贵:EIP-1109 专用调用指令的算盘 · 图 1

以太坊虚拟机里有一类特殊住户:地址段开头的预编译合约。它们不是字节码写的合约,而是客户端直接实现的算法——哈希、椭圆曲线运算、配对检查等。调用它们时,虚拟机不执行程序,直接算出结果,并按每种算法事先定好的价格收费。EIP-1109 在 2018 年 5 月由 Jordi Baylina 提出,盯上的是这套机制里一个别扭的计费细节,提案现标 Stagnant。

双重计费卡在哪里

问题出在入口。字节码要调用预编译,用的和普通合约调用完全相同的 CALL 指令,而 CALL 自带一笔固定开销——按当时代价是七百 Gas 的账户访问费。于是出现荒诞的叠层:SHA256 预编译本身只收六十 Gas,可用一笔调用实际花掉七百六十;椭圆曲线加法预编译定价五百 Gas,走 CALL 就要八百起。提案指出,预编译的成本模型早就把运算量算进去了,再叠一层为通用调用设计的安全保证金毫无道理,而且它划了一条硬地板——任何预编译的等效使用成本永远不可能低于七百 Gas,定价再低也白搭。

专用指令怎么省钱

EIP-1109 的解法是新增一条专用操作码 PRECOMPILEDCALL,提案给它挑了紧跟 STATICCALL 之后的空位编码。计费变成两层:指令本身只收基础费两个 Gas,外加目标预编译的专项定价,通用 CALL 那七百 Gas 的账户费则整个免掉。参数表也比 CALL 精简:五个栈位分别装目标地址、输入内存位置与长度、输出内存位置与长度,没有价值字段——预编译本来也不收转账。正因为预编译不执行操作码、没有逃逸路径,这条指令连 Gas 参数都不需要传:剩余燃料不够两个 Gas 加专项费时直接出气耗尽报错,也不用做栈深度检查。返回语义保持朴素:成功压一,目标地址不是已定义预编译、或者输入非法导致失败则压零。拿它去调普通合约地址会被当作调用未定义合约,虚拟机必须拒绝执行。

提案特意强调向后兼容:老的 CALL 调用预编译的行为一个字节都不变,新指令只给愿意用它的合约省钱。这条路线也不是凭空拍的——早年就有直接给 CALL 里预编译分支免税的尝试,作者认为单独加指令比改动 CALL 语义更好实现、更好测试。

为什么值得较这个真

单看一次省七百 Gas 无感,真正要紧的是密集调用场景:零知识证明验证器、批量签名聚合、账户抽象的验证回路,一次交易里可能要过几十次预编译门,门门七百的过路费能让证明系统的有效成本翻倍。EIP-1109 之后,同类诉求由 2018 年同期的 EIP-1108 用另一条路部分解决——大幅下调相关预编译的专项定价;再后来 EIP-2069、EIP-2565、EIP-2929 等提案持续重排冷热线索费与预编译费用。专用指令本身没被激活,操作码空位后来也各有归属,但双重计费这个靶子算是被别的箭射中了。

快速问答

问:预编译和普通合约在 Gas 上最大的差别是什么? 答:普通合约按执行的每条指令计价,预编译按整笔运算一口价,不看字节码——因为虚拟机根本不会逐条跑它。

问:调用不存在的预编译地址会怎样? 答:走普通 CALL 会得到空返回并成功;而提案里的专用指令必须返回失败,不允许把”地址没定义”误当成”有空合约”。

一条判断线

给合约开发者留三条经验。第一,能合并就合并:同一笔交易里对同一预编译的连续调用,输入拼接后一次进门,比分几次进门省下整段入口费。第二,别把专用指令当万能钥匙:它只砍入口开销,专项运算费一分不少,账要为运算量本身算。第三,看升级公告里的价目表而不是教程:入口费与专项费的相对高低随版本变化,旧文章里”永远贵七百”的结论对当前网络可能已经过时。三条共性是把成本意识从”指令怎么省”移到”数据怎么攒”——跨门次数永远是最容易砍掉的那一项。

风险提示:Gas 价格与预编译定价随网络升级变化,以当期规范为准;本文不构成投资建议。