diff --git a/crates/common/src/api/mod.rs b/crates/common/src/api/mod.rs index b948321b..e28eeda3 100644 --- a/crates/common/src/api/mod.rs +++ b/crates/common/src/api/mod.rs @@ -31,8 +31,9 @@ pub const PATH_GET_HEADER: &str = "/header/{slot}/{parent_hash}/{pubkey}"; pub const PATH_HEADER_STREAM: &str = "/header_stream/{slot}/{parent_hash}/{pubkey}"; pub const PATH_GET_PAYLOAD: &str = "/blinded_blocks"; -// Gloas (ePBS) builder-API additions, per https://github.com/ethereum/builder-specs/pull/165. -// Not yet wired to the auctioneer -- see docs/gloas-support-plan.md. +// Gloas (ePBS) builder-API additions, per +// https://github.com/ethereum/builder-specs/blob/main/specs/gloas/builder.md. +// TODO(gloas): not yet wired to the auctioneer; see gattaca-com/helix#489. pub const PATH_GET_EXECUTION_PAYLOAD_BID: &str = "/execution_payload_bid/{slot}/{parent_hash}/{parent_root}/{proposer_pubkey}"; pub const PATH_SUBMIT_BUILDER_PREFERENCES: &str = "/builder_preferences/{proposer_pubkey}"; diff --git a/crates/common/src/chain_info.rs b/crates/common/src/chain_info.rs index eef457f9..e3234a5a 100644 --- a/crates/common/src/chain_info.rs +++ b/crates/common/src/chain_info.rs @@ -20,8 +20,8 @@ pub struct ChainInfo { pub clock: SlotClock, pub genesis_time_in_secs: u64, pub builder_domain: B256, - /// Domain for verifying Gloas builder-API `SignedRequestAuth` signatures. Not a consensus - /// domain; see `ChainSpec::get_request_auth_domain`. + /// Domain for verifying Gloas builder-API `SignedBuilderRequestAuth` signatures. Not a + /// consensus domain; see `ChainSpec::get_request_auth_domain`. pub request_auth_domain: B256, } diff --git a/crates/relay/src/api/proposer/error.rs b/crates/relay/src/api/proposer/error.rs index 4b831d1e..4064c867 100644 --- a/crates/relay/src/api/proposer/error.rs +++ b/crates/relay/src/api/proposer/error.rs @@ -125,12 +125,12 @@ pub enum ProposerApiError { SszDecodeError(DecodeError), // Gloas (ePBS) builder-API additions, per - // https://github.com/ethereum/builder-specs/pull/165. Not yet wired to the auctioneer. - #[error("invalid SignedRequestAuth: signature verification failed")] + // https://github.com/ethereum/builder-specs/blob/main/specs/gloas/builder.md. + #[error("invalid SignedBuilderRequestAuth: signature verification failed")] InvalidRequestAuthSignature, #[error( - "invalid SignedRequestAuth: auth.message.slot ({auth_slot}) does not match the request slot ({request_slot})" + "invalid SignedBuilderRequestAuth: auth.message.slot ({auth_slot}) does not match the request slot ({request_slot})" )] RequestAuthSlotMismatch { auth_slot: u64, request_slot: u64 }, diff --git a/crates/relay/src/api/proposer/get_execution_payload_bid.rs b/crates/relay/src/api/proposer/get_execution_payload_bid.rs index e0ca5fb9..d10bc454 100644 --- a/crates/relay/src/api/proposer/get_execution_payload_bid.rs +++ b/crates/relay/src/api/proposer/get_execution_payload_bid.rs @@ -9,7 +9,7 @@ use helix_common::{ decoder::Encoding, utils::extract_request_id, }; -use helix_types::{ForkName, SignedRequestAuth}; +use helix_types::{ForkName, SignedBuilderRequestAuth}; use hyper::StatusCode; use ssz::Decode; use tracing::info; @@ -19,11 +19,8 @@ use crate::api::{Api, proposer::error::ProposerApiError}; impl ProposerApi { /// Serves a `SignedExecutionPayloadBid` for the given slot/parent_hash/parent_root to a - /// Gloas (ePBS) proposer. - /// - /// Not yet wired to the auctioneer: this only validates the request per - /// and always answers with the - /// spec's "no bid available" response. + /// Gloas (ePBS) proposer, per + /// . #[tracing::instrument(skip_all, err(level = tracing::Level::TRACE), fields(id =% extract_request_id(&headers), slot = params.slot))] pub async fn get_execution_payload_bid( Extension(proposer_api): Extension>>, @@ -42,10 +39,11 @@ impl ProposerApi { return Err(ProposerApiError::MissingTimingHeaders); } - let signed_request_auth: SignedRequestAuth = match Encoding::from_content_type(&headers) { - Encoding::Json => serde_json::from_slice(&body)?, - Encoding::Ssz => SignedRequestAuth::from_ssz_bytes(&body)?, - }; + let signed_request_auth: SignedBuilderRequestAuth = + match Encoding::from_content_type(&headers) { + Encoding::Json => serde_json::from_slice(&body)?, + Encoding::Ssz => SignedBuilderRequestAuth::from_ssz_bytes(&body)?, + }; if signed_request_auth.message.slot != params.slot { return Err(ProposerApiError::RequestAuthSlotMismatch { @@ -67,8 +65,8 @@ impl ProposerApi { ); // TODO(gloas): fetch/build the SignedExecutionPayloadBid from the auctioneer, honoring - // any stored max_execution_payment preference. Not wired in yet -- always reports "no - // bid available", which is a valid response per spec. + // any stored max_execution_payment preference. Until then, "no bid available" is a + // valid response per spec. Ok(StatusCode::NO_CONTENT) } } diff --git a/crates/relay/src/api/proposer/submit_builder_preferences.rs b/crates/relay/src/api/proposer/submit_builder_preferences.rs index 57b6f417..f164932d 100644 --- a/crates/relay/src/api/proposer/submit_builder_preferences.rs +++ b/crates/relay/src/api/proposer/submit_builder_preferences.rs @@ -13,11 +13,8 @@ use super::{ProposerApi, get_payload::fork_name_from_header}; use crate::api::{Api, proposer::error::ProposerApiError}; impl ProposerApi { - /// Accepts a proposer's Gloas (ePBS) `BuilderPreferencesRequest`. - /// - /// Not yet wired in: this only validates the request per - /// ; preferences are not yet stored - /// or enforced when serving bids. + /// Accepts a proposer's Gloas (ePBS) `BuilderPreferencesRequest`, per + /// . #[tracing::instrument(skip_all, err(level = tracing::Level::TRACE), fields(id =% extract_request_id(&headers)))] pub async fn submit_builder_preferences( Extension(proposer_api): Extension>>, @@ -44,11 +41,11 @@ impl ProposerApi { proposer_pubkey = ?params.proposer_pubkey, slot = request.auth.message.slot, max_execution_payment = request.preferences.max_execution_payment, - "validated submitBuilderPreferences request (not yet persisted -- storage not wired in)" + "validated submitBuilderPreferences request (not yet persisted)" ); // TODO(gloas): reject stale slots, store preferences per proposer per slot, and honor - // max_execution_payment when serving bids. Not wired in yet. + // max_execution_payment when serving bids. Ok(StatusCode::ACCEPTED) } } diff --git a/crates/types/src/request_auth.rs b/crates/types/src/request_auth.rs index f903d0f4..046924e4 100644 --- a/crates/types/src/request_auth.rs +++ b/crates/types/src/request_auth.rs @@ -2,8 +2,7 @@ //! `getExecutionPayloadBid` and `submitBuilderPreferences` requests, and to carry a //! proposer's per-builder payment preferences. //! -//! See . Not yet part of any merged -//! spec, so field shapes may still change. +//! See . use alloy_primitives::B256; use lh_types::SignedRoot; @@ -23,7 +22,7 @@ crate::ssz_bytes_wrapper! { /// Authenticates a `getExecutionPayloadBid` or `submitBuilderPreferences` request. Signed /// under `DOMAIN_REQUEST_AUTH`, distinct from the in-protocol `DOMAIN_BEACON_BUILDER`. #[derive(PartialEq, Debug, Serialize, Deserialize, Clone, Encode, Decode, TreeHash)] -pub struct RequestAuth { +pub struct BuilderRequestAuth { /// Opaque authentication data agreed with the builder out of band. pub data: RequestAuthData, /// The proposal slot this request is authorized for. @@ -31,17 +30,17 @@ pub struct RequestAuth { pub slot: u64, } -impl SignedRoot for RequestAuth {} +impl SignedRoot for BuilderRequestAuth {} #[derive(PartialEq, Debug, Serialize, Deserialize, Clone, Encode, Decode)] -pub struct SignedRequestAuth { - pub message: RequestAuth, +pub struct SignedBuilderRequestAuth { + pub message: BuilderRequestAuth, pub signature: BlsSignatureBytes, } -impl SignedRequestAuth { +impl SignedBuilderRequestAuth { /// `pubkey` is resolved from the `proposer_pubkey` path parameter, not carried inside - /// `RequestAuth` itself. `domain` is `ChainInfo::request_auth_domain`. + /// `BuilderRequestAuth` itself. `domain` is `ChainInfo::request_auth_domain`. pub fn verify_signature( &self, pubkey: &BlsPublicKeyBytes, @@ -72,7 +71,7 @@ pub struct BuilderPreferences { #[derive(PartialEq, Debug, Serialize, Deserialize, Clone, Encode, Decode)] pub struct BuilderPreferencesRequest { pub preferences: BuilderPreferences, - pub auth: SignedRequestAuth, + pub auth: SignedBuilderRequestAuth, } #[cfg(test)] @@ -82,8 +81,8 @@ mod tests { use super::*; use crate::BlsKeypair; - fn sample_request_auth() -> RequestAuth { - RequestAuth { data: RequestAuthData(vec![1, 2, 3, 4].into()), slot: 123 } + fn sample_request_auth() -> BuilderRequestAuth { + BuilderRequestAuth { data: RequestAuthData(vec![1, 2, 3, 4].into()), slot: 123 } } #[test] @@ -97,14 +96,14 @@ mod tests { fn request_auth_ssz_round_trip() { let auth = sample_request_auth(); let bytes = auth.as_ssz_bytes(); - assert_eq!(auth, RequestAuth::from_ssz_bytes(&bytes).unwrap()); + assert_eq!(auth, BuilderRequestAuth::from_ssz_bytes(&bytes).unwrap()); } #[test] fn builder_preferences_request_json_round_trip() { let request = BuilderPreferencesRequest { preferences: BuilderPreferences { max_execution_payment: 42 }, - auth: SignedRequestAuth { + auth: SignedBuilderRequestAuth { message: sample_request_auth(), signature: BlsSignatureBytes::default(), }, @@ -121,7 +120,7 @@ mod tests { let root = message.signing_root(domain); let signature = keypair.sk.sign(root); - let signed = SignedRequestAuth { message, signature: signature.serialize().into() }; + let signed = SignedBuilderRequestAuth { message, signature: signature.serialize().into() }; let pubkey: BlsPublicKeyBytes = keypair.pk.serialize().into(); signed.verify_signature(&pubkey, domain).expect("valid signature should verify");