# Merge 01, batch `el-evm`: Geth, Nethermind, Besu vs Amsterdam EIP-7843 / EIP-7954 / EIP-8024

This merges three item reviews: `item-geth`, `item-nethermind` and `item-besu`. All three are present and non-empty. The merge adds no findings of its own. Every review was public-source reading only, and nothing was run against a network or node.

**Summary:** none of the three items reported a consensus candidate (a gas, state, validity or encoding difference from the spec). The only reported item is a withheld live-code item from Besu (besu-1). Besu also gave two cosmetic observations, which it labels as not candidates.

## 1. Pins

| Client | Branch | Commit | Spec commit (ethereum/execution-specs, `forks/bogota`, `src/ethereum/forks/amsterdam/`) |
|---|---|---|---|
| Geth (ethereum/go-ethereum) | master | `8817f99f92852f8ac53e596de6202dd9e3494065` (2026-10-27T03:32:47Z) | `7f2f4c9bf6d73b09ecb8b24eb14d08bf0cf0247b` |
| Nethermind (NethermindEth/nethermind) | master | `d48833af88ec4224c86afadd1c596e8f619c406b` (2026-10-27T12:00:20Z) | `7f2f4c9bf6d73b09ecb8b24eb14d08bf0cf0247b` |
| Besu (besu-eth/besu) | main | `aa06c4f8dfc461e483d46c6a1ac72b5bfef8d945` (2026-10-27T13:41:33Z) | `7f2f4c9bf6d73b09ecb8b24eb14d08bf0cf0247b` |

Other pins, from item-nethermind:
- ethereum/EIPs master `d4c575521d99ec881a444ab3af604e684b5f0743`
- ethereum/consensus-specs master `8a80ffa82bd49aac0dd6db17f9e8150aa257fbc2`
- ethereum/execution-apis main `3284ee80c3da3e516c53782dde75157cccf1ebdc`

All items resolved their pins through `api.github.com/repos/<repo>/commits/<branch>`, and all three agree on the same Geth and execution-specs commits.

**Spec versions.** All items report EIP-7843, EIP-7954 and EIP-8024 as **Last Call** on eips.ethereum.org (deadline 2026-11-01). None is live on mainnet.

**EIP-8024 differs from the brief's checklist.** All three items independently found that the current EIP-8024 assigns these opcodes:
- `DUPN 0xe6`, `SWAPN 0xe7`, `EXCHANGE 0xe8`.
- `decode_single`: `(x + 145) % 256`, with immediates 91–127 invalid.
- `decode_pair`: `k = x ^ 143`; `q, r = divmod(k,16)`; `(q+1, r+1)` if `q < r`, else `(r+1, 29-q)`; immediates 82–127 invalid.
- JUMPDEST analysis unchanged.

All three clients were compared against this current text, not the brief's older "n+1 / nibble / immediate-as-data" wording.

**Method caveat (item-geth).** The shell was unavailable to that item, so its Geth, EELS and Nethermind line numbers came through a fetch helper. They are accurate to about ±2 lines. Quoted code is as returned.

Item-nethermind's EIP-7773 inventory lists 18 scheduled EIPs: 2780, 7688, 7708, 7732, 7778, 7843, 7928, 7954, 7976, 7981, 7997, 8024, 8037, 8038, 8045, 8061, 8246, 8282. This merge covers only 7843, 7954 and 8024.

## 2. Candidates

### Geth
No candidates (item-geth §2). Possible divergences the item considered and disproved:
1. **Error ordering when faults coincide** (e.g. DUPN at 1024 items with an invalid immediate). Geth gives `ErrStackOverflow` where EELS gives `InvalidParameter`. Both are exceptional halts (`core/vm/gascosts.go#L249-L264` `Exit`), so only the trace string differs.
2. **Size check before EIP-8037 state gas.** Same order in Geth (`core/vm/evm.go#L672-L677`) and EELS (`interpreter.py` L367 before L375/L379).
3. **Fork-transition init-code limit.** `CheckMaxInitCodeSize`/`CheckMaxCodeSize` test `IsAmsterdam` first, using block rules. No off-by-one.
4. **Optional-field RLP.** `SlotNumber` is the last `rlp:"optional"` field after `BlockAccessListHash`, and both are mandatory in Amsterdam.
5. **Pre-Amsterdam payload with `slotNumber`.** Rejected in `NewPayloadV4` and in `verifyHeader` L284-L286. Open PR #35502 only changes the Engine API error code.
6. **Nethermind SLOTNUM `BadInstruction` on null slot** (N`EvmInstructions.Environment.cs#L970`). Unreachable, because the header validator requires the field.

### Nethermind
No candidates (item-nethermind). Possible divergences the item considered and disproved:
- **SWAPN/EXCHANGE one-based indexing** ([EvmInstructions.Stack.cs#L1053-L1163], [EvmStack.cs#L2088-L2179]). The item enumerated all 256 immediates offline from transcribed expressions: 219 valid singles and 210 valid pairs, identical to spec positions. Stack heights 0–1024 were also compared.
- **Missing immediate / overflow.** A terminal DUPN needs 145 items, SWAPN 146, EXCHANGE 17. `MaxStackSize = 1025` sentinel permits height 1024.
- **First Amsterdam block slot.** The block copies the CL's absolute slot. There is no parent-slot ordering, and orphaned headers are validated.
- **Stale size cap / deposit accounting.** 65,536 cap (`CodeSizeConstants.cs#L6-L10`), strict `>`, size guard before deposit charges (`CodeDepositHandler.cs#L58-L86`).

### Besu
**besu-1: Besu, EIP-7843: affects live code, details withheld.**
- **Confidence:** low; practical reachability doubtful.
- **Detail:** no file, line or trigger is given, per the brief's live-code rule.
- **Known-check:** not found (query `slot number before Amsterdam` and the queries in §4).

Possible divergences the item considered and disproved:
1. **Signed-`long` truncation of slot ≥ 2^63.** Disproved: unsigned RLP decode (`BytesValueRLPInput.java#L322-L337`), unsigned push (`Words.java#L243-L253`), `UInt64` parsing (`PayloadAttributesV4.java#L49-L58`, `QuantityJson.java#L76-L93`), and `Long.toHexString` serialization (`QuantityJson.java#L35-L37`).
2. **EXCHANGE validity boundary.** The comment at `ExchangeOperation.java#L80` says "80-127", but the table (`Eip8024Decoder.java#L119-L134`) rejects 82–127, which matches `S/vm/stack.py#L79-L84`.
3. **Gas charged after mutation in the inlined fast path** (`EVM.java#L464-L475`, `#L486-L494`). On insufficient gas the result is `EXCEPTIONAL_HALT`/`INSUFFICIENT_GAS`: the frame reverts and all gas is consumed, the same outcome as the spec.
4. **Code deposit check order.** Besu checks size before the 0xEF prefix; the spec checks the prefix first. Both give the same halt before any charge (`ContractCreationProcessor.java#L176-L192`).

Besu cosmetic observations. The item calls these "not candidates; no effect on gas, state, validity or encoding":
- **O-1:** the debug disassembler `printInstruction` (`B/evm/…/evm/Code.java#L380-L401`, `OpcodeInfo.java#L309-L311`) prints the EIP-8024 immediate as data. The EIP says an invalid immediate such as `0x5b` should be shown as `INVALID` + `JUMPDEST`. 0x4B is also listed as `unallocatedOpcode` (`OpcodeInfo.java#L154`). Severity: cosmetic.
- **O-2:** stale comment at `ExchangeOperation.java#L80` ("80-127"; the code uses 82–127).

## 3. Rules checked (evidence per client)

Path prefixes:
- G = go-ethereum, N = `src/Nethermind/`, B = besu, S = EELS amsterdam, each at its pin.
- Besu: `evm/…/evm/` = `evm/src/main/java/org/hyperledger/besu/evm/`, `core/…/ethereum/` = `ethereum/core/src/main/java/org/hyperledger/besu/ethereum/`, `api/…/internal/` = `ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/`.

### EIP-7843 SLOTNUM
- **Opcode 0x4b, gas 2.**
  - G `core/vm/opcodes.go#L108`; `core/vm/eips.go#L579-L586` (`constantGas: GasQuickStep`).
  - N `Nethermind.Evm/Instructions/EvmInstructions.Environment.cs#L961-L977` / `#L963-L978` (`UpdateGas<BaseGasCost>`); `Nethermind.Core/GasCostOf.cs#L8-L10`.
  - B `evm/…/evm/operation/SlotNumOperation.java#L29-L39`; `FrontierGasCalculator.java#L68`.
  - S `vm/gas.py#L221`; `vm/instructions/block.py#L264-L294`.
- **Pushes header slot as uint64.**
  - G `core/vm/eips.go#L572-L576`; `core/evm.go#L66-L68,#L82`.
  - N `stack.PushUInt64(slotNumber.Value)`.
  - B `ProcessableBlockHeader.java#L192-L194`.
  - S `block.py#L291`; `fork.py#L316`.
- **Overflow at 1024.**
  - G maxStack(0,1), checked in the `interpreter_gen.go` default case.
  - N `EvmStack.cs#L1586-L1609`.
  - B `SlotNumOperationV2.java#L36`.
  - S `vm/stack.py#L128-L129`.
- **Invalid before Amsterdam.**
  - G `core/vm/jump_table.go#L106-L112`; `core/vm/evm.go#L162`.
  - N `Nethermind.Specs/Forks/25_Amsterdam.cs#L19,#L23`; `Instruction.cs#L68-L186`; `VirtualMachine.OpcodeHandlers.cs#L105-L324`.
  - B `MainnetEVMs.java#L1142-L1160`; `EVM.java#L476-L479`.
- **Header `slot_number` is the last field after the BAL hash.**
  - G `core/types/block.go#L102-L106`.
  - N `Nethermind.Serialization.Rlp/HeaderDecoder.cs#L77-L90`, `#L169-L177`; `RlpHelpers.Cursor.cs#L248-L300` (canonical uint64).
  - B `BlockHeader.java#L311-L316`, `#L357-L360`.
  - S `blocks.py#L247-L269`.
- **Present iff Amsterdam.**
  - G `consensus/beacon/consensus.go#L271-L287`.
  - N `HeaderValidator.cs#L98-L110`, `#L479-L500`.
  - B `SlotNumberPresentValidationRule.java#L33-L40`; `MainnetBlockHeaderValidator.java#L153-L159`.
- **Engine API V4/V5 gating; payload slot goes to the header.**
  - G `eth/catalyst/api.go` ~L219, ~L838-L839, ~L867-L868, ~L387; `miner/worker.go#L340-L343`; `beacon/engine/types.go#L374`.
  - N `PayloadAttributes.cs#L292-L307`; `BlockProducerBase.cs#L211-L225`; `ExecutionPayloadV4.cs#L23-L60`.
  - B `EngineNewPayloadV5.java#L67-L73`, `#L88-L91`; `EngineForkchoiceUpdatedV4.java#L165-L183`.
- **First fork block slot.** No spec rule beyond the CL-supplied value.
  - G takes the value from the CL.
  - N copies the CL's absolute slot.
  - B `BlockHeaderBuilder.java#L275-L279`, `GenesisState.java#L230-L231`.
- **Full uint64 range.** Besu: see the disproof in §2. G/N use uint64/`PushUInt64`.

### EIP-7954 code size
- **0x10000 / 0x20000.**
  - G `params/protocol_params.go#L161-L164`.
  - N `Nethermind.Core/CodeSizeConstants.cs#L6-L10` / `#L9`; `25_Amsterdam.cs#L19-L24`; `IReleaseSpecExtensions.cs#L12-L16`.
  - B `EvmSpecVersion.java#L72-L76`, `#L107-L111`.
  - S `vm/interpreter.py#L79-L81`.
- **Strict `>` deposit check.**
  - G `core/vm/common.go#L28-L54`; `core/vm/evm.go#L651-L701`.
  - N `CodeDepositHandler.cs#L58-L86`; `TransactionProcessor.cs#L1828-L1836`; `VirtualMachine.cs#L717-L746`.
  - B `MaxCodeSizeRule.java#L46-L58`.
  - S `interpreter.py#L360-L389`.
- **CREATE/CREATE2 init-code limit.**
  - G `core/vm/gas_table.go` ~L541-L592.
  - N `EvmInstructions.Create.cs#L126-L158`.
  - B `AbstractCreateOperation.java#L88-L95`; `AbstractCreateOperationV2.java#L81-L90`.
  - S `system.py#L198-L222`, `#L258-L286`.
- **Create tx init code over the limit is invalid.**
  - G `core/state_transition.go#L678-L683`; txpool `core/txpool/validation.go#L87-L89`.
  - N `TransactionExtensions.cs#L57-L58`; `TransactionProcessor.cs#L1010-L1014`.
  - B `MainnetTransactionValidator.java#L138-L143`.
  - S `transactions.py#L631-L638`.
- **Init-code word gas 2/word unchanged.**
  - G `state_transition.go` ~L114-L119.
  - N `ReleaseSpecExtensions.cs#L20-L23`; `EthereumGasPolicy.cs#L346-L354`; `CreateGasCost.cs#L25-L28`; `EvmCalculations.cs#L17-L45`.
  - B `ShanghaiGasCalculator.java#L44-L45`; `CreateOperation.java#L44-L52`.
- **EIP-7702 delegation path.**
  - G: no size rule.
  - N `CodeInfoRepository.cs#L72-L100`, `#L174-L195`; `TransactionProcessor.cs#L1418-L1424`.
  - B: no `MaxCodeSizeRule` on that path.
  - S `interpreter.py#L183-L196`.

### EIP-8024 DUPN/SWAPN/EXCHANGE
- **Bytes 0xe6/0xe7/0xe8, gas 3.**
  - G `core/vm/opcodes.go#L232-L234`; `core/vm/eips.go#L339-L359`.
  - N `EvmInstructions.Stack.cs#L1057,#L1074,#L1091`; `IGasCost.cs#L37-L39`.
  - B `DupNOperation.java#L36,#L59`; `SwapNOperation.java#L37,#L56`; `ExchangeOperation.java#L37,#L56`.
  - S `vm/gas.py#L227-L229`.
- **decode_single and decode_pair, invalid ranges.**
  - G `core/vm/instructions.go` ~L978-L1007, ~L1020, ~L1048, ~L1079.
  - N `EvmInstructions.Stack.cs#L1100,#L1104,L1124,L1147-L1157`.
  - B `Eip8024Decoder.java#L95-L105`, `#L119-L151`.
  - S `vm/stack.py#L25-L91`.
- **Underflow, overflow and positions.**
  - G `instructions.go` ~L1008-L1097.
  - N `EvmStack.cs#L23-L29`, `#L2088-L2179`.
  - B `DupNOperation.java#L75-L97`; `SwapNOperation.java#L72-L95`; `ExchangeOperation.java#L72-L100` (+V2).
  - S `vm/instructions/stack.py#L213-L315`.
- **Missing immediate reads 0; pc += 2.**
  - G `instructions.go` ~L1009-L1017.
  - N `EvmInstructions.Stack.cs#L1106-L1109`; `VirtualMachine.Dispatch.std.cs#L340-L343`.
  - B `DupNOperation.java#L39,#L78`.
  - S `stack.py#L232-L234`.
- **JUMPDEST analysis unchanged.**
  - G `core/vm/analysis_legacy.go#L75-L118`.
  - N `JumpDestinationAnalyzer.std.cs#L188-L223`; `JumpDestinationAnalyzer.cs#L140-L152`.
  - B `Code.java#L274-L292`.
  - S `vm/runtime.py#L22-L69`.
- **Invalid before Amsterdam.**
  - G `jump_table.go#L106-L112`.
  - N `25_Amsterdam.cs`.
  - B `EVM.java#L186,#L464-L475,#L647-L658`; `MainnetEVMs.java#L1156-L1159`.

## 4. Cross-client table

| Rule | Geth | Nethermind | Besu |
|---|---|---|---|
| SLOTNUM 0x4b, gas 2 | agrees with spec | agrees with spec | agrees with spec |
| SLOTNUM pushes header slot (full uint64) | agrees with spec | agrees with spec | agrees with spec |
| SLOTNUM overflow at 1024 | agrees with spec | agrees with spec | agrees with spec |
| SLOTNUM invalid before Amsterdam | agrees with spec | agrees with spec | agrees with spec |
| Header slot_number last, RLP uint64 | agrees with spec | agrees with spec | agrees with spec |
| Header slot present iff Amsterdam | agrees with spec | agrees with spec | agrees with spec |
| Engine API V4/V5 slot gating / payload slot into header | agrees with spec | agrees with spec | agrees with spec |
| First Amsterdam block slot value | agrees with spec | agrees with spec | agrees with spec |
| EIP-7843 code active on mainnet (withheld) | not checked | not checked | differs (see besu-1) |
| MAX_CODE_SIZE 0x10000 / MAX_INIT_CODE_SIZE 0x20000 | agrees with spec | agrees with spec | agrees with spec |
| Deploy size strict `>` (65,536 OK) | agrees with spec | agrees with spec | agrees with spec |
| Deposit check order (prefix/size before gas) | agrees with spec | agrees with spec | agrees with spec (size before prefix; same halt) |
| CREATE/CREATE2 init-code limit | agrees with spec | agrees with spec | agrees with spec |
| Create-tx init-code limit (tx invalid) | agrees with spec | agrees with spec | agrees with spec |
| Init-code word gas unchanged | agrees with spec | agrees with spec | agrees with spec |
| Limit switch at fork transition | agrees with spec | agrees with spec | agrees with spec |
| EIP-7702 delegation path | agrees with spec | agrees with spec | agrees with spec |
| DUPN/SWAPN/EXCHANGE bytes 0xe6–0xe8, gas 3 | agrees with spec | agrees with spec | agrees with spec |
| decode_single + invalid 91–127 | agrees with spec | agrees with spec | agrees with spec |
| decode_pair + invalid 82–127 | agrees with spec | agrees with spec | agrees with spec |
| DUPN/SWAPN/EXCHANGE underflow, overflow, positions | agrees with spec | agrees with spec | agrees with spec |
| Missing immediate = 0; pc += 2 | agrees with spec | agrees with spec | agrees with spec |
| Invalid immediate = exceptional halt | agrees with spec | agrees with spec | agrees with spec |
| Gas charge vs mutation order (OOG outcome) | agrees with spec | not checked | agrees with spec |
| JUMPDEST analysis unchanged | agrees with spec | agrees with spec | agrees with spec |
| New stack opcodes invalid before Amsterdam | agrees with spec | agrees with spec | agrees with spec |
| Disassembler output (non-consensus) | not checked | not checked | differs, cosmetic (O-1; not a candidate) |

Notes on the table:
- The Geth column also reflects item-nethermind's cross-check, and the Nethermind column reflects item-geth's partial cross-check.
- Item-geth could not locate where Nethermind derives its init-code limit. Item-nethermind did locate it (`IReleaseSpecExtensions.cs#L12-L16`: twice the code cap).

## 5. Known-check summary

- **Geth** (`repo:ethereum/go-ethereum`, queries `8024`, `7843 OR slotnum OR slotnumber`, `7954`):
  - No candidates, so nothing to mark known.
  - Past fixes are present at the pin: #33361 (PC increment), #33614 (end-of-code immediate), #34634 (min-stack bounds), #35217 (size bump).
  - Open items: #35292 (draft PR, runtime harness), #35502 (Engine API error code), #35546 (RPC divergence issue), #33661 (Amsterdam tracking).
- **Nethermind** (`repo:NethermindEth/nethermind`, terms 7843/SLOTNUM, 7954/"contract size", 8024/SWAPN/DUPN/EXCHANGE):
  - No candidates.
  - Historical problems are marked known and fixed: #13583, #13586, #13688 (slot); #10708 (old 32-KiB version); #10408, #10475, #10710, #11178 (EIP-8024).
- **Besu** (`repo:besu-eth/besu`, queries 7843, SLOTNUM, slotNumber, 7954, code size, 8024, DUPN/SWAPN/EXCHANGE, "slot number before Amsterdam"):
  - besu-1 is **not found** in issues or PRs.
  - Open IS #11246 is a JMH benchmark harness issue, not consensus.
- **execution-specs Test Release Tracker:** #3217 `tests@v21.0.0` (Glamsterdam Testnet) is open; #3518 and the other trackers read are closed. None names a client failure for these EIPs.
- **Hive** (`hive.ethpandaops.io/glamsterdam`). These runs used older client builds, not the pins.

| Client | Suite (build) | Passed / failed | Failures in 7843 / 7954 / 8024 |
|---|---|---|---|
| Geth | consume-rlp (v1.17.11-unstable-d01e869d) | 44159 / 2 | unknown; result JSON over 10 MB, unreadable |
| Geth | consume-engine | 44223 / 2 | unknown (same reason) |
| Geth | consume-enginex | 83208 / 50 | unknown (same reason) |
| Geth | execute | 173 / 0 | none |
| Nethermind | consume-engine (2.3.0-preview+a183d792) | 239 fails / 44,225 | 24/24, 85/85, 294/294 pass |
| Nethermind | consume-enginex (+edb6764a) | 239 fails / 83,258 | 24/24, 85/85, 294/294 pass |
| Nethermind | consume-rlp (+edb6764a) | 0 fails / 44,161 | 23/23, 85/85, 294/294 pass |
| Besu | consume-engine (v26.10-develop-bdb9e5c) | 44216 / 9 | 24, 85, 294 all pass |
| Besu | consume-enginex (develop-fc46028) | 83189 / 69 | 24, 85, 294 all pass |

Besu's failures are in other areas: `test_unrecoverable_signature`, EIP-8037 header, and BPO blob transitions. Nethermind's 239 failures were not attributed to these EIPs.

**Not found / limits:**
- Geth's hive failing-test names could not be read.
- Item-geth's line numbers are ±2.
- No client was built or executed by any item.
