Skip to content

Gloas: builder bids validated after the one-shot bid-pool read — ~20% of proposals fall back to self-build #9741

Description

@qu0b

Summary

On glamsterdam-devnet-7 (Gloas/ePBS, v1.44.0/94954a2, glamsterdam-devnet-7 branch), lodestar proposers fall back to self-building on ~20% of proposals even though a correctly-keyed builder bid was on the wire ~390 ms before slot start. The cause is measurable end-to-end: the bid's gossip validation frequently completes after produceBlockV4 has already done its single, synchronous bid-pool read at slot start. The bid loses a race it was never given a chance to re-enter.

Full write-ups (interactive charts, per-slot evidence, reproduction queries):

Mechanism (at 94954a2)

  1. produceBlockV4 runs at slot start and calls executionPayloadBidPool.getBestBid(slot, bidParentBlockHash, parentBlockRootHex) once — a synchronous exact-triple map read with no wait, no re-check, and no pool-insert wake-up. null → self-build (there is no value comparison yet; the TODO GLOAS: respect builderSelection… above it).
  2. The only path into that pool is validateGossipExecutionPayloadBid, which includes a per-bid chain.regen.getBlockSlotState(..., {dontTransferCache: true}) plus BLS verification.
  3. buildoor (the devnet builder) publishes its first bid at −400 ms and its last at −100 ms before slot start (its defaults), so the entire budget between bid arrival and the pool read is ~390 ms — all of which is consumed by queueing + validation when the tail bites.

Evidence

Bid pipeline latency per implementation — each node's own beacon-event emission (fires post-validation) minus the earliest emission network-wide for the same bid; same bids, same mesh, 2.58 M observations, 2026-07-22 → 07-31:

CL p50 p90 p99
prysm / grandine / lighthouse / teku / nimbus 4–7 ms 8–13 ms 29–85 ms
lodestar 22 ms 751 ms 1.36 s

The distribution is bimodal: a fast mode like every other client, plus a second population (~16% of bids) at 400–1,500 ms — a hump, not a tail, suggesting discrete stalls (event-loop pause / queue behind an expensive item / regen miss) rather than uniformly slower validation.

Per-slot causality on 7,850 lodestar proposals where the proposing node's own bid timeline is measurable:

outcome own validation of the bid (median) n
bid taken −365 ms (before slot start) 6,461 — 1 late
self-built +510 ms (after the decision; p90 +1,034 ms) 1,389 — 1,267 late

The daily fraction of late-validated bids tracks the daily self-build rate almost 1:1 (e.g. 24.1%/20.8%, 28.0%/21.3%, 24.0%/25.0%). The excess on 07-23 is the BuilderCircuitBreaker (working as intended during a payload non-reveal incident). A node restart on 07-28 did not reset the latency, so it is workload-shaped, not accumulated process state; what fattened the tail from 07-25 onward is still open and likely needs your internal gossip-queue/regen metrics.

Impact

On the devnet this is benign (the self-built fallback works and reveals early — every one of those payloads landed). But with the current one-shot read, a validator forfeits the builder bid on every slot the race is lost — the bids left on the table in this window were worth ~2× the local block. It also means lodestar's effective bid acceptance depends on its own validation latency, which seems unintended.

Possible fixes (any one would eliminate the effect)

  • Re-check the pool (or wait briefly / wake on pool insert) before committing to the local payload in produceBlockV4 — even a bounded ~500 ms grace re-read would recover nearly all of the 400–1,500 ms hump.
  • Get the per-bid state regen out of the gossip-validation hot path (cache the prepared parent-branch state for the expected next slot, as bid validation for slot N almost always needs the same state).
  • A metric on bid gossip-validation queue+processing time would make the tail visible without external tooling.

Happy to provide raw data, per-slot samples, or run instrumented builds on the devnet.

Metadata

Metadata

Assignees

No one assigned

    Labels

    spec-gloasIssues targeting the Glamsterdam spec version

    Type

    No type

    Projects

    Status
    In Progress

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions