ethereum / ethereum/execution-apis

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

Open
#884 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
Io
Stars
1.1k
Forks
530
Avg merge
5d 8h
Merged PRs (30d)
9

Description

## 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.

Contributor guide

Open the contributing guide

Research direction

Start with the proposal and the listed simulation entry points: eth_call, eth_estimateGas, eth_createAccessList, eth_simulateV1, and debug_traceCall. Review the fork activation blocks and existing shared-chain coverage, then define the specification wording and add block-20 fixtures for the three fields and eth_createAccessList; done means the rule and tests cover the stated cases without changing error-code scope.

Written by the indexing model from the issue text.

Assessment

Domain
api, backend-api-design
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.