Gloas: external builder API (payload bid selection and block production) - #9757
Gloas: external builder API (payload bid selection and block production)#9757ethDreamer wants to merge 13 commits into
Conversation
67d6620 to
9dd827f
Compare
…ad error variants
…uilder-Url round-trip
9dd827f to
a7ac4b7
Compare
|
Some required checks have failed. Could you please take a look @ethDreamer? 🙏 |
|
thanks for breaking it up into commits like this! im wondering if you would be willing to convert this into stacked PRs? We did this here: #9764 I think it makes reviewing big PRs much easier and hopefully more maintainable for the author. Lmk what you think |
| ```yaml | ||
| # Global bid policy: applies to p2p (gossip) bids, and is the default for any | ||
| # builder below that omits the corresponding field. | ||
| min_bid: 0 # gwei — reject any bid whose total payment is below this |
There was a problem hiding this comment.
🤖 automated (dapplion's agent): min_bid ranks as a tier. It does not reject. A bid below the floor still wins when it is the only candidate. The behavior is correct. This line and line 49 are wrong.
| .and(warp::path("validator")) | ||
| .and(warp::path("builder_preferences")) | ||
| .and(warp::path::end()) | ||
| .and(warp::header::<ForkName>(CONSENSUS_VERSION_HEADER)) |
There was a problem hiding this comment.
🤖 automated (dapplion's agent): This makes Eth-Consensus-Version required, but the comment above says the body is not fork-versioned and uses no such header. post_validator_blocks_v4 omits it. One of the two must change.
| /// viable option — the local build failed and every bid is under the floor — instead of missing | ||
| /// the slot. Whenever *any* candidate clears the floor (the local build always does), the | ||
| /// below-floor ones lose regardless of value, exactly as a hard filter would. | ||
| fn rank_key(&self) -> (bool, bool, Uint256, bool) { |
There was a problem hiding this comment.
🤖 automated (dapplion's agent): builder_boost_factor == u64::MAX no longer means always prefer the builder. A plain multiply ranks a zero-value bid at 0, so any non-zero local build wins. This differs from the pre-Gloas contract. Document it.
| .unwrap_or_else(|| definition.url.to_default_auth_data()); | ||
| // Omit any builder we cannot sign for, logging the error, rather than failing the | ||
| // whole config. | ||
| let auth = match sign(auth_data).await { |
There was a problem hiding this comment.
🤖 automated (dapplion's agent): This awaits one signature per builder, in sequence. On a cold cache this is up to 64 serial remote-signer round trips, on the proposal critical path. The signatures are independent, so use join_all.
| match response { | ||
| Ok(Some(GloasBidResponse { | ||
| bid, | ||
| ssz_response: _, |
There was a problem hiding this comment.
🤖 automated (dapplion's agent): ssz_response is dropped here and forward_signed_block hardcodes false, so every submission is JSON. The doc comment on GloasBidResponse says the opposite. Thread it through, or delete the field.
| direct.builder_boost_factor, | ||
| direct.max_execution_payment, | ||
| direct.min_bid, | ||
| direct.builder_url.expose_full().to_string(), |
There was a problem hiding this comment.
🤖 automated (dapplion's agent): expose_full() puts the full builder URL into a response header that travels BN to VC to BN in cleartext. An API key in the URL reaches every proxy log. Document that credentials belong in auth_data.
Issue Addressed
Closes #9590.
Adds support for the Gloas (ePBS) external builder API: the flow by which a proposer solicits
execution payload bids from off-protocol builders, ranks them against its local build and
gossip-relayed bids, commits to a winner, and forwards the signed block to the winning builder so it
reveals the execution payload envelope.
Implements the relevant parts of:
produceBlockV4, builder-preferences endpoint,Eth-Builder-UrlgetExecutionPayloadBid,submitBuilderPreferences,submitSignedBeaconBlock, request authExecutionPayloadBidProposed Changes
A Gloas proposer now considers three payload sources — its local EL build, gossip bids, and bids
fetched directly from configured builders — selects the most profitable eligible one, and (for a
direct builder) forwards the signed block back for envelope reveal.
The branch is organized as a bottom-up, dependency-ordered stack of commits so it can be reviewed one
layer at a time:
(
compute_domain(DOMAIN_REQUEST_AUTH)) for authenticating Gloas builder-API requests.request auth, builder URL).
produceBlockV4, submitting builder preferences,and the
Eth-Builder-Urlresponse/echo header.Buildersservice thatfans
getExecutionPayloadBid/submitBuilderPreferences/submitSignedBeaconBlockout acrossa proposer's configured builders. The pre-Gloas relay client is relocated here.
sharing the state-dependent consistency checks.
BidCandidate/BidSource { Local, Gossip, Direct }model andselect_payload_bid, wired into Gloas blockproduction. All bid value math lives on
BidCandidate, computed on demand: ranking is the trustedvalue scaled by
builder_boost_factor, in wei, so the local EL block value compares directly andbuilder_boost_factor == u64::MAX("always prefer") is a plain multiply.min_bidis a rankingtier — a below-floor bid wins when it's the only option (e.g. the local build failed) but loses to
any floor-clearing candidate. Direct-bid BLS verification runs on a blocking thread (off the async
executor), and gossip bids are re-validated against the production state at selection time so a bid
that has gone stale since gossip verification can't win selection and then fail block processing.
Buildersservice (honoring--builder-user-agent/--builder-disable-ssz) and wire it into the chain.produceBlockV4,POST /eth/v1/validator/builder_preferences, and theEth-Builder-Urlpublish round-trip. The winning builder's URL travels back to the VC as aresponse header and is echoed on publish, so forwarding the signed block works even when the
publishing beacon node isn't the one that produced it.
production / publish.
Additional Info
Best reviewed commit-by-commit: the commits form a bottom-up dependency layering (types → wire types
→ clients → beacon_chain → node / network / API → validator_client), each a coherent per-crate
change.