Data-driven analysis on EIP-7907

The following report, aims to be a collection of data that will hopefully be a resource that will help ACD to take a decision over EIP-7907.
Also, this hopefully sets a new methodology of backing up EIPs or proposals with as much data as possible, which can definitely help taking better and more informed decisions when scoping forks.

I want to thank @rjl493456442 for his PR adding metrics in Geth and his advice and support during the benchmark collection which has been extremely helpful. And which I’d like to standarize eventually across all clients such that we can compare data easily and collect data easily to inform our decisions on repricings and scaling.


Related Issue:** EIP-7907
Date: 2026-01-13
Benchmark Environment: Geth (dev mode) with mainnet-sized database (~24M blocks), internal caches disabled
Test Configuration: ~18,106 EXTCODESIZE operations per block (all different bytecode contracts), ~50M gas
Hardware: WD Black SN850X NVMe (8TB)


Executive Summary

This report analyzes the performance of the EXTCODESIZE opcode when reading contracts of varying bytecode sizes (0.5KB to 64KB) with Geth’s internal code cache disabled. This represents the worst-case attack scenario where an attacker deploys thousands of unique contracts to force cold disk reads.

The iteration also has the lowest overhead possible leaveraging CREATE2 deterministic address generation.
More information regarding this can be found in:

Key Findings

FindingValue
Code read time range107ms - 904ms (for ~18K code reads)
Per-call latency range5.9µs - 49.9µs
Code read time scaling8.5x growth (0.5KB → 64KB)
64KB block execution time~1006ms
Code read % of block time51% (0.5KB) → 90% (64KB)
Geth efficiency vs raw NVMe24-51%

EIP-7907 Verdict

SizeBlock Time% of 1s BudgetVerdict
24KB (current)535ms54%Safe
32KB685ms69%Safe
64KB1006ms~100%Viable at 60M gas
128KB+Projected 1.5s+>100%Might need gas repricing, We need more data after BALs + ePBS

Recommendation: Proceed with 64KB as the new maximum contract size. Beyond 64KB would require new data collection once BALs and ePBS’s optimizations are landed in all clients.
If a repricing was required after the data collection mentioned above, such pricing would also require being able to benchmark the rest of the clients as well as looking to the rest of EXTCODE* opcodes.


1. Methodology & Benchmark Setup

1.1 Test Environment

ParameterValue
Geth versionv1.16.8-unstable (with lots of hacks)
DatabaseMainnet synced (~24M blocks)
Geth cacheDisabled (forces disk reads)
Contract sizes tested0.5, 1, 2, 5, 10, 24, 32, 64 KB
EXTCODESIZE operations~18,106 per block
Gas per block~50M
Deployed contracts18,100+ unique contracts per size
Iterations per size8
HardwareWD Black SN850X NVMe 8TB

1.2 Attack Scenario Design

This benchmark represents the worst-case attack against EXTCODESIZE:

  • 18,100+ unique contracts deployed per size (forces code cache misses)
  • Each block reads bytecode from all unique contracts exactly once
  • Code cache hit rate: <2% (effectively disabled)
  • OS page cache cleared between benchmark runs

1.3 Raw Disk Baseline (fio)

To establish theoretical maximum performance, we measured raw NVMe capabilities:

Block SizeIOPSThroughputAvg Latency
512B337K172 MB/s95 µs
1KB320K328 MB/s100 µs
4KB272K1.1 GB/s117 µs
24KB171K4.2 GB/s185 µs
32KB155K5.1 GB/s204 µs
64KB85K5.6 GB/s366 µs

2. Benchmark Results

2.1 Code Read Time vs Bytecode Size

Core Finding: Code read time scales with bytecode size when cache is ineffective.

SizeCode Read (ms)Growth vs 0.5KB
0.5KB107ms1.0x (baseline)
1KB135ms1.3x
2KB142ms1.3x
5KB145ms1.4x
10KB161ms1.5x
24KB428ms4.0x
32KB584ms5.5x
64KB904ms8.5x

Key insight: Code read time grows 8.5x as bytecode size grows 128x. This is sub-linear scaling (not 1:1), but the absolute time impact is significant.

2.2 Bytes Read vs Code Read Time (Correlation)

The strong positive correlation (R² ≈ 0.96) confirms that code read time scales with total bytes read when caches are ineffective.

2.3 Per-Call Latency

Per-call latency grows with bytecode size:

SizePer-Call LatencyGrowth
0.5KB5.9 µs1.0x
1KB7.5 µs1.3x
10KB8.9 µs1.5x
24KB23.7 µs4.0x
32KB32.3 µs5.5x
64KB49.9 µs8.5x

3. Execution Time Breakdown

3.1 Component Analysis

Code read becomes the dominant factor at larger bytecode sizes:

SizeCode ReadAccount ReadEVM ExecDB WriteOtherTotal
0.5KB107ms (51%)54ms34ms12ms2ms209ms
1KB135ms (57%)53ms37ms12ms1ms238ms
10KB161ms (59%)53ms40ms12ms5ms271ms
24KB428ms (80%)44ms46ms15ms2ms535ms
32KB584ms (85%)38ms47ms13ms3ms685ms
64KB904ms (90%)38ms51ms12ms1ms1006ms

Observation: At 64KB, code read consumes 90% of block execution time. This is dramatically different from warm-cache scenarios where code read is only 8-10%.


4. Block Time Budget Analysis (EIP-7907 Focus)

4.1 Time vs Budget Target

Using a 1-second target for block execution:

SizeBlock Time% of 1s BudgetStatus
0.5KB209ms21%Well under budget
1KB238ms24%Well under budget
2KB248ms25%Well under budget
5KB252ms25%Well under budget
10KB271ms27%Well under budget
24KB535ms54%Under budget
32KB685ms69%Under budget
64KB1006ms~100%At limit

Conclusion: 64KB contracts are viable under worst-case attack conditions at 60M gas blocks. The ~1-second execution time is at the budget limit but acceptable. Note that this is a quite conservative limit considering ePBS & BALs will likely reshape what we consider a safe budget in the near future.

4.2 Gas Processing Rate (Mispricing Analysis)

SizeGas UsedBlock TimeMgas/s
0.5KB49.4M209ms236
1KB49.4M238ms208
10KB49.4M271ms182
24KB49.4M535ms92
32KB49.4M685ms72
64KB49.4M1006ms49

Mispricing observed: Same gas cost, but 5x different execution time (236 Mgas/s → 49 Mgas/s). This indicates that under worst-case conditions, larger contracts impose disproportionately higher cost on validators.

Implication for 128KB+: Beyond 64KB, a gas model adjustment would be needed—likely a base cost plus size-dependent component.

Notice this is quite conservative. As in order to “halt” the network or “significantly hurt slow validators”, the setup required would be of hundreds of times the 18k unique contracts. Which incurrs on a massive costs (we can’t reuse them as they would be cached after the first block execution).


5. Raw Disk Baseline (Geth vs NVMe Efficiency)

5.1 Efficiency Comparison

SizeGeth IOPSRaw NVMe IOPSEfficiencyGeth ThroughputRaw NVMeEfficiency
0.5KB171K337K51%83 MB/s172 MB/s48%
1KB142K320K44%139 MB/s328 MB/s42%
24KB43K171K25%1.0 GB/s4.2 GB/s24%
32KB31K155K20%979 MB/s5.1 GB/s19%
64KB20K85K24%1.26 GB/s5.6 GB/s23%

Observation: Geth achieves 20-51% of raw disk performance. The gap is likely due to:

  • Pebble/LevelDB overhead (index traversal, bloom filters)
  • Key hashing and lookup
  • Value deserialization

6. Comparison with Warm Cache Scenario

6.1 Cached vs Uncached Performance

SizeWarm CacheCold CacheSlowdown
0.5KB5.3ms107ms21x
1KB4.4ms135ms31x
2KB4.5ms142ms32x
5KB4.6ms145ms31x
10KB4.7ms161ms34x
24KB4.8ms428ms89x
32KB4.9ms584ms119x
64KB4.9ms904ms181x

The “flat cost” finding from warm-cache benchmarks remains valid for normal operation. Cold cache conditions require extreme attack scenarios (18K+ unique contracts).


7. Implications for EIP-7907 & Recommendations

7.1 Summary of Findings

  1. Code read time scales with size under attack conditions (8.5x from 0.5KB to 64KB)
  2. 64KB is viable at 60M gas blocks—worst-case ~1s execution, within budget
  3. This represents the absolute worst case—18K+ unique contracts is impractical to deploy and maintain (you need a new set for each block you want to run the attack).
  4. Normal operation is unaffected—warm cache scenarios show flat ~5ms cost
  5. Gas mispricing exists under attack (5x execution time variation for same gas)

7.2 EIP-7907 Recommendation

ActionRecommendation
64KB limitProceed - viable under worst-case attack. No need for the EIP
128KB+ limitRequires re-measuring with BALs + ePBS

It seems we can just “keep it simple” and provide Smart Contract developers a nice upgrade on the codesize limit without any changes to the protocol besides the 64kB limit and the initcode size increase.

Once we have BALs and ePBS on a more ready state, we will be in a position where data will guide us better towards a good decision on repricing/just proceed to 256kB.
But it feels unnecessary to do a repricing now for something that doesn’t really need it even in it’s worst case.

7.3 Why 64KB is Acceptable

  1. Attack impracticality: Deploying 18K+ unique 64KB contracts requires:

    • ~13M gas per contract deployment (32K base + 64K × 200 gas/byte)
    • Hundreds of blocks just for setup
    • Significant ongoing cost to maintain attack surface
  2. Block time within budget: Even worst-case ~1s is acceptable for 60M gas blocks

  3. Cache effectiveness in practice: Real mainnet blocks reuse contracts; code cache hit rate is typically high

  4. Sub-linear scaling: 8.5x time for 128x size growth indicates amortization still helps


Source
Disclaimer: The content above is only the author's opinion which does not represent any position of Followin, and is not intended as, and shall not be understood or construed as, investment advice from Followin.
Like
Add to Favorites
Comments