Alert Source Discuss
Standards Track: Core

EIP-3860: 限制和计量 initcode

将 initcode 的最大大小限制为 49152,并为每 32 字节的 initcode 块应用 2 个额外的 gas 成本

Authors Martin Holst Swende (@holiman), Paweł Bylica (@chfast), Alex Beregszaszi (@axic), Andrei Maiboroda (@gumb0)
Created 2021-07-16
Requires EIP-170

摘要

我们通过引入 initcode 的最大大小限制(MAX_INITCODE_SIZE = 2 * MAX_CODE_SIZE = 49152)来扩展 EIP-170

此外,我们为每个 32 字节的 initcode 块引入 2 gas 的费用,以表示 jumpdest 分析的成本。

最后,大小限制产生了一个很好的特性:EVM 代码大小、代码偏移量 (PC) 和跳转偏移量都适合 16 位值。

动机

在合约创建期间,客户端必须在执行之前对 initcode 执行 jumpdest 分析。执行的工作量与 initcode 的大小成线性比例。目前,这项工作没有被计量,也没有协议强制执行的大小上限。

目前有三种收费方式:

  1. calldata(又名 initcode)的成本:对于值为零的字节,成本为 4 gas,否则为 16 gas。
  2. 生成的已部署代码的成本:每个字节 200 gas。
  3. 仅在 CREATE2 的情况下,地址计算(代码哈希)的成本:每个字 6 gas。

只有第一种成本适用于 initcode,但仅适用于合约创建交易。对于 CREATE/CREATE2,没有这样的成本,并且可以以相对便宜的方式以编程方式生成 initcode 的变体。过去,由于 2017 年 geth 1.6.5 修复的一个漏洞,有可能制作恶意的 initcode

此外,缺乏限制导致了一些 EVM 提案的冗长讨论,影响了设计,甚至导致功能的延迟或取消。

我们的动机有三个原因:

  1. 确保 initcode 被公平收费(最重要的是成本与 initcode 的长度成正比),以最大限度地降低未来的风险。
  2. 拥有一个未来可扩展的成本系统。
  3. 通过显式限制(代码大小、代码偏移量 (PC) 和跳转偏移量适合 16 位)来简化 EVM 引擎。

规范

参数

常量
INITCODE_WORD_COST 2
MAX_INITCODE_SIZE 2 * MAX_CODE_SIZE

其中 MAX_CODE_SIZEEIP-170 定义为 24576

我们将 initcode_cost(initcode) 定义为等于 INITCODE_WORD_COST * ceil(len(initcode) / 32)

规则

  1. 如果创建交易中的交易数据(initcode)长度超过 MAX_INITCODE_SIZE,则交易无效。(请注意,这类似于因未满足内在 gas 成本要求而被视为无效的交易。
  2. 对于创建交易,扩展交易数据成本公式以包括 initcode_cost(initcode)。(请注意,这包含在交易内在成本中,即 gas 不足以支付 initcode 成本的交易无效。
  3. 如果 CREATECREATE2 指令的 initcode 长度超过 MAX_INITCODE_SIZE,则指令执行会异常中止(就像 gas 耗尽一样)。
  4. 对于 CREATECREATE2 指令,收取额外的 gas 成本,等于 initcode_cost(initcode)。此成本在计算生成的合约地址和执行 initcode 之前扣除。(请注意,这意味着在 CREATE2 中应用哈希成本之前或同时应用。

理由

Gas 成本常量

INITCODE_WORD_COST 的值是基于每个实现的不同的最坏情况的性能基准选择的。基准线的基准是 geth 1.10.9 中 KECCAK256 哈希的性能,它与 4.0 GHz x86_64 CPU 上的 70 Mgas/s gas 限制目标匹配。

EVM 版本 MB/s B/CPUcycle CPUcycle/B 1 B 的成本 32 B 的成本
geth/KECCAK256 1.10.9 357 1.8 0.6 0.2 6.0
geth 1.10.9 1091 5.5 0.2 0.1 2.0
evmone/Baseline 0.8.2 727 3.7 0.3 0.1 2.9
evmone/Advanced 0.8.2 155 0.8 1.3 0.4 13.8

每个字(32 字节块)的 Gas 成本

我们基于 Geth 的实现并与 KECCAK256 性能进行比较,选择了每个字 2 gas 的成本。这意味着每个字节的成本为 0.0625。虽然 EVM 中不允许使用小数 gas 成本,但我们可以通过按字收费来近似它。

此外,计算每个字的 gas 与 EIP-1014CREATE2hashcost 的计算兼容。因此,相同的实现可以用于具有不同成本常数的 CREATECREATE2:激活前 CREATE0CREATE26,激活后 CREATE2CREATE26 + 2

initcode 的大小限制的原因

通过设定上限,估算和创建最坏情况的场景更容易,因为搜索的一个参数大大减少了。这允许选择更乐观的每字节 gas。

如果没有上限,则成本需要更高,以应对未知的未知数。鉴于大多数 initcodeTODO:在此处说明在主网上看到的导致部署的最大 initcode 大小)不超过建议的限制,因此通过过于保守的成本来惩罚合约似乎没有必要。

initcode 大小限制的效果

在大多数情况下(如果不是全部情况),当创建一个新合约时,生成的运行时代码是从 initcode 本身复制的。对于基本情况,2 * MAX_CODE_SIZE 限制允许 MAX_CODE_SIZE 用于运行时代码,另一个 MAX_CODE_SIZE 用于合约构造函数代码。但是,该限制可能对在单个创建交易中部署多个合约的情况具有实际意义。

创建交易的 Initcode 成本

与交易数据成本(每个字节 4 或 16 gas)相比,创建交易数据的 initcode 成本(每个字节 0.0625 gas)可以忽略不计。尽管如此,我们还是决定将其包含在规范中以保持一致性,更重要的是为了向前兼容。

如何报告 initcode 限制违规?

我们指定 CREATE/CREATE2 的 initcode 大小限制违规会导致执行异常中止。这将其置于早期 gas 不足检查组中,包括:堆栈下溢、内存扩展、静态调用违规、initcode 哈希成本以及此 EIP 引入的 initcode 成本。它们先于后来的“轻量级”检查:调用深度和余额。该选择使检查顺序保持一致并降低了实现复杂性(gas 不足检查可以按任何顺序执行)。

向后兼容性

此 EIP 需要“网络升级”,因为它修改了共识规则。

已经部署的合约不应受到影响,但是某些交易(initcode 超过建议的限制)仍然可以包含在区块中,但会导致异常中止。

测试用例

测试应包括以下情况:

  • 创建交易,其 gas 限制足以支付 initcode 成本
  • 创建交易,其 gas 限制足以支付除 initcode 成本之外的内在成本
  • CREATE/CREATE2/创建交易,其中 len(initcode)MAX_INITCODE_SIZE
  • CREATE/CREATE2/创建交易,其中 len(initcode)MAX_INITCODE_SIZE+1

安全注意事项

对于客户端实现,此 EIP 使基于 jumpdest 分析的攻击的问题减少,因此应提高客户端的稳健性。

对于第 2 层,此 EIP 引入了以前没有的故障模式。可能存在工厂合约,它们部署多级合约层次结构,以便多个合约的代码包含在第一个合约的 initcode 中。此 EIP 的作者不知道任何此类合约。

目前,在伦敦,gas 限制为 30M,有可能触发总共 ~1.3GB initcode 的 jumpdest 分析。使用此 EIP,此类攻击的成本将大约增加 80M gas。

版权

版权及相关权利通过 CC0 放弃。

Citation

Please cite this document as:

Martin Holst Swende (@holiman), Paweł Bylica (@chfast), Alex Beregszaszi (@axic), Andrei Maiboroda (@gumb0), "EIP-3860: 限制和计量 initcode," Ethereum Improvement Proposals, no. 3860, July 2021. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-3860.