Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 78 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions crates/rbuilder-operator/src/flashbots_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@ impl LiveBuilderConfig for FlashbotsConfig {
bid_observer,
bidding_service.clone(),
cancellation_token.clone(),
None, // No EPBS block observer in flashbots config
)
.await?;

Expand Down
108 changes: 108 additions & 0 deletions crates/rbuilder-primitives/src/epbs/bid.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
//! ExecutionPayloadBid types for EPBS.
//!
//! These types represent the builder's commitment to produce an execution payload.
//! See: https://github.com/ethereum/consensus-specs/blob/master/specs/gloas/builder.md

use alloy_primitives::{Address, BlockHash, B256};
use alloy_rpc_types_beacon::BlsSignature;
use serde::{Deserialize, Serialize};
use serde_with::{serde_as, DisplayFromStr};

/// Signing domain for EPBS builder bids.
/// From consensus-specs/specs/gloas/beacon-chain.md:
/// | DOMAIN_BEACON_BUILDER | DomainType('0x0B000000') |
pub const DOMAIN_BEACON_BUILDER: [u8; 4] = [0x0B, 0x00, 0x00, 0x00];

/// from consensus-specs/specs/gloas/beacon-chain.md:
#[serde_as]
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct ExecutionPayloadBid {
/// hash of the current head of execution chain
pub parent_block_hash: BlockHash,
/// hash tree root of the beacon block the proposer will build on
pub parent_block_root: B256,
/// this is the blockhash which the builder constructed the payload
pub block_hash: BlockHash,
/// previous RANDAO of the constructed payload
pub prev_randao: B256,
/// execution address to receive the payment
pub fee_recipient: Address,
/// gas limit of the constructed payload
#[serde_as(as = "DisplayFromStr")]
pub gas_limit: u64,
/// validator index of the builder performing these actions.
#[serde_as(as = "DisplayFromStr")]
pub builder_index: u64,
/// to be the slot for which this bid is aimed.
#[serde_as(as = "DisplayFromStr")]
pub slot: u64,
/// to be the value (in gwei) that the builder will pay the proposer if the bid is accepted
#[serde_as(as = "DisplayFromStr")]
pub value: u64,
/// must be zero for in protocol payments. non-zero only if proposer accepts trusted payments
#[serde_as(as = "DisplayFromStr")]
pub execution_payment: u64,
/// hash tree root of the blob KZG commitments.
pub blob_kzg_commitments_root: B256,
}

impl ExecutionPayloadBid {
/// Returns the total payment to the proposer (value + execution_payment).
pub fn total_value(&self) -> u64 {
self.value.saturating_add(self.execution_payment)
}

/// Returns true if this bid uses only in-protocol (beacon chain) payment.
pub fn is_in_protocol_payment(&self) -> bool {
self.execution_payment == 0
}
}

/// SignedExecutionPayloadBid is a signed commitment from a builder.
///
/// signature is created using the builder's validator key and the
/// DOMAIN_BEACON_BUILDER domain.
///
/// from consensus-specs/specs/gloas/beacon-chain.md:

#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct SignedExecutionPayloadBid {
/// execution payload
pub message: ExecutionPayloadBid,
/// bls signature over the bid using the builder's validator key.
pub signature: BlsSignature,
}

/// resp for get_bid endpoint.
///
/// This follows the Builder API spec for EPBS:
/// GET /eth/v1/builder/execution_payload_bid/{slot}/{parent_hash}/{parent_root}/{proposer_index}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GetExecutionPayloadBidResponse {
/// The fork version, e.g., "gloas".
pub version: String,
/// signed bid using validator signature
pub data: SignedExecutionPayloadBid,
}

/// the params are for the get_bid endpoint following the builder-sepc
#[derive(Debug, Clone)]
pub struct GetBidParams {
/// slot for which the bid is being considered for
pub slot: u64,
/// hash of the parent block the proposer will upon
pub parent_hash: BlockHash,
/// root of the parent block the proposer will build upon
pub parent_root: B256,
/// to be reitrved from the path params
pub proposer_index: u64,
/// address from the X-Fee-Recipient header
pub fee_recipient: Address,
/// timeout ms for request via X-Timeout-Ms header
pub timeout_ms: Option<u64>,
/// timestamp from Date-Milliseconds header for latency measurement
pub date_milliseconds: Option<u64>,
}



129 changes: 129 additions & 0 deletions crates/rbuilder-primitives/src/epbs/envelope.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
//! ExecutionPayloadEnvelope types for EPBS.
//!
//! These types represent the full execution payload that the builder reveals
//! after their bid is included in a beacon block.
//! See: https://github.com/ethereum/consensus-specs/blob/master/specs/gloas/builder.md

use alloy_primitives::{Bytes, B256};
use alloy_rpc_types_beacon::BlsSignature;
use alloy_rpc_types_engine::ExecutionPayloadV3;
use serde::{Deserialize, Serialize};
use serde_with::{serde_as, DisplayFromStr};

/// ExecutionPayloadEnvelope contains the full execution payload and associated data.
///
/// This is revealed by the builder after their SignedExecutionPayloadBid is included
/// in a beacon block. The envelope is broadcast on the `execution_payload` P2P topic.
///
/// From consensus-specs/specs/gloas/beacon-chain.md:

#[serde_as]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ExecutionPayloadEnvelope {
/// The full execution payload.
/// TODO: This should be the Gloas-specific ExecutionPayload when available in Alloy.
pub payload: ExecutionPayloadV3,
/// Execution requests (deposits, withdrawals, consolidations).
/// TODO: Use proper ExecutionRequests type from Alloy when available.
pub execution_requests: ExecutionRequests,
/// Validator index of the builder.
#[serde_as(as = "DisplayFromStr")]
pub builder_index: u64,
/// Hash tree root of the beacon block that included this builder's bid.
pub beacon_block_root: B256,
/// Slot of the beacon block.
#[serde_as(as = "DisplayFromStr")]
pub slot: u64,
/// Blob KZG commitments for this payload.
/// The hash_tree_root of this must match blob_kzg_commitments_root in the bid.
pub blob_kzg_commitments: Vec<Bytes>,
/// State root after applying the execution payload.
pub state_root: B256,
}

/// Placeholder for ExecutionRequests until available in Alloy.
/// TODO: Replace with alloy_rpc_types_beacon::requests::ExecutionRequestsV4 or equivalent.
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct ExecutionRequests {
/// Deposit requests from the execution layer.
#[serde(default)]
pub deposits: Vec<Bytes>,
/// Withdrawal requests from the execution layer.
#[serde(default)]
pub withdrawals: Vec<Bytes>,
/// Consolidation requests from the execution layer.
#[serde(default)]
pub consolidations: Vec<Bytes>,
}

/// SignedExecutionPayloadEnvelope is the envelope signed by the builder.
///
/// From consensus-specs/specs/gloas/beacon-chain.md:
/// ```python
/// class SignedExecutionPayloadEnvelope(Container):
/// message: ExecutionPayloadEnvelope
/// signature: BLSSignature
/// ```
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SignedExecutionPayloadEnvelope {
/// The execution payload envelope message.
pub message: ExecutionPayloadEnvelope,
/// BLS signature over the envelope using the builder's validator key.
pub signature: BlsSignature,
}

/// Cached payload data stored by the builder after creating a bid.
///
/// When a builder creates an ExecutionPayloadBid, they must store the full
/// payload data so they can reveal it when/if their bid is accepted.
#[derive(Debug, Clone)]
pub struct CachedPayloadData {
/// The signed bid that was broadcast/returned.
pub bid: super::SignedExecutionPayloadBid,
/// The full execution payload (to be revealed later).
pub payload: ExecutionPayloadV3,
/// Execution requests associated with the payload.
pub execution_requests: ExecutionRequests,
/// Blob KZG commitments.
pub blob_kzg_commitments: Vec<Bytes>,
/// Timestamp when this cache entry was created.
pub created_at: std::time::Instant,
}

impl CachedPayloadData {
/// Creates a new cached payload entry.
pub fn new(
bid: super::SignedExecutionPayloadBid,
payload: ExecutionPayloadV3,
execution_requests: ExecutionRequests,
blob_kzg_commitments: Vec<Bytes>,
) -> Self {
Self {
bid,
payload,
execution_requests,
blob_kzg_commitments,
created_at: std::time::Instant::now(),
}
}

/// Build the envelope from cached data and the beacon block info.
pub fn build_envelope(
&self,
beacon_block_root: B256,
state_root: B256,
) -> ExecutionPayloadEnvelope {
ExecutionPayloadEnvelope {
payload: self.payload.clone(),
execution_requests: self.execution_requests.clone(),
builder_index: self.bid.message.builder_index,
beacon_block_root,
slot: self.bid.message.slot,
blob_kzg_commitments: self.blob_kzg_commitments.clone(),
state_root,
}
}
}



Loading