Alert Source Discuss
Standards Track: Core

EIP-3529: 减少退款

Authors Vitalik Buterin (@vbuterin), Martin Swende (@holiman)
Created 2021-04-22
Requires EIP-2200, EIP-2929, EIP-2930

简单总结

移除 SELFDESTRUCT 的 gas 退款,并将 SSTORE 的 gas 退款降低到一个较低的水平,在这个水平上,退款仍然是可观的,但对于当前对退款机制的“利用”来说,不再足够高。

动机

最初引入 SSTORESELFDESTRUCT 的 gas 退款是为了激励应用程序开发者编写实践“良好的状态卫生”的应用程序,清除不再需要的存储插槽和合约。然而,这种技术的好处已被证明远低于预期,并且 gas 退款产生了多个意想不到的有害后果:

  • 退款催生了 GasToken。GasToken 在将 gas 空间从低费用时期转移到高费用时期方面具有优势,但它也对网络有不利影响,特别是在加剧状态大小(因为状态槽被有效地用作“电池”来节省 gas)和低效地阻塞区块链 gas 使用方面
  • 退款增加了区块大小的方差。区块中实际消耗的 gas 的理论最大值几乎是表面上的 gas 上限的两倍(因为退款为区块中的后续交易增加了 gas 空间,尽管退款上限为交易 gas 使用量的 50%)。这并非致命,但仍然不受欢迎,特别是考虑到退款可以用于维持比 EIP-1559 更长时间的 2 倍使用量峰值。

规范

参数

常量
FORK_BLOCK 待定
MAX_REFUND_QUOTIENT 5

对于 block.number >= FORK_BLOCK 的区块,以下更改适用。

  1. 移除 SELFDESTRUCT 退款。
  2. SSTORE_CLEARS_SCHEDULE(如 EIP-2200 中定义)替换为 SSTORE_RESET_GAS + ACCESS_LIST_STORAGE_KEY_COST(截至 EIP-2929 + EIP-2930 为 4,800 gas)
  3. 将交易后退还的最大 gas 减少到 gas_used // MAX_REFUND_QUOTIENT

备注:之前,最大 gas 退款 定义为 gas_used // 2。这里我们将常量 2 命名为 MAX_REFUND_QUOTIENT,并将其值更改为 5

理由

EIP-2200 中,引入了三种退款情况:

  1. 如果原始值为非零,并且新值为零,则将 SSTORE_CLEARS_SCHEDULE(当前为 15,000)gas 添加到退款计数器
  2. 如果原始值为零,当前值为非零,并且新值为零,则将 SSTORE_SET_GAS - SLOAD_GAS(当前为 19,900)gas 添加到退款计数器
  3. 如果原始值为非零,当前值为不同的非零值,并且新值等于原始值,则将 SSTORE_RESET_GAS - SLOAD_GAS(当前为 4,900)gas 添加到退款计数器

在这三种情况中,只有 (1) 启用了 gastoken,并允许一个区块在执行上消耗超过区块 gas 上限的 gas。(2) 不具有此属性,因为要获得 19,900 的退款,必须 之前将 相同的存储槽 从零更改为非零,这会花费 20,000 gas。无法从清除一个存储槽中获得 gas 并将其用于编辑另一个存储槽意味着它不能用于 gas token。此外,获得退款需要恢复存储写入和扩展的效果,因此退还的 gas 不会增加客户端在处理区块时的负载。(3) 的行为类似:只有在之前在同一存储槽上花费了 5,000 gas 时才能获得 4,900 的退款。

此 EIP 处理情况 (1)。我们可以使用类似的“配对”参数来确定 gastoken 在什么条件下是不可行的(即,你无法从存储槽中获得比你投入的更多的 gas),将每个退款映射到同一交易中同一存储槽上的先前支出。如果存储槽在其原始值为非零时更改为零,则有两种可能性:

  1. 这可能是第一次将存储槽设置为零。在这种情况下,我们可以将此事件与首次读取和编辑存储槽的 SSTORE_RESET_GAS + ACCESS_LIST_STORAGE_KEY_COST 最低成本配对。
  2. 这可能是第二次或之后将存储槽设置为零。在这种情况下,我们可以将此事件与上一次将值设置为零的最近一次配对,其中从退款中移除 SSTORE_CLEARS_SCHEDULE gas。

对于第二次及之后的事件,SSTORE_CLEARS_SCHEDULE 具有什么值并不重要,因为该大小的每个退款都与相同大小的退款移除配对。这留下了第一个事件。为了保证在插槽上花费的总 gas 为正,我们需要 SSTORE_CLEARS_SCHEDULE <= SSTORE_RESET_GAS + ACCESS_LIST_STORAGE_KEY_COST。因此,此 EIP 只是将 SSTORE_CLEARS_SCHEDULE 降低到这两个成本之和。

对此 EIP 的一种替代直觉是,对于清除尚未读取的数据(通常是“无用”数据),不会有净退款,但对于清除已读取的数据(这很可能是“有用”数据),将继续有净退款。

向后兼容性

退款目前仅在交易执行 应用,因此它们不会影响执行期间任何特定调用帧可用的 gas 量。因此,删除它们不会破坏任何代码的执行能力,尽管它会使一些应用程序在经济上不可行。

Gas token 将变得毫无价值。DeFi 套利机器人今天经常使用已建立的 gas token 方案或自定义替代方案来降低链上成本,他们将受益于重写其代码以删除对这些不再起作用的 gas 存储机制的调用。

然而,完全保留 new = original = 0 != current 情况下的退款,并在其他 nonzero -> zero 情况下保留一些退款,确保了一些关键用例继续获得(并且应该获得)有利的 gas 成本待遇。例如,zero -> nonzero -> zero 存储设置模式继续仅花费约 100 gas。这种模式的两个重要示例包括:

  • 防重入锁(通常在子调用开始之前从 0 翻转到 1,然后在子调用结束时翻转回 0)
  • ERC20 批准和发送(“批准值”在批准 token 转移时从零变为非零,然后在 token 转移处理时变回零)

对存储清除激励的影响

对早期退款移除 EIP(EIP-3298EIP-3403)的批评是,这些 EIP 完全消除了将值设置为零的动机,鼓励用户如果他们预计即使是很小的概率他们会想再次使用该存储槽,也不要完全清除存储槽。

例如,如果你有 1 个单位的 ERC20 token,并且你要赠送或出售你的全部余额,你可以只赠送 0.999999 个单位,并将剩余的留下。如果你将来决定用同一个帐户重新获得更多该 token,你只需为 SSTORE 支付 5000 gas(读取 2100 + 非零到非零设置 2900),而不是 22100(零到非零设置 20000)。今天,这被清除的 15000 退款所抵消,因此只有当你超过 15000 / 17100 = 87.7% 确定你将再次使用该插槽时,你才有动机这样做;使用 EIP-3298 或 EIP-3403,抵消激励将不存在,因此如果再次使用该插槽的机会任何 大于 0% 的值,则设置为非零更好。

剩余 4800 gas 的退款,因此只有当你期望超过 4800 / 17100 = 28.1% 的概率你将再次使用该插槽时,才有动机保持存储插槽非零。这并不完美,但可能高于普通人期望以后用同一个地址重新获得 token 的期望,如果他们清除了他们的全部余额。

将退款限制为 gas 支出的 1/5 意味着此退款只能用于将处理区块所需的存储写入操作的数量最多增加 25%,从而限制了使用此机制进行以存储写入为中心的拒绝服务攻击的能力。

测试用例

EIP-2929 Gas 成本

请注意,“热”插槽和“冷”插槽之间存在差异。下表显示了截至 EIP-2929 的值,假设所有接触到的存储插槽都已经是“热的”(区别在于一次性成本 2100 gas)。

代码 使用的 Gas 退款 原始 1st 2nd 3rd 有效 gas(退款后)
0x60006000556000600055 212 0 0 0 0   212
0x60006000556001600055 20112 0 0 0 1   20112
0x60016000556000600055 20112 19900 0 1 0   212
0x60016000556002600055 20112 0 0 1 2   20112
0x60016000556001600055 20112 0 0 1 1   20112
0x60006000556000600055 3012 15000 1 0 0   -11988
0x60006000556001600055 3012 2800 1 0 1   212
0x60006000556002600055 3012 0 1 0 2   3012
0x60026000556000600055 3012 15000 1 2 0   -11988
0x60026000556003600055 3012 0 1 2 3   3012
0x60026000556001600055 3012 2800 1 2 1   212
0x60026000556002600055 3012 0 1 2 2   3012
0x60016000556000600055 3012 15000 1 1 0   -11988
0x60016000556002600055 3012 0 1 1 2   3012
0x60016000556001600055 212 0 1 1 1   212
0x600160005560006000556001600055 40118 19900 0 1 0 1 20218
0x600060005560016000556000600055 5918 17800 1 0 1 0 -11882

减少退款

如果通过将 SSTORE_CLEARS_SCHEDULE 从 15000 更改为 4800(并删除自毁退款)来部分删除退款,这将是比较表。

代码 使用的 Gas 退款 原始 1st 2nd 3rd 有效 gas(退款后)
0x60006000556000600055 212 0 0 0 0   212
0x60006000556001600055 20112 0 0 0 1   20112
0x60016000556000600055 20112 19900 0 1 0   212
0x60016000556002600055 20112 0 0 1 2   20112
0x60016000556001600055 20112 0 0 1 1   20112
0x60006000556000600055 3012 4800 1 0 0   -1788
0x60006000556001600055 3012 2800 1 0 1   212
0x60006000556002600055 3012 0 1 0 2   3012
0x60026000556000600055 3012 4800 1 2 0   -1788
0x60026000556003600055 3012 0 1 2 3   3012
0x60026000556001600055 3012 2800 1 2 1   212
0x60026000556002600055 3012 0 1 2 2   3012
0x60016000556000600055 3012 4800 1 1 0   -1788
0x60016000556002600055 3012 0 1 1 2   3012
0x60016000556001600055 212 0 1 1 1   212
0x600160005560006000556001600055 40118 19900 0 1 0 1 20218
0x600060005560016000556000600055 5918 7600 1 0 1 0 -1682

安全注意事项

退款对交易执行不可见,因此这不应对交易执行逻辑产生任何影响。

如果我们在不计算后来重置回零的零到非零 SSTORE 的情况下,可以在区块中花费在执行上的最大 gas 量限制为 gas 限制。不计算它们是可以的,因为如果这样的 SSTORE 被重置,则不会扩展存储,并且客户端不需要实际调整梅克尔树;gas 消耗被退还,但客户端通常处理这些操作码所需的努力也被取消了。如果 new_value = original_value,客户端应确保不进行存储写入;自以太坊开始以来,这是一种谨慎的优化,但现在变得更加重要。

版权

Copyright and related rights waived via CC0.

Citation

Please cite this document as:

Vitalik Buterin (@vbuterin), Martin Swende (@holiman), "EIP-3529: 减少退款," Ethereum Improvement Proposals, no. 3529, April 2021. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-3529.