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 |
Table of Contents
摘要
我们通过引入 initcode
的最大大小限制(MAX_INITCODE_SIZE = 2 * MAX_CODE_SIZE = 49152
)来扩展 EIP-170。
此外,我们为每个 32 字节的 initcode
块引入 2
gas 的费用,以表示 jumpdest 分析的成本。
最后,大小限制产生了一个很好的特性:EVM 代码大小、代码偏移量 (PC
) 和跳转偏移量都适合 16 位值。
动机
在合约创建期间,客户端必须在执行之前对 initcode
执行 jumpdest 分析。执行的工作量与 initcode
的大小成线性比例。目前,这项工作没有被计量,也没有协议强制执行的大小上限。
目前有三种收费方式:
- calldata(又名
initcode
)的成本:对于值为零的字节,成本为 4 gas,否则为 16 gas。 - 生成的已部署代码的成本:每个字节 200 gas。
- 仅在
CREATE2
的情况下,地址计算(代码哈希)的成本:每个字 6 gas。
只有第一种成本适用于 initcode
,但仅适用于合约创建交易。对于 CREATE
/CREATE2
,没有这样的成本,并且可以以相对便宜的方式以编程方式生成 initcode
的变体。过去,由于 2017 年 geth 1.6.5 修复的一个漏洞,有可能制作恶意的 initcode
。
此外,缺乏限制导致了一些 EVM 提案的冗长讨论,影响了设计,甚至导致功能的延迟或取消。
我们的动机有三个原因:
- 确保
initcode
被公平收费(最重要的是成本与initcode
的长度成正比),以最大限度地降低未来的风险。 - 拥有一个未来可扩展的成本系统。
- 通过显式限制(代码大小、代码偏移量 (
PC
) 和跳转偏移量适合 16 位)来简化 EVM 引擎。
规范
参数
常量 | 值 |
---|---|
INITCODE_WORD_COST |
2 |
MAX_INITCODE_SIZE |
2 * MAX_CODE_SIZE |
其中 MAX_CODE_SIZE
由 EIP-170 定义为 24576
。
我们将 initcode_cost(initcode)
定义为等于 INITCODE_WORD_COST * ceil(len(initcode) / 32)
。
规则
- 如果创建交易中的交易数据(
initcode
)长度超过MAX_INITCODE_SIZE
,则交易无效。(请注意,这类似于因未满足内在 gas 成本要求而被视为无效的交易。) - 对于创建交易,扩展交易数据成本公式以包括
initcode_cost(initcode)
。(请注意,这包含在交易内在成本中,即 gas 不足以支付 initcode 成本的交易无效。) - 如果
CREATE
或CREATE2
指令的initcode
长度超过MAX_INITCODE_SIZE
,则指令执行会异常中止(就像 gas 耗尽一样)。 - 对于
CREATE
和CREATE2
指令,收取额外的 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-1014 的 CREATE2
的 hashcost 的计算兼容。因此,相同的实现可以用于具有不同成本常数的 CREATE
和 CREATE2
:激活前 CREATE
为 0
,CREATE2
为 6
,激活后 CREATE
为 2
,CREATE2
为 6 + 2
。
initcode 的大小限制的原因
通过设定上限,估算和创建最坏情况的场景更容易,因为搜索的一个参数大大减少了。这允许选择更乐观的每字节 gas。
如果没有上限,则成本需要更高,以应对未知的未知数。鉴于大多数 initcode(TODO:在此处说明在主网上看到的导致部署的最大 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.