Glamsterdam devnet-8 — RPC interoperability after a seven-client re-test

Prepared for ACDT · 2026-08-24 10:14 UTC · geth, Besu, Nethermind, reth, Erigon, Ethrex and Nimbus-EL · live post-Amsterdam/Gloas network · identical requests sent to one Teku-paired node per execution client.

The EVM consensus surface remains clean; the RPC surface does not. The original devnet-7 differential found no execution-result divergence after 22 rounds, but catalogued 28 RPC, debug and method-support differences. Re-running every finding on devnet-8 shows that only three are fixed on their originally affected client, three are partially improved, and 21 still reproduce.

This report distinguishes unambiguous client defects, missing optional coverage, implementation conventions, and questions that need a cross-client rule. Missing methods and different debug output are not presented as consensus bugs.

7
execution clients
tested live
21
numbered findings
still reproduce
3
partially
improved
3
fixed on the
original client
0
consensus divergences
in this RPC suite

Network control: at publication the network was synced and finalizing normally, at current epoch 2458 and finalized epoch 2456. The findings below are live RPC behavior, not symptoms of a stalled or partitioned chain.

1 · The ACDT asks

AreaObserved splitSuggested outcome
eth_simulateV1.traceTransfers × EIP-7708 Three clients return both the legacy synthetic 0xeeee…eeee log and the protocol 0xffff…fffe log; Besu substitutes the synthetic log; Nethermind has a third behavior; two clients do not implement the method. Specify whether traceTransfers=true adds to, replaces, or suppresses the EIP-7708 protocol log. Add one shared fixture before every client independently freezes a different rule.
eth_estimateGas after EIP-2780/8037 Up to three answers for simple transfers and five for a storage/refund probe. All observed answers are safe over-estimates, but wallets receive client-dependent gas limits. Decide whether the API promises the executable minimum or only a safe bound. In either case, publish cross-client fixtures for the repriced paths.
Debug tracer normalization Default tracer, tracer name, depth convention and per-opcode state-gas attribution differ. The same SSTORE step differs by 97,920 gas while total execution agrees. Continue standardization in execution-apis; require total-gas fields to distinguish regular and state gas, while explicitly allowing or forbidding per-step attribution choices.
Filter and execution-error semantics First historical eth_getFilterChanges poll is empty on four clients and full on reth; memory OOG maps to three error codes, including code 3 (revert) on Ethrex. Define first-poll behavior for closed historical ranges and a portable error category for OOG versus REVERT, even if exact messages remain implementation-defined.

2 · Clear client defects still present

IDClientLive devnet-8 resultImpact / status
D4 analogueNimbus-EL A value transfer to a fresh account returns success from eth_call at gas limits 21,000, 50,000 and 204,599. The other six return OOG; all succeed at 204,600. Simulation masks OOG and can cause tooling to underfund a transaction. The original Nethermind bug is fixed; the behavior moved to the seventh client newly included in this run.
D8Besu, Ethrex eth_createAccessList returns no entry for account-only BALANCE and EXTCODESIZE accesses, for both existing and non-existent accounts. Storage accesses work. The returned list does not pre-warm the accessed account. Besu: issue #11134.
D12Ethrex Receipt/log timestamps are now fixed, but blockTimestamp is still absent from every transaction returned by eth_getTransactionByHash and full-block queries. Consumers need a block lookup fallback for all Ethrex transaction objects.
D18Besu eth_feeHistory accepts reward percentiles 150 and -5 even though execution-apis constrains values to 0–100. Invalid input succeeds and cross-client validation diverges. Evidence added to issue #11055.
D22Erigon eth_getProof succeeds at head and fails at head−1, −2, −4 and −16 with root hash mismatch in account proof trie. Proofs at finalized blocks are unusable. The failure is safe—it errors rather than returning an invalid proof—but it is not a pruning/window response.
D23Erigon For the same closed two-block range, eth_getLogs returned 474 logs while a filter created with the same object returned zero from eth_getFilterLogs. Silent data loss for consumers using filter handles.
D24Ethrex eth_getLogs and eth_newFilter reject a filter without topics as missing a required parameter. The other six accept the field's omission. Rejects a valid filter object; breaks generic log tooling.

Also unambiguously stale, but lower impact: Erigon still rejects short storage keys such as 0x0 in eth_getProof (D11); Erigon returns a degenerate null-ratio response for eth_feeHistory block counts ≥263 (D19); and Ethrex still requires the optional filter field above. These are RPC input/edge handling, not execution defects.

3 · Repricing makes eth_estimateGas observably client-dependent

All seven clients agree on real block execution. They do not agree on the gas limit a caller should submit for the same transaction. Values below are decimal and were measured with identical live requests on devnet-8.

Call shapeSpec-derived executable minimumgethBesuNethermindrethErigonEthrexNimbus
self-send, value=112,00021,00012,00012,00021,00012,00012,00012,000
value=0 → other existing account15,00021,00015,15915,00021,00015,00015,00015,000
value=1 → existing account21,00021,00021,16521,00021,00021,00021,00021,000
value=1 → fresh account204,600207,391207,391207,391207,391204,600204,600204,600

The geth/reth 21,000 floor predates the EIP-2780 cheap paths. Besu adds a small margin on existing-account access. Five clients add 2,791 on a fresh-account transfer. These are safe over-estimates in the tested cases, so this is an interoperability and wallet-pricing issue—not a consensus claim.

4 · eth_simulateV1.traceTransfers has four outcomes

A single plain value transfer now causes a protocol-native EIP-7708 log from 0xfffffffffffffffffffffffffffffffffffffffe. The older simulation option synthesizes a second ERC-20-shaped log from 0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee.

ClienttraceTransfers=falsetraceTransfers=trueInterpretation
gethffffeeee + ffffdouble-counts
rethffffeeee + ffffdouble-counts
Erigonffffeeee + ffffdouble-counts
Besuffffeeeesubstitutes; drops protocol log
Nethermind−32603 Internal errorffffdifferent broken combination
Ethrex−32601 Method not foundnot implemented
Nimbus-EL−32601 not registerednot implemented

This is the most useful ACDT discussion item. No implementation can infer whether the legacy synthetic log should coexist with a protocol log that did not exist when traceTransfers was designed. A shared rule is cheaper now than seven client-specific compatibility branches later.

5 · Method and shape coverage

A dash means missing or unusable, not “consensus incorrect.” Debug and txpool methods are valuable for devnet differential testing, but several are not standardized Ethereum JSON-RPC.

SurfacegethBesuNethermindrethErigonEthrexNimbus
debug_traceCall default struct traceyesyesyesyesyescall frame
eth_simulateV1yesyespartialyesyes
eth_call state overridesyesyesyesyesyes
txpool_statusyesyesyesyesyes
raw transaction by hash / block indexyesyesyesyes
eth_newFilteryesyesyesyesyestopics required
eth_getFilterLogsyesyesyesyesreturns 0
trace_block / trace_call—*yesyesyesyes
debug_storageRangeAtnextKeycompletenullnextKey
debug_accountRangeobjectaddressMapnullraw-address cursor
eth_getUncleCountByBlockNumberyesyesyesyesyesyes

* geth has never implemented Parity-style trace_*; this is expected, not a regression. Besu raw-transaction support is already tracked in issue #10477.

6 · Complete D1–D28 status ledger

IDStatusDevnet-8 resultClassification
D1OPENNimbus still rejects omitted/null params; [] works.interop strictness
D2FIXEDNethermind trace succeeds without explicit gasPrice.original client fixed
D3OPENEthrex defaults to callTracer, rejects structLogger, names it opcodeTracer.tracer convention
D4MOVEDNethermind fixed; Nimbus now returns success below the runtime-OOG threshold.client defect
D5OPENEstimate-gas spread remains, including legacy 21,000 floors.needs API expectation
D6OPEN49,152-byte deployment: Besu/Nethermind/Ethrex proceed; geth/reth/Erigon cap/OOG.node policy
D7OPENSSTORE per-step cost splits 3–3 by 97,920; no consensus claim.tracer attribution
D8OPENBesu/Ethrex omit account-only accesses from generated lists.client defect
D9OPENMemory OOG returns -32000, reth -32003, Ethrex 3; Nimbus returns empty success on the no-to probe.error semantics
D10DECISIONFour distinct simulate/transfer-log outcomes.spec interaction
D11OPENErigon rejects short storage key 0x0.input strictness
D12PARTIALEthrex log timestamps fixed; transaction timestamps still absent.client defect
D13OPENBesu/Ethrex add depth=2 step for empty sub-call; others do not.tracer convention
D14OPENEthrex and Nimbus cannot serve the tested eth_call state override.coverage gap
D15OPENEthrex and Nimbus lack txpool_status.coverage gap
D16PARTIALErigon added geth-shaped support; Besu/reth/others remain incompatible or absent.debug API shape
D17PARTIALBesu added its own variant; geth/Erigon/Besu/reth still have incompatible shapes.debug API shape
D18OPENBesu accepts 150 and −5; Nimbus also accepts −5.client validation
D19OPENErigon returns oldestBlock=0x0, gasUsedRatio=null for huge blockCount.edge handling
D20FIXEDEthrex simulated GASLIMIT now equals the pinned header exactly.original client fixed
D21OPENBesu/Ethrex still lack raw-tx RPCs; Nimbus also lacks them.coverage gap
D22OPENErigon proof fails at every tested non-head depth.client defect
D23OPENErigon direct logs=474, filter logs=0 for identical range.client defect
D24OPENEthrex requires optional topics.client defect
D25OPENEthrex still lacks block filter and Parity trace methods; Nimbus also lacks them.coverage gap
D26AMBIGUOUSFirst historical changes poll: reth=474, four clients=0.needs API expectation
D27OPENEthrex lacks uncle-count RPC.coverage gap
D28NOTEDeprecated eth_coinbase/eth_accounts remain client-specific.not a defect

7 · What no longer needs ACDT time

8 · Minimal reproductions

These commands use the ethPandaOps Panda CLI. Replace the network and instance names to run the same JSON-RPC against another endpoint.

Nimbus runtime OOG returns success

panda ethnode exec-rpc glamsterdam-devnet-8 teku-nimbusel-1 eth_call \
'[{"from":"0x2cdec15cb181def9f2c24c34dad73539015272be",
   "to":"0x00000000000000000000000000000000deadbe02",
   "value":"0x1","gas":"0x31f37","gasPrice":"0x77359400"},"latest"]'
# Nimbus: 0x
# Other six: OOG. gas=0x31f38 succeeds everywhere.

Besu/Ethrex account-only access-list omission

panda ethnode exec-rpc glamsterdam-devnet-8 teku-besu-1 eth_createAccessList \
'[{"from":"0x2cdec15cb181def9f2c24c34dad73539015272be",
   "data":"0x7300000000000000000000000000000000deadbe023100",
   "gas":"0x100000","gasPrice":"0x77359400"},"latest"]'
# Besu/Ethrex: accessList=[]
# geth/reth/Erigon/Nimbus: [{address: 0x...deadbe02, storageKeys: []}]

Besu fee-history bounds

panda ethnode exec-rpc glamsterdam-devnet-8 teku-besu-1 eth_feeHistory \
'["0x4","latest",[150]]'
panda ethnode exec-rpc glamsterdam-devnet-8 teku-besu-1 eth_feeHistory \
'["0x4","latest",[-5]]'
# Both succeed on Besu.

Erigon non-head proof

head=$(panda ethnode exec-rpc glamsterdam-devnet-8 teku-geth-1 eth_blockNumber)
prior=$(printf '0x%x' $((head-1)))
panda ethnode exec-rpc glamsterdam-devnet-8 teku-erigon-1 eth_getProof \
"[\"0x0000BFF46984E3725691FA540A8C7589300D8282\",[],\"$prior\"]"
# -32000 root hash mismatch in account proof trie

Erigon filter self-inconsistency

filter='{"fromBlock":"0x12a3d","toBlock":"0x12a3e","topics":[]}'
panda ethnode exec-rpc glamsterdam-devnet-8 teku-erigon-1 eth_getLogs "[$filter]"
id=$(panda ethnode exec-rpc glamsterdam-devnet-8 teku-erigon-1 eth_newFilter "[$filter]")
panda ethnode exec-rpc glamsterdam-devnet-8 teku-erigon-1 eth_getFilterLogs "[\"$id\"]"
# Same node and range: 474 direct, 0 through the filter.

Ethrex optional topics field

panda ethnode exec-rpc glamsterdam-devnet-8 teku-ethrex-1 eth_getLogs \
'[{"fromBlock":"0x12a3d","toBlock":"0x12a3e"}]'
# -32000 Expected parameter: topics is missing

9 · Method, scope and versions

Each differential sent the identical request to the seven named nodes, pinned a block where head races could affect output, reduced responses to the field under test, and re-ran anything anomalous against a spec-derived boundary or an internal consistency check. Stateful filters were uninstalled after use. No transaction was broadcast.

ClientLive web3_clientVersion
gethGeth/v1.17.6-unstable-aa1f2fcf-20260813/linux-amd64/go1.26.5
Besubesu/v26.8-develop-6081aee/linux-x86_64/openjdk-java-25
NethermindNethermind/v1.40.0-unstable+3e3bd4bf-hpA/linux-x64/dotnet10.0.11
rethreth/v2.5.0-3d270d9/x86_64-unknown-linux-gnu
Erigonerigon/3.7.0/linux-amd64/go1.26.7
Ethrexethrex/v22.0.0-glamsterdam-devnet-8-86dd3154e5bf415d82d15149bba09adcdbf29481
Nimbus-ELNimbus/v0.4.0-ed9be981/linux-amd64/Nim-2.2.10

Sources and prior art: original devnet-7 differential report · execution-apis fee-history schema · eth_simulateV1 notes · EIP-7708 · Besu access-list issue.

Scope guard: this report does not re-assert the original report's cryptographic root checks, opcode sweep or total-gas agreement; it re-tests the RPC findings. “Open” means the same observable difference still exists on devnet-8. It does not mean the method is standardized, required, or consensus-critical.

Generated from live glamsterdam-devnet-8 probes on 2026-08-24. Public Panda upload. The upload is intended for ACDT discussion and client-team triage; exact commands above are the portable evidence.