基于数据的EIP-7907分析

本文为机器翻译
展示原文

以下报告旨在收集数据,希望这些数据能够帮助 ACD 就 EIP-7907 做出决定。
此外,这有望建立一种新的方法,即用尽可能多的数据来支持 EIP 或提案,这肯定有助于在确定分支范围时做出更好、更明智的决策。

我要感谢@rjl493456442提交的 PR,他在 Geth 中添加了相关指标,并在基准测试数据收集过程中提供了极大的帮助和建议。我希望最终能将这些指标标准化,以便在所有客户端中通用,方便我们轻松比较和收集数据,从而为重新定价和扩展规模的决策提供依据。


相关问题:** EIP-7907
日期: 2026年1月13日
基准测试环境: Geth(开发模式),数据库规模与主网相同(约 2400 万个区块),内部缓存已禁用。
测试配置:每个区块约 18,106 次 EXTCODESIZE 操作(所有字节码合约均不同),约 50M gas
硬件: WD Black SN850X NVMe (8TB)


执行摘要

本报告分析了在禁用 Geth 内部代码缓存的情况下,读取不同字节码大小(0.5KB 至 64KB)合约时EXTCODESIZE操作码的性能。这代表了最坏情况下的攻击场景,攻击者部署数千个不同的合约来强制执行冷磁盘读取。

该迭代还具有最低的开销,从而避免了CREATE2确定性地址生成的问题。
更多相关信息请参见:

主要发现

寻找价值
代码读取时间范围107毫秒 - 904毫秒(约18000次代码读取)
每次通话延迟范围5.9微秒 - 49.9微秒
代码读取时间缩放增长8.5倍(0.5KB → 64KB)
64KB 数据块执行时间约1006毫秒
代码读取占块时间的百分比51% (0.5KB) → 90% (64KB)
Geth 效率对比原始 NVMe 24-51%

EIP-7907 裁决

尺寸块时间1秒预算的百分比判决
24KB(当前) 535毫秒54%安全的
32KB 685毫秒69%安全的
64KB 1006毫秒约100%在60M气体条件下可行
128KB+预计1.5秒以上100%以上可能需要重新调整天然气价格。在 BALs 和 ePBS 之后,我们需要更多数据。

建议:将新的最大合约大小设定为 64KB。超过 64KB 则需要在 BAL 和 ePBS 的优化功能部署到所有客户端后重新收集数据。
如果在上述数据收集之后需要重新定价,则此类定价还需要能够对其他客户进行基准测试,并参考其他EXTCODE*操作码。


1. 方法论与基准测试设置

1.1 测试环境

范围价值
Geth 版本v1.16.8-不稳定版(包含大量修改)
数据库主网已同步(约2400万个区块)
Geth 缓存已禁用(强制磁盘读取)
合同规模测试0.5、1、2、5、10、24、32、64 KB
EXTCODESIZE 操作每块约 18,106 个
每块气体约5000万
已部署合同每种规模的合同超过 18,100 份。
每个尺寸的迭代次数8
硬件WD Black SN850X NVMe 8TB

1.2 攻击场景设计

此基准测试代表了针对EXTCODESIZE最坏情况攻击

  • 每个规模部署超过 18,100 个独特的合约(导致代码缓存未命中)
  • 每个区块都会从所有唯一合约中读取字节码,且读取次数不得少于一次。
  • 代码缓存命中率:<2%(实际上已禁用)
  • 基准测试运行之间清除操作系统页面缓存

1.3 原始磁盘基线 (fio)

为了确定理论上的最大性能,我们测量了NVMe的原始性能:

块大小IOPS吞吐量平均延迟
512B 337K 172 MB/s 95 微秒
1KB 320K 328 MB/s 100 微秒
4KB 272K 1.1 GB/s 117 微秒
24KB 171K 4.2 GB/s 185 微秒
32KB 155K 5.1 GB/s 204 微秒
64KB 85K 5.6 GB/s 366 微秒

2. 基准测试结果

2.1 代码读取时间与字节码大小

7907_violin_code_read_time
7907_violin_code_read_time 1800×1050 91.5 KB
7907_code_read_vs_size
7907_code_read_vs_size 1500×900 55 KB

核心发现:当缓存无效时,代码读取时间与字节码大小成正比。

尺寸代码读取时间(毫秒)增长量(相对于 0.5KB)
0.5KB 107毫秒1.0倍(基线)
1KB 135毫秒1.3倍
2KB 142毫秒1.3倍
5KB 145毫秒1.4倍
10KB 161毫秒1.5倍
24KB 428毫秒4.0x
32KB 584毫秒5.5倍
64KB 904毫秒8.5倍

关键发现:字节码大小增长 128 倍时,代码读取时间增长 8.5 倍。这是亚线性增长(并非 1:1),但绝对时间影响非常显著。

2.2 字节读取时间与代码读取时间(相关性)

7907_code_read_vs_total_bytes
7907_code_read_vs_total_bytes 1500×900 76 KB

强正相关性(R² ≈ 0.96)证实,当缓存无效时,代码读取时间与读取的总字节数成正比。

2.3 每次呼叫延迟

7907_每次调用延迟
每次调用延迟 7907,分辨率 1500×900,59.1 KB

每次调用延迟随字节码大小的增加而增加:

尺寸每次呼叫延迟生长
0.5KB 5.9 微秒1.0x
1KB 7.5 微秒1.3倍
10KB 8.9 微秒1.5倍
24KB 23.7 微秒4.0x
32KB 32.3 微秒5.5倍
64KB 49.9 微秒8.5倍

3. 执行时间细分

3.1 成分分析

7907_time_breakdown_stacked
7907_time_breakdown_stacked 1800×1050 69.2 KB

字节码尺寸较大时,代码读取量成为主要因素

尺寸代码读取账户读取EVM执行数据库写入其他全部的
0.5KB 107毫秒(51%) 54毫秒34毫秒12毫秒2毫秒209毫秒
1KB 135毫秒(57%) 53毫秒37毫秒12毫秒1毫秒238毫秒
10KB 161毫秒(59%) 53毫秒40毫秒12毫秒5毫秒271毫秒
24KB 428毫秒(80%) 44毫秒46毫秒15毫秒2毫秒535毫秒
32KB 584毫秒(85%) 38毫秒47毫秒13毫秒3毫秒685毫秒
64KB 904毫秒(90%) 38毫秒51毫秒12毫秒1毫秒1006毫秒

观察:在 64KB 缓存大小下,代码读取消耗了 90% 的块执行时间。这与热缓存场景下代码读取仅占 8-10% 的情况截然不同。


4. 时间块预算分析(EIP-7907 重点)

4.1 时间与预算目标

7907_block_time_budget
7907_block_time_budget 1500×900 60.4 KB

使用 1 秒作为区块执行目标:

尺寸块时间1秒预算的百分比地位
0.5KB 209毫秒21%远低于预算
1KB 238毫秒24%远低于预算
2KB 248毫秒25%远低于预算
5KB 252毫秒25%远低于预算
10KB 271毫秒27%远低于预算
24KB 535毫秒54%预算内
32KB 685毫秒69%预算内
64KB 1006毫秒约100%极限

结论:在6000万gas区块的最坏攻击条件下,64KB合约是可行的。约1秒的执行时间虽然接近预算极限,但尚可接受。需要注意的是,考虑到ePBS和BAL很可能在不久的将来改变我们对安全预算的定义,这个极限相当保守。

4.2 天然气处理率(定价错误分析)

7907_mgas_vs_size
7907_mgas_vs_size 1500×900 64.3 KB
尺寸燃气使用块时间兆气体/秒
0.5KB 4940万209毫秒236
1KB 4940万238毫秒208
10KB 4940万271毫秒182
24KB 4940万535毫秒92
32KB 4940万685毫秒72
64KB 4940万1006毫秒49

发现定价错误: gas 成本相同,但执行时间却相差 5 倍(236 Mgas/s → 49 Mgas/s)。这表明,在最坏情况下,规模更大的合约会给验证者带来不成比例的更高成本。

对 128KB 以上文件的影响:超过 64KB 后,需要对 gas 模型进行调整——可能是一个基本成本加上一个与大小相关的组件。

请注意,这还是相当保守的估计。因为要“瘫痪”网络或“严重影响慢速验证者”,所需的设置数量将是 18000 个独立合约的数百倍。这将带来巨大的成本(我们无法重复使用这些合约,因为它们会在第一个区块执行后被缓存)。


5. 原始磁盘基准(Geth 与 NVMe 效率对比)

5.1 效率比较

7907_geth_vs_nvme
7907_geth_vs_nvme 2100×900 64.7 KB
尺寸Geth IOPS原始 NVMe IOPS效率Geth 吞吐量原始 NVMe效率
0.5KB 171K 337K 51% 83 MB/s 172 MB/s 48%
1KB 142K 320K 44% 139 MB/s 328 MB/s 42%
24KB 43K 171K 25% 1.0 GB/s 4.2 GB/s 24%
32KB 31K 155K 20% 979 MB/s 5.1 GB/s 19%
64KB 2万85K 24% 1.26 GB/s 5.6 GB/s 23%

观察结果: Geth 仅能达到磁盘原始性能的 20% 至 51%。造成这种差距的原因可能是:

  • Pebble/LevelDB 开销(索引遍历、布隆过滤器)
  • 密钥哈希和查找
  • 值反序列化

6. 与热缓存场景的比较

6.1 缓存与非缓存性能比较

7907_cached_vs_uncached
7907_cached_vs_uncached 1800×900 64 KB
尺寸暖缓存冷缓存减速
0.5KB 5.3毫秒107毫秒21倍
1KB 4.4毫秒135毫秒31倍
2KB 4.5毫秒142毫秒32倍
5KB 4.6毫秒145毫秒31倍
10KB 4.7毫秒161毫秒34倍
24KB 4.8毫秒428毫秒89倍
32KB 4.9毫秒584毫秒119x
64KB 4.9毫秒904毫秒181倍

在正常操作情况下,基于热缓存基准测试得出的“固定成本”结论仍然有效。冷缓存条件则需要极端攻击场景(超过 18,000 个独立合约)。


7. 对 EIP-7907 的影响及建议

7.1 研究结果总结

  1. 在攻击条件下,代码读取时间随代码大小而增加(从 0.5KB 到 64KB 增加到 8.5 倍)。
  2. 64KB 的数据在 6000 万个 gas 块的情况下是可行的——最坏情况下执行时间约为 1 秒,在预算范围内。
  3. 这代表了最糟糕的情况——部署和维护 18000 多个独特的合约是不切实际的(每次要运行攻击的区块都需要一个新的合约集)。
  4. 正常运行不受影响——热缓存场景下开销约为 5 毫秒。
  5. Gas定价错误问题正受到攻击(相同gas的执行时间差异可达5倍)

7.2 EIP-7907 建议

行动推荐
64KB限制继续执行——在最坏情况下的攻击下仍然可行。无需 EIP。
128KB+限制需要使用支气管肺泡灌洗液 (BAL) 和电子磷酸盐缓冲液 (ePBS) 进行重新测量。

看来我们可以“保持简单”,在不改变协议本身的情况下,为智能合约开发者提供代码大小限制方面的良好升级,除了 64kB 的限制和 initcode 大小的增加之外。

一旦 BAL 和 ePBS 达到更完善的状态,我们将能够利用数据更好地指导我们做出关于重新定价/直接进入 256kB 的良好决策。
但即使在最糟糕的情况下,这东西也并不需要重新定价,所以现在重新定价似乎没有必要。

7.3 为什么 64KB 是可以接受的

  1. 攻击不切实际:部署超过 18000 个唯一的 64KB 合约需要:

    • 每个合约部署约需 1300 万 gas(3.2 万基础 gas + 6.4 万 gas × 200 gas/字节)
    • 仅设置阶段就需要数百个模块。
    • 维护攻击面需要持续投入大量成本
  2. 阻塞时间在预算范围内:即使最坏情况下约 1 秒,对于 6000 万个 gas 块来说也是可以接受的。

  3. 缓存实际有效性:主主网区块会复用合约;代码缓存命中率通常很高。

  4. 亚线性扩展:规模增长 128 倍所需时间仅为 8.5 倍,这表明摊销仍然有效。


来源
免责声明:以上内容仅为作者观点,不代表Followin的任何立场,不构成与Followin相关的任何投资建议。
喜欢
收藏
评论