智能合约互相调用是以太坊最频繁的动作,而承担这个动作的 CALL、DELEGATECALL、STATICCALL 三条指令从早期虚拟机设计一路服役至今,语义层层加码:它们要求调用方报一个燃料额度,还要调用方自己算清”带不带转账、拿不拿补贴”。EIP-7069 由 Alex Beregszaszi 等人在 2023 年 5 月提交,为对象格式合约准备了三个替身——EXTCALL、EXTDELEGATECALL、EXTSTATICCALL,外加补齐 returndata 读法的 RETURNDATALOAD,提案现标 Stagnant。
砍掉的第一样东西:燃料自选权
旧指令允许调用方说”这次最多给你多少 Gas”。听起来是精明的预算控制,实际埋了两颗雷。雷一是可观测性:Gas 价格表每几年就要重排一次,EIP-1884 上调存储读价那年,一批合约因为硬编码了传参额度直接批量卡死——无论用户给交易加多少燃料都救不回来,因为瓶颈在合约内部那道人为的天花板。雷二是绝大多数人根本不用这个旋钮:编译器从 Solidity 0.4.21 起默认把全部剩余燃料传给被调方,除非开发者显式改写。既然几乎没人依赖,删掉换来一个 一个宝贵的性质:任何出气失败都能靠给交易加更多燃料解决,上限只剩区块总量。提案保留了兜底底线——被调方至少拿两千三,调用方至少留五千,这两道地板以后调价也不影响合约代码。
语义清单里的其他改动
新指令用固定的燃料公式:调用方剩余燃料减去六十四分之一与五千这两者中的较大值,余额全部带进被调方。账户访问费沿用冷热定价,冷目标照付两千六的差价,带转账创建新账户再收两万五。返回值从一/零布尔升级为可扩展的三档:零成功、一回退、二失败——回退与硬失败的区分让调用方第一次能从栈上直接知道对方是”主动拒绝”还是”燃料烧穿”。输出不再写入指定内存地址,改用 RETURNDATALOAD 按需读取,或 RETURNDATACOPY 整段拷贝;在对象格式代码里越界读取不再抛异常,缺的部分补零。顺带一提,老 CALL 在新格式合约里会被校验器直接拒收,两套指令井水不犯河水。
为什么停在 Stagnant
这套重构的所有好处都挂在一个前置工程上:对象格式本身。EOF 多年来在”先安全容器再谈新指令”的争论中反复搁置,7069 作为其配套件跟随停摆。另一层阻力是保守情绪:CALL 语义的每处简化都会写进新合约的假设里,而 Gas 政策制定者担心失去”合约自带燃料上限”这层缓冲后,未来的定价弹性全部转移到交易层。提案文本里的参数表原样沿用既有冷热常数,就是在表明态度:我不改价格,只改通道。
快速问答
问:不给被调方限燃料,重入攻击会不会更容易? 答:重入防护从来不由燃料额度承担——写锁在调用前落账、检查在返回后复核,才是正统做法;燃料旋钮挡不住先改状态再回调的脚本。
问:状态码二和一回退在费用上有什么不同? 答:回退时未用燃料退还调用方,硬失败(典型是燃料耗尽)则把传下去的燃料全部烧掉,这是三档设计里最贵的一个分支。
一次调用的两种账本
用一个具体场景收尾。合约钱包替用户转发一笔调用目标合约的交易。老指令下,钱包得先估算自己剩多少燃料、挑一个数传下去,目标合约里的存储操作若恰好撞上调价年份,传下去的额度不够,整条链在钱包这一环掐死,用户加再多手续费无济于事。新指令下,钱包把除保留线以外的全部燃料放出去,目标合约能花多少花多少,出气错误变成用户侧可调的交易参数。省心的另一面是钱包放弃了”我替你设上限”的护栏——好在重入与授权护栏本就该由状态锁和调用方身份检查提供,燃料旋钮从来不是合格的安全部件。两本账摊开,就能理解为什么提案作者把它定位成给未来调价腾空间的地基工程。
风险提示:EVM 指令语义以当前规范为准,误用调用模式可能导致合约资产损失;本文不构成投资建议。
发表评论
还没有评论,来说两句吧。
评论区为展示样式,提交不会被处理。