Skip to content

builder_api: add Gloas Staked Builder API client - #819

Open
NikhilSharmaWe wants to merge 3 commits into
grandinetech:glamsterdam-devnet-7from
NikhilSharmaWe:gloas-builder-api-client
Open

builder_api: add Gloas Staked Builder API client#819
NikhilSharmaWe wants to merge 3 commits into
grandinetech:glamsterdam-devnet-7from
NikhilSharmaWe:gloas-builder-api-client

Conversation

@NikhilSharmaWe

@NikhilSharmaWe NikhilSharmaWe commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Part of #769. Client side of the Gloas Staked Builder API (ethereum/builder-specs#138), aligned with the clarifications in ethereum/builder-specs#165 (ACDT CL guidance: implement before merge).

Changes

  • Add DOMAIN_REQUEST_AUTH (0x0B000001) and the Gloas builder containers (RequestAuthV1, SignedRequestAuthV1, BuilderPreferencesV1, BuilderPreferencesRequestV1) to builder_api
  • Add three HTTP client methods:
    • get_execution_payload_bid — POST /eth/v1/builder/execution_payload_bid/{slot}/{parent_hash}/{parent_root}/{proposer_pubkey} with a required SignedRequestAuthV1 body (JSON or SSZ), required Date-Milliseconds and X-Timeout-Ms headers (timeout matches the reqwest timeout), and always Eth-Consensus-Version. 200 returns a bid, 204 means no bid. Gloas+ only; rejects earlier phases and auth.message.slot ≠ path slot before the request.
    • submit_builder_preferences — POST /eth/v1/builder/builder_preferences/{validator_pubkey} with BuilderPreferencesRequestV1 (SSZ field order: auth then preferences), expects 202
    • submit_signed_beacon_block — POST /eth/v1/builder/beacon_blocks, expects 202
  • Signing support for RequestAuthV1 (SignForAllForks, genesis fork + zero genesis validators root, per spec compute_domain(DOMAIN_REQUEST_AUTH))
  • Prometheus histograms for the three calls

Notes

Tests

  • cargo test -p builder_api --features blst — httpmock coverage for all three endpoints (200/204/400/500, required auth body + timing / consensus-version headers, auth slot mismatch, non-2xx handling, pre-Gloas rejection), DOMAIN_REQUEST_AUTH signing-root check, and SSZ field-order lock for BuilderPreferencesRequestV1

Signed-off-by: Nikhil Sharma <nikhilsharma230303@gmail.com>
Signed-off-by: Nikhil Sharma <nikhilsharma230303@gmail.com>
@NikhilSharmaWe

Copy link
Copy Markdown
Contributor Author

cc @hangleang

@hangleang

Copy link
Copy Markdown
Member

cc @hangleang

Thanks for the contributions, I was occupy by other urgent tasks so I won't be able to review within this week or two

@hangleang

Copy link
Copy Markdown
Member

FYI: clients have agreed to implement the spec PR ethereum/builder-specs#165 even it still unmerged (see https://forkcast.org/calls/acdt/090/?breakout=cl#t=70 on decisions), could you update the PR accordingly?

Signed-off-by: Nikhil Sharma <nikhilsharma230303@gmail.com>
@NikhilSharmaWe

NikhilSharmaWe commented Aug 6, 2026

Copy link
Copy Markdown
Contributor Author

@hangleang Updated with ethereum/builder-specs#165 as requested:

  • Required SignedRequestAuth on bid fetch
  • Required Date-Milliseconds and X-Timeout-Ms
  • Always send Eth-Consensus-Version
  • BuilderPreferencesRequest SSZ order: auth then preferences

Tests updated; cargo test -p builder_api --features blst passes.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just cosmetic, all new containers name has been removed the version suffix

/// [`MAX_DATA_SIZE`] from `builder-specs` (Gloas).
///
/// [`MAX_DATA_SIZE`]: https://github.com/ethereum/builder-specs/blob/main/specs/gloas/builder.md#constants
pub type MaxDataSize = U4096;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it should be put in consts.rs i believe

tag = "version",
content = "data"
)]
pub enum GetExecutionPayloadBidResponse<P: Preset> {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this response type and its implementations should be put in combined.rs

Comment thread builder_api/src/api.rs
.parse_gloas_response::<GetExecutionPayloadBidResponse<P>>(response)
.await?;

let bid = bid_response.into_bid();

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

returned bid isn't checked against request slot, parent_hash, parent_root as specified in https://github.com/ethereum/builder-specs/blob/main/specs/gloas/validator.md#validating-a-signedexecutionpayloadbid

Comment thread builder_api/src/api.rs
.builder_submit_builder_preferences_times
.start_timer()
});

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why won't check slot like get_execution_payload_bid does?

Comment thread builder_api/src/api.rs
})
}

async fn parse_gloas_response<T: DeserializeOwned + SszRead<Phase>>(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this function almost identical to parse_response below, can we reuse the function?

Comment thread builder_api/src/api.rs
let request = self
.client
.post(url.into_url())
.timeout(remaining_time)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this might be the wrong deadline for this endpoint. I guess this is copied from post_blinded_block pre-Gloas path which proposer require builder to response before attestation deadline, so it has the payload to construct and publish beacon block before deadline. but now builder is responsible to publish their payload, and this endpoint is just notify them to publish payload envelope by themselves through beacon node

Comment thread builder_api/src/api.rs
let request = self
.client
.post(url.into_url())
.timeout(REQUEST_TIMEOUT)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the interval timeout should be put on this endpoint instead, as now proposer request bid from builder to choose on proposal hot path

Comment thread builder_api/src/api.rs
.header(DATE_MS_HEADER, format!("{date_ms}"))
.header(
X_TIMEOUT_MS_HEADER,
format!("{}", REQUEST_TIMEOUT.as_millis()),

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here

Comment thread builder_api/src/api.rs
"/eth/v1/builder/execution_payload_bid/{slot}/{parent_hash:?}/{parent_root:?}/{pubkey:?}"
))?;

let use_json = self.config.builder_api_format == BuilderApiFormat::Json;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should have fallback to json format if builder doesn't support SSZ, same as other two endpoints

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants