EIP-3978 解析:在回滚时退还状态修改成本——SSTORE / CREATE / SELFDESTRUCT / LOG 的 Gas 重新定价机制

发布时间:2026/9/15 17:02:28
EIP-3978 解析:在回滚时退还状态修改成本——SSTORE / CREATE / SELFDESTRUCT / LOG 的 Gas 重新定价机制 EIP-3978 解析在回滚时退还状态修改成本——SSTORE / CREATE / SELFDESTRUCT / LOG 的 Gas 重新定价机制【免费下载链接】EIPsThe Ethereum Improvement Proposal repository项目地址: https://gitcode.com/GitHub_Trending/ei/EIPsEIP-3978Gas refunds on reverts提出了一套针对 EVM 回滚场景的 Gas 定价修正方案当一笔交易或其内部子调用发生回滚时被丢弃的状态修改不应再按已持久化的价格收费而应仅保留访问成本、退还修改成本。本文基于本仓库 EIPS/eip-3978.md 的完整规范结合 EIP-2929 的访问成本模型与 EIP-3298、EIP-3529 的退款机制演进逐层拆解其revert_gas_refund计数器设计、各操作的目标定价以及当前提案状态帮助你理解回滚不白花钱这条 Gas 记账思路的来龙去脉。一、背景回滚时 Gas 收费的固有矛盾EVM 的执行模型是确定性的回滚一笔交易或回滚其中任何一个子调用都会丢弃调用帧内部产生的所有状态修改——存储槽的写入、合约的创建、日志、余额变动统统不会落盘。然而在现行 Gas 规则下这些被丢弃的修改却仍然以它们真的发生了的价格向用户收费。EIP-3978 的作者认为这是不公平的用户为从未持久化的修改支付了费用。这一矛盾在 EIP-3298 之后变得更加突出。EIP-3298 移除了存储清除退款storage-clearing refund与退款上限使得 gas 退款机制仅剩一种用途同一笔交易内部的存储恢复即把槽位写回交易开始时的 original value 时退还当次写入的修改成本。但一旦执行路径发生回滚退款不是被保留而是被完全抹除——这一点在更早的 EIP-1087 的测试用例中就已经被明确记录例如Writing x, then 0 to a storage slot that contains 0, then reverting the stack frame in which the writes occurred20200 gas无退款Writing x, then y to a storage slot that contains y, then reverting the stack frame in which the writes occurred5200 gas无退款也就是说修改被回滚时之前为该修改支付的成本无法通过退款机制拿回。由此产生了一个被 EIP-3978 明确点出的怪异现象某些场景下在交易末尾把代币转回去而不是回滚反而更便宜——因为转账路径可以保留既有的 gas 退款而回滚路径会丢掉一切。这显然违背了Gas 应当反映实际资源消耗的基本原则。二、设计动机每类操作的重新定价目标EIP-3978 的动机部分为各类状态修改型操作逐一给出了回滚场景下的目标价格——统一收敛到 100 gas即 EIP-2929 定义的WARM_STORAGE_READ_COST被回滚的操作现行成本目标价格SSTORE存储写入按 EIP-2200/2929 的写入价格如热槽 2900、冷槽另加 2100置零/新建场景更高100 gas即 SLOAD 的热读取价LOG0/LOG1/LOG2/LOG3/LOG4375 375×topic 数 内存成本100 gas带 ETH 转账的CALLpositive_value_cost 9,000 gas9,000 基础成本100 gas带 ETH 转账且目标账户为空的CALLvalue_to_empty_account_cost 25,000 gas25,000 基础成本100 gasCREATE/CREATE232,000 gas100 gasSELFDESTRUCT5,000 或 25,000 gas100 gas此外提案还附带一条补充主张CREATE与CREATE2的 32,000 gas 固定价格应当仅在返回的字节码非空时才收取——即创建出空合约不应按完整创建收费。这里的关键语义是100 gas 是访问成本不是修改成本。执行回滚时存储槽、账户、合约代码的访问确实发生了节点确实执行了读取路径这部分成本应当保留而修改从未持久化对应的修改成本应当退还。三、规范revert_gas_refund计数器机制EIP-3978 的核心是一个新增的、按调用帧callframe跟踪的计数器revert_gas_refund初始值为 0。3.1 哪些操作计入revert_gas_refund规范明确规定以下操作集合会修改revert_gas_refundSSTORELOG0、LOG1、LOG2、LOG3、LOG4CALLCREATE、CREATE2SELFDESTRUCT可以看到这正是上表中所有状态修改型操作的并集——它们都可能留下被回滚丢弃的修改。3.2 累积规则每当上述操作执行时revert_gas_refund按以下公式累积call.revert_gas_refund operation.gas - WARM_STORAGE_READ_COST其中operation.gas是该操作在本次执行中实际收取的 Gas含冷/热访问加价WARM_STORAGE_READ_COST即 EIP-2929 中定义的 100 gas。公式的含义非常直观从操作的总成本中扣掉 100 gas 的访问成本剩余部分就是修改成本全部计入待退还的池子。例如一次热槽SSTORE若收取 2,900 gas则revert_gas_refund增加 2,800一次CREATE收取 32,000 gas则增加 31,900。3.3 回滚时的结算规则在回滚发生时不再简单地抹除gas_refund而是将revert_gas_refund并入交易级退款计数器。规范给出的伪代码如下if (call.reverted) { // existing behavior tx.gas_refund - call.gas_refund; // New behavior added to existing according to the EIP-3978 tx.gas_refund call.revert_gas_refund; }第一行是既有行为回滚帧此前累积的gas_refund同交易内的存储恢复退款被撤销第二行是 EIP-3978 新增行为回滚帧累积的revert_gas_refund被加入到交易级退款中。也就是说EIP-3978 并不是推翻既有退款机制而是在其之上增加一条回滚帧修改成本退还的新路径且仍然复用tx.gas_refund这一交易级计数器来结算。由于退款最终仍走既有的tx.gas_refund结算通道可以推断其同样会受 EIP-3529 所设的退款上限gas_used // MAX_REFUND_QUOTIENT其中MAX_REFUND_QUOTIENT 5约束即单笔交易退款不会超过已消耗 Gas 的五分之一——这保证了回滚退款不会被滥用为负成本交易。3.4 与 EIP-2929 访问成本模型的衔接EIP-3978 在requires字段中明确依赖 EIP-2929。EIP-2929 为以太坊引入了交易级热/冷访问记账accessed_addresses与accessed_storage_keys集合并定义了三项关键常量常量值含义WARM_STORAGE_READ_COST100热槽/热账户的读取成本COLD_SLOAD_COST2,100首次读取某个存储键的冷加载成本COLD_ACCOUNT_ACCESS_COST2,600首次访问某个账户/地址的冷加载成本EIP-3978 选择以WARM_STORAGE_READ_COST100 gas作为回滚修改的目标价格与基准扣减项正是因为它代表状态访问的最低真实成本——无论冷热访问总是发生了而修改则因回滚而没有发生。同时由于 EIP-2929 规定如果某个作用域回滚访问集合应恢复到进入该作用域之前的状态回滚并不会影响访问记账本身EIP-3978 只需在退款一侧做文章两者可以干净地叠加。四、设计理由Gas 应当反映使用成本EIP-3978 的 Rationale 只有一句话但它是整个提案的定价哲学Gas should reflect the cost of use. The revert cost reflects the cost of access during execution, but not the cost of modification.即Gas 应当反映真实的使用成本。回滚场景下执行的访问读存储、查账户、加载代码确实消耗了节点资源应照常收费而修改写存储、建合约、销毁合约因为被回滚而从未落地不应再收费。EIP-3978 正是把这两部分成本在回滚路径上做了精确切分——operation.gas - WARM_STORAGE_READ_COST这一公式就是修改成本的量化表达。五、向后兼容性EIP-3978 的 Backwards Compatibility 一节声明没有已知的向后不兼容问题。这一判断是合理的原因有二退款不影响执行语义。正如 EIP-3529 所强调的refund 只在交易执行结束后一次性结算执行过程中任何调用帧都观察不到退款值因此修改退款规则不会改变任何合约的可执行性改动方向是少收费。EIP-3978 只会让回滚路径的用户支付得更少不会让任何操作变得更贵现有合约的 Gas 预算不会因此被突破。需要说明的是任何 Gas 定价类的 Core 变更若要生效仍需通过硬分叉scheduled network upgrade引入这一点与 EIP-3298、EIP-3529 的落地方式一致。六、提案现状测试用例、参考实现与状态说明需要如实说明的是EIP-3978 的Test Cases测试用例、Reference Implementation参考实现与Security Considerations安全考虑三个章节目前均标注为TBD——提案并未给出具体的执行向量测试集与客户端实现代码。从元数据看该 EIP 创建于 2021-09-16最后更新于 2022-02-14当前状态为Stagnant停滞。依据 EIP-1 的定义任何处于Draft、Review或Last Call状态的 EIP若超过 6 个月无活动将被移至Stagnant作者或 EIP 编辑可将其移回Draft或更早状态以复活该提案。因此本文介绍的机制属于一份完整但尚未被采纳的历史提案它反映的是 2021–2022 年间社区对回滚成本公平性的一种系统化设计思考而非当前主网生效的规则。尽管如此其保留访问成本、退还修改成本的思路在后续协议演进中仍有回响——例如本仓库中更新的 EIP-3298 修订版继续强调退款计数器随调用帧记账、回滚帧的增量被丢弃的语义以及退款与状态费用结算的分离这正与 EIP-3978 试图修正的问题域一脉相承。七、结语与延伸阅读EIP-3978 用最简的机制设计回答了回滚时修改成本该不该退的问题新增一个按调用帧跟踪的revert_gas_refund计数器将回滚帧内SSTORE、LOG*、CALL、CREATE、SELFDESTRUCT等操作的修改成本总成本减去 100 gas 访问成本累积起来在回滚结算时并入交易级退款。整个提案只有两条公式、一段伪代码、一张操作清单却精准地补上了既有退款机制在回滚路径上的缺口。若想进一步理解其依赖与背景建议按以下路径在本仓库内延伸阅读EIP-2929定义WARM_STORAGE_READ_COST100、COLD_SLOAD_COST2,100、COLD_ACCOUNT_ACCESS_COST2,600及热/冷访问集合是 EIP-3978 的定价基准EIP-3298移除存储清除退款与退款上限促使退款机制收缩为同交易内存储恢复专用正是 EIP-3978 动机的历史前提EIP-3529引入MAX_REFUND_QUOTIENT 5的退款上限说明 EIP-3978 新增退款所经由的tx.gas_refund通道的约束条件EIP-1283 / EIP-1087SSTORE净计量与存储退款的早期设计其中 EIP-1087 明确记载了回滚帧写入无退款的历史行为EIP-1EIP 状态机定义可查询Stagnant等状态的含义与流转规则。本文依据 EIP-3978 及本仓库相关 EIP 文档撰写该提案内容依据 CC0 许可 发布。【免费下载链接】EIPsThe Ethereum Improvement Proposal repository项目地址: https://gitcode.com/GitHub_Trending/ei/EIPs创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考