From 73b84bb8224cf54eb4ce8fe6b7b3663198c2b72f Mon Sep 17 00:00:00 2001 From: Barnabas Busa Date: Wed, 22 Apr 2026 14:00:35 +0200 Subject: [PATCH] miner: supply a slot number when synthesising pending block post-Amsterdam getPending builds the pending block on demand via generateWork, but after EIP-7843 (#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. --- miner/miner.go | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/miner/miner.go b/miner/miner.go index 0ff0237a08..921200bcaa 100644 --- a/miner/miner.go +++ b/miner/miner.go @@ -151,12 +151,24 @@ func (miner *Miner) getPending() *newPayloadResult { return cached } 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{} } + // Post-Amsterdam, prepareWork requires a slot number (EIP-7843). The pending + // block is synthetic and has no canonical slot, so derive one from the parent + // when available and fall back to zero otherwise. + 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{ timestamp: timestamp, @@ -166,6 +178,7 @@ func (miner *Miner) getPending() *newPayloadResult { random: common.Hash{}, withdrawals: withdrawal, beaconRoot: nil, + slotNum: slotNum, noTxs: false, }, false) // we will never make a witness for a pending block if ret.err != nil {