Skip to content
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
9a19c1d
add builder server
faheelsattar Jan 8, 2026
ad63d2b
upd name
faheelsattar Jan 26, 2026
cfe43cd
add proposer prefs, adjust bids and payload envelope acc to spec
faheelsattar Mar 27, 2026
fc32161
fix ssz signing for bid and envelope
faheelsattar Mar 27, 2026
50d8e41
add epbs beacon api endpoint and sse topics
faheelsattar Mar 27, 2026
3163043
refactor bid provider
faheelsattar Mar 27, 2026
4548c73
add p2p epbs builder service
faheelsattar Mar 27, 2026
f809360
wire to live_builder
faheelsattar Mar 27, 2026
3225d01
clean
faheelsattar Mar 27, 2026
f75f9ae
bump rust toolchain
faheelsattar May 8, 2026
5af3514
pin reth version + lh consensus types
faheelsattar May 8, 2026
d91f56f
adapt to reth changes
faheelsattar May 8, 2026
b8e27f0
reth changes
faheelsattar May 8, 2026
a131683
ssz signing via lh consensus types
faheelsattar May 8, 2026
413e372
add beacon api client endpoints
faheelsattar May 8, 2026
761c64b
fix p2p bid pipeline
faheelsattar May 8, 2026
515c613
reveal pipeline
faheelsattar May 9, 2026
3af53ce
clean
faheelsattar May 9, 2026
0ecd82a
Merge pull request #4 from faheelsattar/feat/buildder-server-epbs-lh
faheelsattar May 9, 2026
9ef8866
glam devnet setup and updates
faheelsattar May 15, 2026
c4449b3
serialize envelope payload in beacon api format
faheelsattar May 15, 2026
9873d4c
eip-8037 gas accounting
faheelsattar May 15, 2026
0ec18ca
eip-7928 BAL changes
faheelsattar May 15, 2026
cda5d5b
clean
faheelsattar May 15, 2026
f7481c7
update execution payload submission timeout
faheelsattar May 19, 2026
ef13ce6
fix check for finalization epoch
faheelsattar May 19, 2026
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