ethereum / ethereum/execution-apis
Simulation methods: fields whose EIP is not active at the target block
- 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
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