Skip to content

miner: supply a slot number when synthesising pending block post-Amsterdam - #34792

Merged
fjl merged 1 commit into
ethereum:masterfrom
barnabasbusa:bbusa/pending-supply-slotnum-master
May 28, 2026
Merged

miner: supply a slot number when synthesising pending block post-Amsterdam#34792
fjl merged 1 commit into
ethereum:masterfrom
barnabasbusa:bbusa/pending-supply-slotnum-master

Conversation

@barnabasbusa

Copy link
Copy Markdown
Member

Summary

miner.getPending() builds the pending block on demand via generateWork. After EIP-7843 landed in #33589, prepareWork rejects the call unless generateParams.slotNum is non-nil once Amsterdam is active:

// miner/worker.go
if miner.chainConfig.IsAmsterdam(header.Number, header.Time) {
    if genParams.slotNum == nil {
        return nil, errors.New("no slot number set post-amsterdam")
    }
    header.SlotNumber = genParams.slotNum
}

getPending was never updated to populate slotNum, so on any Amsterdam-activated chain every RPC that resolves through the pending state will fail:

eth_getBalance(addr, "pending")          -> -32000 "pending state is not available"
eth_call({...}, "pending")               -> -32000 "pending state is not available"
eth_getBlockByNumber("pending", ...)     -> -32000 "pending block is not available"

This breaks faucets, nonce trackers, gas estimators, simulators — anything polling pending.

Why this is latent on master today

Amsterdam isn't activated on any production chain yet, so IsAmsterdam returns false and the guard is skipped. The bug is sitting there waiting to bite every Geth deployment the moment Amsterdam is turned on. Already visible on bal-devnet-3 and glamsterdam-devnet-0 devnet builds.

Fix

Mirror the existing Shanghai branch above: when Amsterdam is active for the next block, synthesise a slot number from the parent header (header.SlotNumber + 1, falling back to 0 when unset) and pass it through generateParams. The pending block is empty post-merge (see #28440), so the exact slot value is not user-observable — it just has to satisfy prepareWork's invariant.

 	var (
-		timestamp  = uint64(time.Now().Unix())
-		withdrawal types.Withdrawals
+		timestamp   = uint64(time.Now().Unix())
+		childNumber = new(big.Int).Add(header.Number, big.NewInt(1))
+		withdrawal  types.Withdrawals
+		slotNum     *uint64
 	)
-	if miner.chainConfig.IsShanghai(new(big.Int).Add(header.Number, big.NewInt(1)), timestamp) {
+	if miner.chainConfig.IsShanghai(childNumber, timestamp) {
 		withdrawal = []*types.Withdrawal{}
 	}
+	if miner.chainConfig.IsAmsterdam(childNumber, timestamp) {
+		var n uint64
+		if header.SlotNumber != nil {
+			n = *header.SlotNumber + 1
+		}
+		slotNum = &n
+	}
 	ret := miner.generateWork(context.Background(),
 		&generateParams{
 			...
 			beaconRoot:  nil,
+			slotNum:     slotNum,
 			noTxs:       false,
 		}, false)

+16/-3 in a single file.

Reproduction & verification

Reproduced on a Kurtosis enclave running two EL nodes on the same Amsterdam-active chain (block 0x12402, slot 0x1870e):

Call unpatched c30c846c patched (this PR)
eth_getBalance(addr, "latest") 0xb37ea69 0xb37ea69
eth_getBalance(addr, "pending") -32000 "pending state is not available" 0xb37ea69
eth_call({...}, "pending") -32000 "pending state is not available" 0x
eth_getBlockByNumber("pending", ...) -32000 "pending block is not available" full block
eth_getTransactionCount(addr, "pending") 0x0 (txpool path) 0x0

Balances at latest identical on both nodes, confirming this is a pure fallback-path fix, not papering over state divergence.

Other ELs on the same chain (Nethermind, Besu, Reth, Erigon, Ethrex) serve pending without issue — Nethermind aliases pending → head (BlockTree.PendingHash => Head?.Hash), so its engine-API slotnum enforcement is never reached from RPC.

Test plan

  • go build ./miner/...
  • go test ./miner/... (existing suite passes)
  • go vet ./miner/...
  • End-to-end verification in Kurtosis against Amsterdam-active chain (see table above)
  • Add a unit test for getPending under Amsterdam — happy to follow up if reviewers want one; the existing miner_test.go doesn't currently exercise pending post-fork and I didn't want to balloon the diff

Companion PRs

If preferred, those two can be dropped in favour of just merging this one and letting the devnet branches pull master.

Blame

Introduced by #33589 (EIP-7843 SLOTNUM, merged 2026-02-26). That PR added the slotNum == nil → error guard in prepareWork but didn't touch miner/miner.go, leaving getPending as the only caller that doesn't supply a slot.

…erdam

getPending builds the pending block on demand via generateWork, but after
EIP-7843 (ethereum#33589) prepareWork rejects the call unless generateParams.slotNum
is non-nil once Amsterdam is active:

    if miner.chainConfig.IsAmsterdam(header.Number, header.Time) {
        if genParams.slotNum == nil {
            return nil, errors.New("no slot number set post-amsterdam")
        }
        header.SlotNumber = genParams.slotNum
    }

getPending never populated slotNum, so on any Amsterdam-activated chain
eth_getBalance(addr, "pending") (and every other RPC that resolves through
the pending state) fails with "pending state is not available", breaking
faucets and nonce trackers that poll pending.

Fix by synthesising a slot number from the parent header when available,
mirroring how the Shanghai branch above already conditionally populates
withdrawals. The pending block is empty post-merge so the exact slot value
is not user-visible; SlotNumber+1 (or zero) is sufficient to satisfy the
prepareWork invariant.

Observed on a bal-devnet-3 Geth build: every eth_getBalance(...,"pending")
returned -32000 "pending state is not available" until the faucet was
repointed at a non-Geth EL. Other clients (Nethermind, Besu, Reth, Erigon,
Ethrex) serve pending on the same chain without issue.
@barnabasbusa

Copy link
Copy Markdown
Member Author

@fjl

1 similar comment
@barnabasbusa

Copy link
Copy Markdown
Member Author

@fjl

@fjl fjl added the amsterdam label May 28, 2026
@fjl fjl added this to the 1.17.4 milestone May 28, 2026
@fjl
fjl merged commit 95320ff into ethereum:master May 28, 2026
9 of 10 checks passed
0xjvn pushed a commit to 0xjvn/go-ethereum that referenced this pull request Jun 5, 2026
gballet pushed a commit to gballet/go-ethereum that referenced this pull request Jun 12, 2026
AdityaSripal pushed a commit to AdityaSripal/go-ethereum that referenced this pull request Jun 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants