# Glamsterdam client conformance review: candidate bugs

Review date: 2026-10-27. Scope: the 18 EIPs scheduled in EIP-7773 (Amsterdam on the execution layer, Gloas on the consensus layer), compared against the execution-specs and consensus-specs reference code, in Geth, Nethermind, Besu, Erigon and Reth (execution) and Lighthouse, Prysm, Teku, Nimbus, Lodestar and Grandine (consensus). Everything was done by reading public pages and public source at pinned commits. Nothing was executed against any node or network, so every item below is a code-reading finding, not an observed failure, except where a public hive result is named.

The goal built no code, so there is no trunk repository or commit to name. This report rests on the goal's 18 research batches (74 items): `spec-list`, `known-issues`, `review-pilot`, `el-bal`, `el-stategas`, `el-intrinsic`, `el-evm`, `el-transfer`, `el-requests`, `cl-state`, `cl-forkchoice`, `cl-misc`, `hive-failures`, `disprove` (15 items, one per strongest candidate), `rerun-evm-reth`, `rerun-hive-reth` and `spec-lag`. The commit pins and the line ranges of the top candidates were re-checked against the raw files on 2026-10-27 while writing this report; corrected line numbers replace the earlier notes where they differed.

Headline. None of the 18 EIPs is active on Ethereum mainnet today, so most findings are reported in full. The strongest new findings are on the consensus layer, in the EIP-7732 fork-choice paths that handle payload timeliness committee (PTC) votes: Grandine, Nimbus, Lodestar and Prysm each diverge from the spec's `on_block` / `on_payload_attestation_message` rules in a way that can give them a different head from the other clients under ordinary non-finality conditions. On the execution layer the two highest-ranked items are already known but unfixed in Reth (the EIP-8037 per-transaction gas cap, and the EIP-8282 predeploy-absence check). All EL intrinsic-gas, calldata, access-list, SLOTNUM, contract-size, DUPN/SWAPN/EXCHANGE, transfer-log and SELFDESTRUCT rules read as matching the spec in every client.

## 1. Ranked candidate bugs

Confidence: how sure the review is that the code differs from the spec as described. Severity: the consequence if the condition arises (fork-choice divergence between clients, valid block rejected, invalid block accepted, gossip-only, cosmetic). Verdict is the result of the `disprove` batch, which tried to show each strong candidate was not a divergence. Pins are in section 6; links use the full commit hash.

| # | Client | EIP | What is wrong | Spec citation | Pinned code | Condition under which the client's result differs from the spec | Confidence | Severity | Known / new | Verdict |
|---|---|---|---|---|---|---|---|---|---|---|
| 1 | Grandine | 7732 | The gossip rule "[IGNORE] this is the first valid payload attestation from this validator index" is present only as commented-out code, with a TODO asking implementers whether vote override is allowed. A second, differently-voting message from the same PTC member is accepted and overwrites the stored vote. | p2p-interface.md L908-911 ("[IGNORE] This is the first valid payload attestation from this validator index") and L947-948 (key added to `seen`) | [fork_choice_store/src/store.rs#L4364-L4381](https://github.com/grandinetech/grandine/blob/52cd677117b5d93bf477fe4843148070b0303444/fork_choice_store/src/store.rs#L4364-L4381) (commented IGNORE, then unconditional Accept); [#L4802-L4830](https://github.com/grandinetech/grandine/blob/52cd677117b5d93bf477fe4843148070b0303444/fork_choice_store/src/store.rs#L4802-L4830) (vote written without a first-seen check) | One PTC member publishes two payload attestation messages for the same slot with different `payload_present` values. Every other client keeps the first; Grandine keeps the last. If the pair straddles the 257-of-512 PTC threshold, Grandine's payload status for that block, and therefore its head, differs. Nimbus comparator: [gossip_validation.nim#L2005-L2024](https://github.com/status-im/nimbus-eth2/blob/0abc0dd0fc0750a8875c49089d074f7d7197b62c/beacon_chain/gossip_processing/gossip_validation.nim#L2005-L2024) (isSeen). | High | High (fork-choice divergence) | New. Not in Grandine issues; Grandine PR 705 (closed, unmerged) touched the area; consensus-specs issue 5333 (open) discusses override semantics, but the current spec text is unambiguous. | Confirmed |
| 2 | Nimbus, Lodestar | 7732 | In-block payload attestations are applied to fork choice by setting only the bits present in the aggregation bitfield. The spec applies each in-block attestation per attesting validator and writes every PTC seat that validator occupies; `compute_ptc` samples with replacement, so one validator can hold several seats. | fork-choice.md L268-L290 (`notify_ptc_messages`: one message per attesting index); L1159-1161 and L1185-1187 (`on_payload_attestation_message` collects every `ptc_index` whose validator matches and writes each); beacon-chain.md L1266 (`compute_ptc`) | Nimbus [beacon_chain/fork_choice/fork_choice.nim#L519-L529](https://github.com/status-im/nimbus-eth2/blob/0abc0dd0fc0750a8875c49089d074f7d7197b62c/beacon_chain/fork_choice/fork_choice.nim#L519-L529); Lodestar [importBlock.ts#L280-L296](https://github.com/ChainSafe/lodestar/blob/e4b4f615e68c1a4ea7c57632e6ed8d824624573f/packages/beacon-node/src/chain/blocks/importBlock.ts#L280-L296) and [protoArray.ts#L756-L785](https://github.com/ChainSafe/lodestar/blob/e4b4f615e68c1a4ea7c57632e6ed8d824624573f/packages/fork-choice/src/protoArray/protoArray.ts#L756-L785) | A validator holds two or more PTC seats in a slot and a block includes an aggregate with only some of that validator's seat bits set. Spec, Prysm ([process_block.go#L625-L659](https://github.com/OffchainLabs/prysm/blob/eb681c1d13b57c2d0c5da27a63bb41ac9d071e1a/beacon-chain/blockchain/process_block.go#L625-L659)) and Teku ([ForkChoice.java#L1268-L1307](https://github.com/Consensys-Incorporated/teku/blob/2c35574b3373cbe8ad121a768d98f066aec05b73/ethereum/statetransition/src/main/java/tech/pegasys/teku/statetransition/forkchoice/ForkChoice.java#L1268-L1307)) count every seat; Nimbus and Lodestar count only the set bits. Near the PTC threshold the payload status, and so the head, differs. Duplicate seats are rare on mainnet-size validator sets but common on small devnets. | High | High (fork-choice divergence) | New. Lodestar PR 9369 addressed duplicates on the gossip path only, not block import. Not found for Nimbus. | Confirmed |
| 3 | Grandine | 7732 | A block's included payload attestations are applied to fork choice asynchronously, after the block has been inserted, the head recomputed and the store snapshot published. The spec applies them inside `on_block`, before any head computation that follows. | fork-choice.md L1084-L1098 (`on_block`: head computed before insert, then block stored, then `notify_ptc_messages` runs as part of the same handler) | [fork_choice_store/src/store.rs#L4548-L4685](https://github.com/grandinetech/grandine/blob/52cd677117b5d93bf477fe4843148070b0303444/fork_choice_store/src/store.rs#L4548-L4685) (`apply_block` recomputes head without the block's PTC votes); [fork_choice_control/src/mutator.rs#L3629-L3630](https://github.com/grandinetech/grandine/blob/52cd677117b5d93bf477fe4843148070b0303444/fork_choice_control/src/mutator.rs#L3629-L3630) (task spawned), [#L3669-L3672](https://github.com/grandinetech/grandine/blob/52cd677117b5d93bf477fe4843148070b0303444/fork_choice_control/src/mutator.rs#L3669-L3672) (snapshot published), [#L1692-L1725](https://github.com/grandinetech/grandine/blob/52cd677117b5d93bf477fe4843148070b0303444/fork_choice_control/src/mutator.rs#L1692-L1725) (votes land later); [fork_choice_control/src/queries.rs#L308-L325](https://github.com/grandinetech/grandine/blob/52cd677117b5d93bf477fe4843148070b0303444/fork_choice_control/src/queries.rs#L308-L325) (head query has no barrier) | A block includes an aggregate that moves its parent's PTC tally across 257 of 512. Between publication and the asynchronous task, Grandine's head (and the head it reports to validators and the execution client) is the one computed without those votes. Example from the disproof item: a boosted EMPTY child whose included aggregate crosses the threshold flips the head once the votes land. Nimbus applies them synchronously ([fork_choice.nim#L519-L529](https://github.com/status-im/nimbus-eth2/blob/0abc0dd0fc0750a8875c49089d074f7d7197b62c/beacon_chain/fork_choice/fork_choice.nim#L519-L529)). | High that the window exists; medium-high that it changes the head | Medium-high (transient fork-choice divergence, wrong head served during the window) | New. Not found in Grandine issues. | Confirmed |
| 4 | Prysm | 7732 | Two paths record payload attestations without the spec's slot guards. (a) A message submitted through the Beacon API is checked against the current slot but not against the slot of the attested block, so a message whose `data.slot` differs from the block's slot is recorded instead of being dropped. (b) A message queued because its block was unknown is replayed when the block arrives without re-checking that `data.slot` is still the current slot. The in-block path was fixed in Prysm PR 17555; these two were not. | fork-choice.md L1150-L1154 ("PTC votes can only change the vote for their assigned beacon block, return early otherwise": `if data.slot != state.slot: return`) and L1167-L1169 (`if not is_from_block: assert data.slot == get_current_slot(store)`) | [handlers_pool.go#L956-L1046](https://github.com/OffchainLabs/prysm/blob/eb681c1d13b57c2d0c5da27a63bb41ac9d071e1a/beacon-chain/rpc/eth/beacon/handlers_pool.go#L956-L1046) (current-slot check only); [receive_payload_attestation_message.go#L27-L88](https://github.com/OffchainLabs/prysm/blob/eb681c1d13b57c2d0c5da27a63bb41ac9d071e1a/beacon-chain/blockchain/receive_payload_attestation_message.go#L27-L88) (no block-slot check); [pending_payload_attestation.go#L68-L108](https://github.com/OffchainLabs/prysm/blob/eb681c1d13b57c2d0c5da27a63bb41ac9d071e1a/beacon-chain/sync/pending_payload_attestation.go#L68-L108) (replay without current-slot check); [forkchoice/doubly-linked-tree/gloas.go#L537-L549](https://github.com/OffchainLabs/prysm/blob/eb681c1d13b57c2d0c5da27a63bb41ac9d071e1a/beacon-chain/forkchoice/doubly-linked-tree/gloas.go#L537-L549) (`SetPTCVote` overwrites unconditionally) | (a) A validator client submits a payload attestation whose `data.slot` names a block from another slot (for example a late-arriving duty). (b) A gossip message for a block that arrives in the next slot. Lighthouse enforces both guards in one place ([fork_choice.rs#L1426-L1437](https://github.com/sigp/lighthouse/blob/a376defa30470923556d91bdeb15a802e297f31f/consensus/fork_choice/src/fork_choice.rs#L1426-L1437)). Prysm's PTC tally for the block then differs from every other client's. | High that the vote is recorded; medium that it changes the head | Medium-high (fork-choice divergence) | New. PR 17555 covered the in-block path only. | Confirmed |
| 5 | Grandine | 7732 | `is_head_weak` treats the head weight of equivocating validators as zero. The spec adds the effective balance of every equivocating validator in the head slot's committees to the head weight, because their attestations were discounted earlier. | fork-choice.md L815-L832 ("Compute head weight including equivocations", loop over committees adding `effective_balance`) | [fork_choice_store/src/store.rs#L1473-L1526](https://github.com/grandinetech/grandine/blob/52cd677117b5d93bf477fe4843148070b0303444/fork_choice_store/src/store.rs#L1473-L1526) (`is_head_weak`; equivocators filtered to zero) | Equivocating validators sit in the head slot's committees and the head is near the 20% reorg threshold. The disproof item's 1,000-validator example shows the verdict flipping. Grandine's proposer then builds on the parent while other proposers would build on the head. Proposer-side only; block validation is unaffected. | High | Medium (proposer reorg decision differs; no validation split) | New. Lighthouse's own approximation is known (issues 9593, 9831). Teku matches the spec. | Confirmed |
| 6 | Grandine | 8282, 7688 | The Engine API bridge decodes deposit requests into a list capped at 8,192 entries (the Electra `MAX_DEPOSIT_REQUESTS_PER_PAYLOAD`). The Gloas `ExecutionRequests.deposits` is a `ProgressiveList` with no fixed cap (EIP-7688), and no cap is scheduled (EIP-8254 is Draft). A payload with more deposit requests is rejected as malformed. | beacon-chain.md: `ExecutionRequests` fields are `ProgressiveList` (EIP-7688 types, e.g. L205-L245 pattern) with no `MAX_DEPOSIT_REQUESTS_PER_PAYLOAD` in Gloas | [types/src/preset.rs#L415](https://github.com/grandinetech/grandine/blob/52cd677117b5d93bf477fe4843148070b0303444/types/src/preset.rs#L415) (`MaxDepositRequestsPerPayload = U8192`); [execution_engine/src/types.rs#L1095](https://github.com/grandinetech/grandine/blob/52cd677117b5d93bf477fe4843148070b0303444/execution_engine/src/types.rs#L1095) and [#L1232-L1303](https://github.com/grandinetech/grandine/blob/52cd677117b5d93bf477fe4843148070b0303444/execution_engine/src/types.rs#L1232-L1303) (request parsing into the capped list) | A block whose execution payload carries more than 8,192 deposit requests. Each deposit costs roughly 23,500 gas, so this needs about 193-200M gas of deposits in one block, which is above today's gas limits but a valid block under the spec. Grandine rejects a valid block. | Medium | High if reached (valid block rejected), latent at current gas limits | New. Not found. | Confirmed |
| 7 | Prysm | 7732 | The batch import path used during initial sync never applies in-block payload attestations to fork choice, and `insertFirstPayloadIfNeeded` marks the parent FULL on the strength of the child's bid before the child's signature is verified, with no rollback. | fork-choice.md L1084-L1098 (`on_block` runs `notify_ptc_messages` for every block, regardless of how it was received) | [process_block.go#L188-L382](https://github.com/OffchainLabs/prysm/blob/eb681c1d13b57c2d0c5da27a63bb41ac9d071e1a/beacon-chain/blockchain/process_block.go#L188-L382) (`onBlockBatch`); [process_block_helpers.go#L457-L469](https://github.com/OffchainLabs/prysm/blob/eb681c1d13b57c2d0c5da27a63bb41ac9d071e1a/beacon-chain/blockchain/process_block_helpers.go#L457-L469) | A node that syncs through a range containing competing branches and then chooses a head inside that range, or a batch containing a block that later fails verification after its parent was marked FULL. The fork-choice store after sync differs from a node that processed the same blocks one by one. | Medium-low | Medium (post-sync fork-choice divergence) | New. Not found. | Confirmed narrowly |
| 8 | Reth | 8037 | `TX_MAX_TOTAL_GAS_LIMIT` (2^32-1) is not enforced in the block executor: with EIP-8037 active the regular-gas cap is replaced by `u64::MAX` and only block capacity is checked. The spec rejects any transaction whose `gas` exceeds 2^32-1. | transactions.py L640-L641 (`if tx.gas > GasCosts.TX_MAX_TOTAL_GAS_LIMIT: raise TransactionGasLimitExceededError`) | [crates/ethereum/evm/src/executor.rs#L275-L302](https://github.com/paradigmxyz/reth/blob/e0378eb691aa2770bb96fd4ecec2f4445a2a3e7c/crates/ethereum/evm/src/executor.rs#L275-L302) (capacity only); evm2 [crates/evm2/src/version/mod.rs#L87-L94](https://github.com/alloy-rs/evm2/blob/4a53ec080dfbb904e98e3ca8f0abcc11ab8abae0/crates/evm2/src/version/mod.rs#L87-L94) (`tx_gas_caps` returns `u64::MAX` under 8037); pool [crates/transaction-pool/src/validate/eth.rs#L648-L653](https://github.com/paradigmxyz/reth/blob/e0378eb691aa2770bb96fd4ecec2f4445a2a3e7c/crates/transaction-pool/src/validate/eth.rs#L648-L653) | A block whose gas limit is at least 2^32 (set at genesis, or reached after roughly 4,400 maximum-increase blocks from 60M) contains a transaction with `gas` above 2^32-1 that fits the remaining capacity. Reth executes it; the spec and the other clients reject the block. Public hive run of 2026-10-26 at this pin: 6 `above_cap` fixtures fail; Reth executes the transaction and the block is caught only by the block access list hash mismatch. | High | High (invalid block accepted; latent) | Known, unfixed: alloy-rs/evm2 issue 807 (open), revm issue 3929 (open), Reth PRs 27589 and 27641 (closed, unmerged). | Confirmed |
| 9 | Reth | 8282 | When the builder deposit or builder exit predeploy has no code, the system call succeeds with empty output and the block is accepted. The spec's checked system transaction raises when the target has no code, so the block is invalid. | fork.py L665-L719 (`process_checked_system_transaction` rejects a target without code) and L899-L921 (builder deposit and exit calls use it) | [crates/ethereum/evm/src/execution.rs#L390-L416](https://github.com/paradigmxyz/reth/blob/e0378eb691aa2770bb96fd4ecec2f4445a2a3e7c/crates/ethereum/evm/src/execution.rs#L390-L416) and [#L421-L451](https://github.com/paradigmxyz/reth/blob/e0378eb691aa2770bb96fd4ecec2f4445a2a3e7c/crates/ethereum/evm/src/execution.rs#L421-L451) (only a failed status is rejected); evm2 [crates/evm2/src/evm/system.rs#L139-L186](https://github.com/alloy-rs/evm2/blob/4a53ec080dfbb904e98e3ca8f0abcc11ab8abae0/crates/evm2/src/evm/system.rs#L139-L186) | A chain whose state lacks the EIP-8282 predeploys at the Amsterdam fork (a misconfigured devnet, or mainnet only if the predeploys were never deployed). Geth rejects with "empty system contract: no code" ([core/state_processor.go#L469-L471](https://github.com/ethereum/go-ethereum/blob/8817f99f92852f8ac53e596de6202dd9e3494065/core/state_processor.go#L469-L471)). Hive `deploy_after_fork` cases: Reth returns INVALID only through the block access list hash mismatch. | High | Medium (static condition; invalid block accepted on a misconfigured chain) | Known, unfixed: Reth PR 26695 (closed, unmerged), alloy-evm PR 400 (open). | Confirmed |
| 10 | Nimbus (stable) | 7732 | Payload attestation membership is checked against the head state in both gossip validation and the fork-choice handler. The handler in the spec uses the attested block's state; the gossip rule uses the head state, so only the handler half is a divergence. | fork-choice.md L1150 (`state = store.block_states[data.beacon_block_root]`); p2p-interface.md L930 (`state = store.block_states[get_head(store).root]`) | [gossip_validation.nim#L2040-L2052](https://github.com/status-im/nimbus-eth2/blob/0abc0dd0fc0750a8875c49089d074f7d7197b62c/beacon_chain/gossip_processing/gossip_validation.nim#L2040-L2052); [fork_choice_epbs.nim#L231-L247](https://github.com/status-im/nimbus-eth2/blob/0abc0dd0fc0750a8875c49089d074f7d7197b62c/beacon_chain/fork_choice/fork_choice_epbs.nim#L231-L247) | Two branches that split before the end of epoch E-2 (multi-epoch non-finality), so the PTC for a slot differs between the head's state and the attested block's state. Nimbus then validates against the wrong committee and records votes for the wrong seats. | High | Medium (fork-choice divergence only under long non-finality) | Known: fixed on Nimbus unstable by PR 9341; stable at this pin still has it. | Confirmed (stable only) |
| 11 | Nimbus, Lighthouse | 7732 | Bid gossip checks that depend on state (builder index validity, builder can cover bid, builder not exiting) are evaluated on the unadvanced head state instead of the parent block's state advanced to the bid's slot. | p2p-interface.md L1011 (`state = store.block_states[bid.parent_block_root]`) and L1048-L1049 (`process_slots(state, bid.slot)`) | Nimbus [gossip_validation.nim#L1881-L1894](https://github.com/status-im/nimbus-eth2/blob/0abc0dd0fc0750a8875c49089d074f7d7197b62c/beacon_chain/gossip_processing/gossip_validation.nim#L1881-L1894) and [#L1916-L1920](https://github.com/status-im/nimbus-eth2/blob/0abc0dd0fc0750a8875c49089d074f7d7197b62c/beacon_chain/gossip_processing/gossip_validation.nim#L1916-L1920); Lighthouse [gossip_verified_bid.rs#L353-L387](https://github.com/sigp/lighthouse/blob/a376defa30470923556d91bdeb15a802e297f31f/beacon_node/beacon_chain/src/payload_bid_verification/gossip_verified_bid.rs#L353-L387) and [#L449-L472](https://github.com/sigp/lighthouse/blob/a376defa30470923556d91bdeb15a802e297f31f/beacon_node/beacon_chain/src/payload_bid_verification/gossip_verified_bid.rs#L449-L472) | A bid whose parent is not the head, or a builder whose balance or exit status changes between the head state's slot and the bid slot (pending withdrawals, deposits credited at an epoch boundary). The gossip verdict (accept vs ignore vs reject) differs; block validation is unaffected. | High | Low (gossip verdict only) | Lighthouse known (issue 9520). Nimbus new. | Confirmed, narrowed |
| 12 | Lodestar | 7732 | `is_head_weak` falls back to a different balance source when the head block's state is not cached, so the weight used for the 20% threshold can differ from the spec's `head_state` computation. | fork-choice.md L815-L832 | [forkChoice.ts#L1701-L1754](https://github.com/ChainSafe/lodestar/blob/e4b4f615e68c1a4ea7c57632e6ed8d824624573f/packages/fork-choice/src/forkChoice/forkChoice.ts#L1701-L1754) | Head state evicted from the cache while the proposer evaluates a reorg; the fallback balances differ from the head state's effective balances. Proposer-side only. | Medium (full trace not completed) | Medium (proposer reorg decision) | New. | Confirmed conditionally |
| 13 | All six CL clients | 7732 | Builder onboarding at the Gloas fork follows the v1.7.0-beta.4 `fork.md` routing, not the master text after consensus-specs PR 5736 (merged 2026-10-26, commit 8b374fa8), which separates builder and validator deposits at the fork. | specs/gloas/fork.md at 8a80ffa8 (`upgrade_to_gloas`, builder onboarding) versus the same file at v1.7.0-beta.4 | Lighthouse, Prysm, Teku, Nimbus, Lodestar, Grandine fork-upgrade code (per `cl-state` and `spec-lag` results); Teku PR 11582 (open) and Lodestar PR 10428 (open) carry the new routing | Only if clients ship different versions of the routing on the same network: the post-fork builder set and balances would differ and every later bid check would disagree. No release contains PR 5736 (newest is v1.7.0-beta.4); the ACDT 101 plan is a release plus a short devnet, not Hoodi. Sepolia forked 2026-10-06 on the old routing. | High that the code is the old routing | Medium if shipped unevenly; otherwise a transient spec lag | Known (open PRs in two clients; spec lag, not a client bug). | Confirmed as lag |
| 14 | Prysm | 7732 | The bid seen-cache is populated right after the signature check, before the gas-limit, parent and builder checks. A later, corrected bid from the same builder for the same (slot, parent) is IGNOREd even though the first one was rejected. | p2p-interface.md L971 (IGNORE only on an already seen *valid* bid) and L1082 (`seen.execution_payload_bids.add` after all checks) | [validate_execution_payload_bid.go#L69-L74](https://github.com/OffchainLabs/prysm/blob/eb681c1d13b57c2d0c5da27a63bb41ac9d071e1a/beacon-chain/sync/validate_execution_payload_bid.go#L69-L74) and [#L120-L168](https://github.com/OffchainLabs/prysm/blob/eb681c1d13b57c2d0c5da27a63bb41ac9d071e1a/beacon-chain/sync/validate_execution_payload_bid.go#L120-L168) | A builder publishes a bid that fails a post-signature check and then a corrected bid. Prysm peers do not forward the corrected bid; other clients do. | High | Low (gossip propagation only) | Known as a deliberate policy (Prysm PR 16539). | Confirmed |
| 15 | Grandine | 8282, 7688 | (a) Envelope gossip validation does not call `verify_execution_requests_limits`. (b) Beacon API block publication with default gossip validation forwards a block carrying more than the allowed number of payload attestations before full processing rejects it. | p2p-interface.md L871 (`verify_execution_requests_limits(envelope.execution_requests)`) and L509 (definition) | [store.rs#L4004-L4041](https://github.com/grandinetech/grandine/blob/52cd677117b5d93bf477fe4843148070b0303444/fork_choice_store/src/store.rs#L4004-L4041) (envelope gossip rules; no limits call) and [#L4174-L4179](https://github.com/grandinetech/grandine/blob/52cd677117b5d93bf477fe4843148070b0303444/fork_choice_store/src/store.rs#L4174-L4179) (only the requests root is checked); [http_api/src/standard.rs#L5037-L5083](https://github.com/grandinetech/grandine/blob/52cd677117b5d93bf477fe4843148070b0303444/http_api/src/standard.rs#L5037-L5083); [transition_functions/src/gloas/block_processing.rs#L141](https://github.com/grandinetech/grandine/blob/52cd677117b5d93bf477fe4843148070b0303444/transition_functions/src/gloas/block_processing.rs#L141) (payload attestation count checked only in full block processing) and [#L763-L775](https://github.com/grandinetech/grandine/blob/52cd677117b5d93bf477fe4843148070b0303444/transition_functions/src/gloas/block_processing.rs#L763-L775) (request limits checked there, not at envelope gossip) | (a) An envelope whose request counts exceed the limits. The disproof item showed this cannot come from a valid EIP-8282 payload, and block processing rejects it later, so the effect is only a missed REJECT at gossip. (b) A locally published block with five payload attestations is gossiped and then rejected. | High | Low (gossip verdict only) | New. | Confirmed, low |

### 1b. Other candidates (known, cosmetic, or not reachable from canonical state)

| Client | EIP | What is wrong | Pinned code / reference | Severity | Known / new |
|---|---|---|---|---|---|
| Geth | 7928 | Storage restored at a shared block-access index n+1 is recorded as a write; a withdrawal credit forwarded by a system call leaves a zero balance change. Needs substituted predeploy code, so not reachable on canonical state. Geth skips the fixture in `tests/block_test.go#L94-L96`. | go-ethereum issue 35776 (open), PR 35843 (open), PR 35895 (closed, unmerged), issue 35894 (duplicate). Hive 2026-10-26: 2 of 50 BAL cross-index cases fail. | Medium (only on a chain with different predeploy code) | Known |
| Nethermind | 7928 | Block access list decoder caps a single account's change list at 65,535 entries; the spec has no such cap. Needs at least 65,536 transactions from one sender in one block. Hive's 239 failing cases are wrong error reasons in parallel BAL validation (blocks still INVALID). | PR 15276 (open); issues 15223, 15249. `src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/BlockAccessListDecoder.cs` at d48833af. | Low (latent) | Known |
| Erigon | 7928 | A 10 MiB byte cap on the encoded block access list, absent from the spec. | PR 24684 (intentional). | Low (latent) | Known, intentional |
| Erigon | 2780 | Transaction pool reload from disk parses entries without the sender and drops near-minimum-gas self-transfers that were valid when admitted. Pool only, no consensus effect. | [txnprovider/txpool/pool.go#L1046-L1085](https://github.com/erigontech/erigon/blob/f32a39d1e5f87a9ec3a9ce2858171c74acc3c242/txnprovider/txpool/pool.go#L1046-L1085) and [#L2794-L2823](https://github.com/erigontech/erigon/blob/f32a39d1e5f87a9ec3a9ce2858171c74acc3c242/txnprovider/txpool/pool.go#L2794-L2823) | Cosmetic | New, confirmed |
| Besu | 8037, 8038, 8246, 7928 | Convenience `MainnetEVMs.amsterdam` uses `PragueGasCalculator` (tests and evmtool only); `maxUsedGas` RPC floor; LIGHT body validation skips `gas_used`; EVMExecutor/evmtool burn balance. | PRs 11526, 11580, 11655, 11474. Hive 2026-10-26: 9 of 69 failures, unrelated to these. | Low | Known |
| Reth | Engine API | Pre-Amsterdam `engine_newPayloadV5` returns -32602 instead of -38005 (unsupported fork). | `rerun-hive-reth`; wrong-error cases also in Reth PR 26723 (known). | Cosmetic | New (error code) |
| Reth | 7928 | Hive fixture `bal_withdrawals_and_dequeues_net_balance_at_last_index`: expected VALID, Reth returns INVALID in every suite at e0378eb6. No public report found; cause not traced. | Hive glamsterdam listing, run of 2026-10-26. | Medium (valid block rejected if the fixture is right) | New, unexplained |
| Reth (evm2) | 8246 | A SELFDESTRUCT survivor over pre-existing storage behaves differently only under a synthetic pre-state that cannot arise from canonical history. | evm2 at 4a53ec08 (per `el-transfer`). | Low | New, unreachable |
| Nethermind, Geth | 8037 | Negative net state gas at settlement is rejected rather than clamped to zero. Refuted as unreachable (section 3). | Nethermind PR 13178 (intentional). | None | Known, refuted |
| Lighthouse | 7732 | `is_head_weak` omits committee equivocating weight (approximation). | Issues 9593, 9831 (open). | Medium (proposer-side) | Known |
| Lighthouse, Prysm, Lodestar | 7732 | Index-1 (payload-present) attestations: no optimistic IGNORE, no REJECT for an invalid payload status. | Lighthouse issue 10464; Prysm PR 17574 (open); Lodestar issues 10438, 9637. | Low | Known |
| Lighthouse | 7732 | IGNORE instead of REJECT for several invalid-block gossip conditions (code TODOs); no gossip REJECT when a bid is not on the parent's `latest_block_hash`; bid gossip key, lookahead and clock-disparity details; ByRange requests starting before Gloas rejected; canonical filter by weight (spec PR 5608); no envelope replay after restart; PTC duty needs head at slot. | Issues 8949 (closed), 10414, 9474; spec PR 5608. | Low to cosmetic | Mostly known |
| Prysm | 7732 | EnvelopesByRange count and start handling; proposer reorg parent payload status; previous-slot weight (issue 17119, rejected); restart state (issue 17735). Five further 7732 items and one 8061 item are withheld (section 1c). | Issues 17735, 17119. | Low to cosmetic | Mostly known |
| Teku | 7732 | Proposer-boost equivocation suppression missing (issues 10608, 11020; PR 11041 closed); an envelope cache entry from an invalid envelope persists (issues 11568, 11588); ByRoot omits the non-finalized exception; transaction-count cap kept in the Gloas decoder. | As listed. | Low to medium | Mostly known |
| Nimbus | 7732 | Bid verdict order differs (cosmetic); a quarantined envelope is accepted (low); `is_head_weak` node weight includes boost (unproved). | `cl-forkchoice`, `disprove` results. | Low | New, unproved |
| Lodestar | 7732 | JavaScript number precision above 2^53 gwei for a single builder balance (about 9M ETH in one builder); zero-count EnvelopesByRange returns InvalidRequest. | PR 9835 (limitation). | Low | Known |
| Grandine | 7732 | Clock 250 ms early at the fork boundary (PR 989 open, issue 912); no envelope replay after restart (issues 886, 887); delayed index-1 attestations (refuted, section 3). | As listed. | Low | Known |
| Lighthouse, Prysm, Lodestar, Teku | 8061 | `MIN_EPOCHS_FOR_BLOCK_REQUESTS` fixed at 33,024 rather than derived. | Lodestar PR 10120 (open). | Cosmetic | Known |
| Lighthouse | 7688 | Decode-time list limits applied to progressive lists. | Issue 10106 (intentional). | None | Known, intentional |

### 1c. Candidates in code already active on mainnet (details withheld)

The rule in the goal: a candidate in code that is active on Ethereum mainnet today is reported only as client and EIP. These items are in code paths shared with live forks (Fulu, Electra or earlier), so no file, line or condition is given.

- Besu, EIP-7843: affects live code, details withheld (low confidence, not found in issues).
- Lighthouse, EIP-7732: affects live code, details withheld (two items).
- Prysm, EIP-7732: affects live code, details withheld (five items).
- Prysm, EIP-8061: affects live code, details withheld.
- Teku, EIP-7732: affects live code, details withheld.
- Teku, EIP-8061: affects live code, details withheld.
- Nimbus, EIP-7688: affects live code, details withheld.
- Nimbus, EIP-8061: affects live code, details withheld.
- Reth, EIP-7002 and EIP-7251 request handling surfaced by the hive run: affects live code, details withheld.

## 2. Where clients disagree with each other

Each row is a rule where at least two clients give different results. Rows marked "spec-equal" are differences the review could not show to matter.

| Rule | Spec | Clients | Matters? |
|---|---|---|---|
| EIP-8037 per-transaction cap `TX_MAX_TOTAL_GAS_LIMIT` in block execution | Reject `tx.gas > 2^32-1` | Geth, Nethermind, Besu, Erigon enforce; Reth does not (item 8) | Yes, once block gas limit reaches 2^32 |
| EIP-8282 predeploy without code at the fork | Block invalid | Geth, Nethermind, Besu, Erigon reject; Reth accepts (item 9) | Yes, on a chain lacking the predeploys |
| Negative net state gas at settlement (EIP-8037) | Clamp to zero (`gas.py#L1216`) | Geth and Nethermind reject the block; Erigon clamps with a different formula; Reth follows the spec | No: refuted as unreachable (section 3) |
| `engine_forkchoiceUpdatedV4` without `targetGasLimit` | Required field | Geth and Reth accept (Geth PR 35514, intentional); Nethermind rejects | Engine API only; a consensus client that omits it works with two of five clients |
| Extra block access list caps (EIP-7928) | None | Nethermind: 65,535 changes per account; Erigon: 10 MiB encoded; Geth, Besu, Reth: none | Latent; both caps are far above anything a current block can hold |
| Builder onboarding at the Gloas fork | Master (PR 5736) | All six on beta.4 routing; Teku and Lodestar have open PRs for master | Only if shipped unevenly (item 13) |
| State used for payload attestation gossip and handling | Gossip: head state; handler: attested block's state | Teku, Lighthouse, Prysm: block state in both; Nimbus stable: head state in both; Prysm keys by dependent root | Spec text itself is inconsistent (section 5); only Nimbus stable diverges from the handler rule |
| In-block PTC votes for validators holding several seats | Every seat written | Prysm, Teku: all seats; Nimbus, Lodestar: set bits only (item 2) | Yes, near the PTC threshold |
| Timing of in-block PTC votes relative to head computation | Inside `on_block` | Nimbus, Lighthouse, Prysm (gossip path), Teku, Lodestar: synchronous; Grandine: asynchronous (item 3); Prysm batch sync: never (item 7) | Yes, transiently |
| First-valid rule for payload attestation gossip | IGNORE later messages from the same index | Lighthouse, Prysm, Teku, Nimbus, Lodestar: first wins; Grandine: last wins (item 1) | Yes |
| `is_head_weak` equivocator handling | Add effective balance of equivocators in committees | Teku: spec; Lighthouse: omits committee equivocators (known); Grandine: zeroes them (item 5); Lodestar: head-state fallback (item 12); Prysm: previous-slot weight variant (issue 17119); Nimbus: node weight including boost (unproved). Six different approximations. | Proposer-side only |
| Optimistic IGNORE for index-1 payload attestations | Required | Teku, Nimbus, Grandine: yes; Lighthouse, Prysm, Lodestar: missing (known) | Gossip only |
| `MIN_EPOCHS_FOR_BLOCK_REQUESTS` | Derived from churn (EIP-8061) | Lighthouse, Prysm, Lodestar, Teku: constant 33,024; Nimbus, Grandine: derived | Cosmetic |
| IGNORE versus REJECT verdict order for bids and payload attestations | Spec order | Lighthouse, Prysm, Nimbus, Grandine each check in a different order | Cosmetic unless a peer-scoring difference matters |
| EIP-7708 transfer logs | Always on in Amsterdam | Erigon has a `DisabledEIPs` switch (not used on L1) | No |

## 3. Candidates that were disproved, and why

Each was handed to one `disprove` item that tried to show the candidate was not a divergence.

- **Negative net state gas (Geth, Nethermind reject; EELS clamps; Erigon clamps differently).** Refuted. Every state-gas credit in `storage.py#L114-L158`, `gas.py#L459-L647` and `vm/__init__.py#L188-L244` matches a charge in the same transaction, so the net is never negative and the clamp at `gas.py#L1216` is never taken. The difference between reject and clamp is unreachable. Recommend a spec clarification only.
- **Lighthouse applies block-carried PTC votes before `on_block` checks (L-1).** Refuted as consequential. `load_parent` ([block_verification.rs#L2009-L2031](https://github.com/sigp/lighthouse/blob/a376defa30470923556d91bdeb15a802e297f31f/beacon_node/beacon_chain/src/block_verification.rs#L2009-L2031)) enforces the same parent-known predicate on every path before the votes are applied; what remains is ordering with no observable effect.
- **Grandine delays index-1 block attestations (G-4a).** Refuted. phase0 `fork-choice.md#L1013-L1030` permits later processing of block attestations, and Grandine's retry keeps the block origin, so the result matches the spec.
- **Nethermind EIP-8037 items (pre-state versus current existence predicate, per-EIP activation flags, check order).** Not disproved by a dedicated item but ranked out: the existence predicate difference is unreachable, the flags are chainspec-only, the order is cosmetic.
- **Geth EIP-7928 shared-index netting (C1, C2).** Confirmed as a divergence but not reachable from canonical state: it needs substituted predeploy code. Kept in section 1b as known.
- **Teku payload attestation gossip uses the block state (T-3).** Attribution inverted. The spec's gossip rule uses the head state (p2p L930) while its handler uses the block state (fork-choice L1150); Teku, Lighthouse and Prysm chose the block state deliberately (Teku PR 11215, Lighthouse issue 9305). The only client that follows the opposite choice in the handler is Nimbus stable (item 10). Reported as a spec-text issue in section 5.
- **Erigon txpool reload (Erigon-1).** Not disproved: confirmed as cosmetic.
- **Prysm bid seen-cache (P-1), Prysm API and queued payload attestations (P-4, P-5), Prysm batch sync (P-6), Nimbus and Lodestar duplicate seats, Grandine PTC override (G-3), Grandine post-publication votes (G-5), Grandine request limits (C2, C3, C4), bid gossip state (N-3, L-8), `is_head_weak` implementations, Reth caps (R-1), Reth predeploy check (reth-1).** All survived the disproof attempt and appear in section 1 with their verdicts.

## 4. EIP list and spec versions the review used

Source: EIP-7773 "Glamsterdam" (meta), section "Scheduled for Inclusion", at ethereum/EIPs master d4c575521d99ec881a444ab3af604e684b5f0743 (2026-10-27). Seventeen EIPs are in Last Call with a deadline of 2026-11-01 (EIP-8246: 2026-11-04); EIP-7688 is in Review. None is listed in EIP-7600 (Pectra) or EIP-7607 (Fusaka), the EIP-7773 mainnet activation row is blank, and `GLOAS_FORK_EPOCH` is far-future in the mainnet config, so none is live on mainnet today. Glamsterdam activated on Sepolia at epoch 353024 (2026-10-06). The Hoodi activation planned for epoch 132352 (2026-10-26) was postponed with no new date (blog.ethereum.org, 2026-10-22).

| EIP | Title | Layer | Reference spec location (at the pins below) |
|---|---|---|---|
| 2780 | Resource-based intrinsic gas | EL | `src/ethereum/forks/amsterdam/transactions.py`, `vm/gas.py` |
| 7688 | Forward-compatible consensus data structures | CL | `specs/gloas/beacon-chain.md` (ProgressiveList containers) |
| 7708 | ETH transfers emit a log | EL | `vm/instructions/system.py`, `fork.py` |
| 7732 | Enshrined proposer-builder separation (Gloas) | CL | `specs/gloas/beacon-chain.md`, `builder.md`, `fork.md`, `fork-choice.md`, `p2p-interface.md`, `validator.md`, `fast-confirmation.md` |
| 7778 | Block gas accounting without refunds | EL | `fork.py`, `vm/gas.py` |
| 7843 | SLOTNUM opcode | EL (CL: `slot_number` in payload) | `vm/instructions/block.py`; `specs/gloas/beacon-chain.md` |
| 7928 | Block-level access lists | EL (CL: payload field) | `block_access_lists.py`, `fork.py`; `specs/gloas/beacon-chain.md` |
| 7954 | Increase max contract size | EL | `vm/__init__.py`, `transactions.py` |
| 7976 | Calldata floor cost increase | EL | `transactions.py` |
| 7981 | Access list cost increase | EL | `transactions.py` |
| 7997 | Deterministic factory contract | EL | No spec code; rule: clients MUST NOT check factory existence at the fork |
| 8024 | SWAPN, DUPN, EXCHANGE | EL | `vm/instructions/stack.py` (opcodes 0xe6, 0xe7, 0xe8) |
| 8037 | State creation gas | EL | `vm/gas.py`, `vm/instructions/storage.py`, `fork.py` |
| 8038 | State access gas | EL | `vm/gas.py` |
| 8045 | Exclude slashed validators from proposing | CL | `specs/gloas/beacon-chain.md` |
| 8061 | Exit and consolidation churn | CL | `specs/gloas/beacon-chain.md` |
| 8246 | Remove SELFDESTRUCT burn | EL | `vm/instructions/system.py` |
| 8282 | Builder execution requests | EL and CL | `fork.py` (system calls); `specs/gloas/beacon-chain.md` |

EIPs that appear in a spec intro or devnet list but are not scheduled: 7975 (eth/70), 8070 (eth/72), 8136, 8159 (eth/71), 8189 (snap/2) are networking EIPs outside the review; 8070 and 8261 are named in the gloas intro; devnet-8's list includes 7610, which the meta EIP does not.

Spec pins:

| Repository | Branch or tag | Commit | Note |
|---|---|---|---|
| ethereum/execution-specs | `forks/bogota` (default; `master` removed) | 7f2f4c9bf6d73b09ecb8b24eb14d08bf0cf0247b (2026-10-27) | Amsterdam at `src/ethereum/forks/amsterdam` |
| ethereum/execution-specs | `forks/amsterdam` | 16898000911c1e3caa57be08509536c10ae92d3e (2026-10-06) | Branch head |
| ethereum/execution-spec-tests | `tests-glamsterdam-devnet@v8.1.4`; `v21.0.2` | release tags | Devnet-8 fixtures; newest stable release with Amsterdam tests |
| ethereum/consensus-specs | `master` | 8a80ffa82bd49aac0dd6db17f9e8150aa257fbc2 (2026-10-27) | `pyproject` says 1.7.0-beta.5, unreleased |
| ethereum/consensus-specs | `v1.7.0-beta.4` | release of 2026-10-08 | Newest release; the only normative gloas difference to master is `fork.md` builder onboarding (PR 5736, 8b374fa81b0918b84fe0063a56b4a482e1fa2c6f, 2026-10-26) |
| ethereum/EIPs | `master` | d4c575521d99ec881a444ab3af604e684b5f0743 | EIP texts |
| ethpandaops/hive-tests | default | 13e90230 | Hive run definitions |

Client pins (default branch unless noted, read 2026-10-27):

| Client | Repository | Commit |
|---|---|---|
| Geth | ethereum/go-ethereum | 8817f99f92852f8ac53e596de6202dd9e3494065 |
| Nethermind | NethermindEth/nethermind | d48833af88ec4224c86afadd1c596e8f619c406b |
| Besu | besu-eth/besu | aa06c4f8dfc461e483d46c6a1ac72b5bfef8d945 |
| Erigon | erigontech/erigon | f32a39d1e5f87a9ec3a9ce2858171c74acc3c242 |
| Reth | paradigmxyz/reth | e0378eb691aa2770bb96fd4ecec2f4445a2a3e7c |
| Reth EVM (evm2) | alloy-rs/evm2 | 4a53ec080dfbb904e98e3ca8f0abcc11ab8abae0 |
| Lighthouse | sigp/lighthouse, `stable` (v8.3.2) | a376defa30470923556d91bdeb15a802e297f31f (`unstable` 301f3bf2 was not reviewed) |
| Prysm | OffchainLabs/prysm | eb681c1d13b57c2d0c5da27a63bb41ac9d071e1a |
| Teku | Consensys-Incorporated/teku | 2c35574b3373cbe8ad121a768d98f066aec05b73 |
| Nimbus | status-im/nimbus-eth2, `stable` | 0abc0dd0fc0750a8875c49089d074f7d7197b62c |
| Lodestar | ChainSafe/lodestar | e4b4f615e68c1a4ea7c57632e6ed8d824624573f |
| Grandine | grandinetech/grandine | 52cd677117b5d93bf477fe4843148070b0303444 |

Known-results sources used to mark candidates as known: each client's GitHub issues and pull requests (open and closed), the hive dashboard listings `glamsterdam` and `glamsterdam-quick` (newest runs of 2026-10-26, read as JSON), the execution-specs "Test Release Tracker" issues, ethpandaops glamsterdam-devnet-8 notes, and ethereum/pm ACDT issues. Hive at 2026-10-26, full glamsterdam suite failing instances: Reth 1,626 (engine) and 3,094 (enginex), of which 1,613 and 3,075 are the undefined-error-message mapping; Nethermind 239 (wrong error reason, blocks still INVALID); Besu 9 of 69; Geth 2 of 50 (BAL cross-index); Erigon, ethrex and nimbus-el 0. Hive publishes no consensus-layer suites, so no CL candidate could be checked against hive.

## 5. Spec-text issues noticed during the review

- EIP-7981's table still shows 2,400 and 1,900 per-item costs and a 21,000 base, stale against EIP-8038 and EIP-2780; its "flat surcharge" prose and the floor-token rationale give the same numbers, so clients agree.
- EIP-2780's floor prose is ambiguous on zero-value calls; every client reads it the same way as the reference code.
- EIP-7976's "requires" header omits EIP-3860.
- EIP-8024's opcodes are 0xe6, 0xe7, 0xe8 with modular and XOR immediate decoding; older descriptions circulate with different numbers.
- The gloas spec intro pins old EIP revisions: EIP-7732 described as "no EL changes", EIP-7843 with old engine method names, EIP-8282 with old prefix text.
- EIP-7997 has no spec code; the only rule is that clients must not check for the factory at the fork. All five EL clients comply.
- Payload attestation state source: the gossip rule (p2p L930) uses the head state while the fork-choice handler (L1150) uses the attested block's state. Three clients chose the block state for both on purpose. Worth a spec decision.
- `on_payload_attestation_message` first-valid semantics: consensus-specs issue 5333 is open on whether a validator may override its own PTC vote; the current text says IGNORE (item 1 relies on it).
- No deposit-request cap exists in Gloas (`ProgressiveList`); EIP-8254 (Draft) would add one. Item 6 depends on this remaining unscheduled.

## 6. Method, limits and what is missing

Method. One worker per client per EIP group read the client's code at the pin against the EIP text and the reference spec, listed every place its result could differ, compared with one other client, and searched that client's issues and PRs and the hive results. Two known-issue index items and three spec-list items seeded the others. Fifteen disproof items then took the strongest candidates and tried to refute each one, ending in confirmed, refuted or undecided. No candidate ended undecided.

What is missing or weak:

- Nothing was executed. No fixture was run, no node was started. Every "confirmed" verdict is two independent readings of the same code, not an observed result.
- Hive publishes no consensus-layer runs, so every CL candidate is checked against issues and PRs only.
- The Reth EIP-7928 hive failure (`bal_withdrawals_and_dequeues_net_balance_at_last_index`, expected VALID, got INVALID) was not traced to code and has no public report.
- Geth's two failing hive cases could not be named: the result JSON exceeded 10 MB and was not readable by the worker.
- Lodestar's `is_head_weak` fallback (item 12) lacks a full trace of the fallback balance source.
- Nimbus's `is_head_weak` boost handling is unproved either way.
- The Nimbus comparator for item 15 was not read.
- Besu's EVM v2 variants were not traced separately from the main EVM.
- Parallel execution paths (Nethermind BAL validation, Grandine mutator tasks) were read for ordering, not proven race-free.
- Line numbers for Reth (`el-stategas`) and Teku (`cl-state`) were reported by those workers with a tolerance of two to three lines; the line ranges cited in section 1 were re-checked against the raw files on 2026-10-27 and are exact at the pins given.
- Networking EIPs (7975, 8070, 8136, 8159, 8189) were out of scope.
- Six of the goal's 80 items were unspent; no batch was refused.
