Skip to content

Simulation methods: fields whose EIP is not active at the target block #884

Description

@MysticRyuujin

What happened

geth #35588 added checks to eth_call and friends that reject an accessList before Berlin, blobVersionedHashes before Cancun and an authorizationList before Prague. Erigon ported it in erigontech/erigon#23700 and their mainnet test suite broke on 7 pre-Berlin eth_createAccessList fixtures. That raised the question of what the spec expects. Right now it says nothing.

What clients do today

Measured with hive rpc-compat on the shared test chain at block 20 (pre-Berlin, pre-Cancun, pre-Prague). ethrex has no state before London on that chain, so its numbers are from block 0. Images: ethpandaops/* master/main as of 2026-09-08, plus the Erigon PR branch built from source.

eth_call with a caller-supplied field:

Client accessList pre-Berlin blobVersionedHashes pre-Cancun authorizationList pre-Prague
geth master rejects, -32000 rejects, -32000 rejects, -32000
reth rejects, -32003 rejects, -32003 rejects, -32003
Besu rejects, -32602 rejects, -32602 rejects, -32602
Nethermind rejects, -32603 "Internal error" rejects, -32603 "Internal error" rejects, -32603 "Internal error"
Erigon main accepts accepts rejects, -32000
Erigon #23700 rejects, -32000 rejects, -32000 rejects, -32000
ethrex accepts accepts unless maxFeePerBlobGas is set, then -32015 rejects, -32015

An empty accessList: [] behaves the same as a populated one everywhere.

eth_createAccessList pre-Berlin with no accessList in the request:

Client plain transfer contract call that touches storage
geth master fails, -32000 fails, -32000
reth fails, -32003 fails, -32003
Besu {"accessList":[],"gasUsed":"0x5208"} success body with accessList: [], error: "execution reverted", gasUsed: 0x2faf081 (gas cap + 1)
Nethermind {"accessList":[],"gasUsed":"0x5208"} {"accessList":[],"gasUsed":"0xbcf4"}
Erigon main and #23700 {"accessList":[],"gasUsed":"0x5208"} one entry, two storage keys, gasUsed: 0xd52c
ethrex {"accessList":[],"gasUsed":"0x5208"} one entry, two storage keys, gasUsed: 0xbf4c

Post-Berlin controls at latest pass on every client with identical results, so the divergence is the fork boundary, not the fixtures.

Proposal

One rule, no special cases. A simulation request that carries a field whose EIP is not active at the target block is invalid and the client rejects it. This applies to eth_call, eth_estimateGas, eth_createAccessList, eth_simulateV1 and debug_traceCall, and to accessList, blobVersionedHashes and authorizationList. It also applies to eth_createAccessList itself on a block where EIP-2930 is not active, since the method has nothing meaningful to return there.

This matches what four of six clients already do for eth_call, and what Erigon does after #23700. Changes needed: ethrex adds the gate, Nethermind turns its internal error into a normal rejection, Besu stops reporting the eth_createAccessList failure as a success, Erigon drops the exception in #23700 and updates its fixtures.

The alternative is to require eth_createAccessList to succeed regardless of fork. That needs fixes in geth, reth and Besu and still leaves three clients disagreeing on the result, so I am not proposing it.

Error codes are out of scope here. They differ across clients today and belong in #784.

Testing

The shared chain already covers this: Berlin activates at block 24, Cancun at 42, Prague at 45. Once the wording lands I will add fixtures targeting block 20 for the three fields and for eth_createAccessList.

Related: #853 and #854 cover a different eth_createAccessList problem, fee defaults, and stay separate.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions