Skip to content

Commit 04ac47f

Browse files
authored
feat: support relay bid sequencing (#795)
## 📝 Summary Support relay bid sequencing via `x-sequence` header. Ref: - https://docs.ultrasound.money/builders/builder-getting-started#bid-sequencing - https://docs.titanrelay.xyz/builders/builder-integration#submission-headers ## ✅ I have completed the following steps: * [x] Run `make lint` * [x] Run `make test` * [ ] Added tests (if applicable)
1 parent 4e853d6 commit 04ac47f

File tree

4 files changed

+40
-13
lines changed

4 files changed

+40
-13
lines changed

crates/rbuilder-primitives/src/mev_boost/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,7 @@ pub struct BloxrouteRegionalEndpoint {
200200

201201
#[derive(Clone, Debug)]
202202
pub struct BidMetadata {
203+
pub sequence: u64,
203204
pub value: BidValueMetadata,
204205
pub order_ids: Vec<OrderId>,
205206
pub bundle_hashes: Vec<B256>,

crates/rbuilder-primitives/src/mev_boost/submit_header.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,22 @@
11
use crate::mev_boost::{
22
adjustment::BidAdjustmentDataV2,
33
ssz_roots::{calculate_transactions_root_ssz, calculate_withdrawals_root_ssz},
4+
BidMetadata,
45
};
56
use alloy_primitives::{Address, Bloom, Bytes, B256, U256};
67
use alloy_rpc_types_beacon::{relay::BidTrace, requests::ExecutionRequestsV4, BlsSignature};
78
use alloy_rpc_types_engine::ExecutionPayloadV3;
89
use serde_with::{serde_as, DisplayFromStr};
910

11+
/// Optimistic V3 bid submission with metadata.
12+
#[derive(Clone, Debug)]
13+
pub struct SubmitHeaderRequestWithMetadata {
14+
/// Header submission.
15+
pub submission: SubmitHeaderRequest,
16+
/// Bid metadata.
17+
pub metadata: BidMetadata,
18+
}
19+
1020
/// Optimistic V3 bid submission.
1121
#[derive(
1222
PartialEq,
@@ -18,7 +28,7 @@ use serde_with::{serde_as, DisplayFromStr};
1828
ssz_derive::Encode,
1929
ssz_derive::Decode,
2030
)]
21-
pub struct HeaderSubmissionOptimisticV3 {
31+
pub struct SubmitHeaderRequest {
2232
/// URL pointing to the builder's server endpoint for retrieving
2333
/// the full block payload if this header is selected.
2434
pub url: Vec<u8>,

crates/rbuilder/src/live_builder/block_output/relay_submit.rs

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,14 @@ use rbuilder_primitives::{
2626
built_block::{block_to_execution_payload, SignedBuiltBlock},
2727
mev_boost::{
2828
BidAdjustmentData, BidMetadata, BidValueMetadata, ExecutionPayloadHeaderElectra,
29-
HeaderSubmission, HeaderSubmissionElectra, HeaderSubmissionOptimisticV3, MevBoostRelayID,
30-
SignedHeaderSubmission, SubmitBlockRequest, SubmitBlockRequestWithMetadata,
31-
ValidatorSlotData,
29+
HeaderSubmission, HeaderSubmissionElectra, MevBoostRelayID, SignedHeaderSubmission,
30+
SubmitBlockRequest, SubmitBlockRequestWithMetadata, SubmitHeaderRequest,
31+
SubmitHeaderRequestWithMetadata, ValidatorSlotData,
3232
},
3333
};
3434
use reth_chainspec::ChainSpec;
3535
use std::sync::Arc;
36+
use time::OffsetDateTime;
3637
use tokio::{
3738
sync::{broadcast, Notify},
3839
time::Instant,
@@ -221,12 +222,15 @@ async fn run_submit_to_relays_job(
221222
}
222223
});
223224

225+
// SAFETY: UNIX timestamp in nanos won't exceed u64::MAX until year 2554
226+
let sequence = OffsetDateTime::now_utc().unix_timestamp_nanos() as u64;
224227
let executed_orders = block
225228
.trace
226229
.included_orders
227230
.iter()
228231
.flat_map(|exec_res| exec_res.order.original_orders());
229232
let bid_metadata = BidMetadata {
233+
sequence,
230234
value: BidValueMetadata {
231235
coinbase_reward: block.trace.coinbase_reward,
232236
top_competitor_bid: block.trace.seen_competition_bid,
@@ -403,7 +407,7 @@ fn create_optimistic_v3_request(
403407
request: &AlloySubmitBlockRequest,
404408
maybe_adjustment_data: Option<&BidAdjustmentData>,
405409
adjustment_data_required: bool,
406-
) -> eyre::Result<HeaderSubmissionOptimisticV3> {
410+
) -> eyre::Result<SubmitHeaderRequest> {
407411
let maybe_adjustment_data_v2 = maybe_adjustment_data.map(|d| d.clone().into_v2());
408412
if maybe_adjustment_data_v2.is_none() && adjustment_data_required {
409413
eyre::bail!("adjustment data is required")
@@ -455,7 +459,7 @@ fn create_optimistic_v3_request(
455459
_ => eyre::bail!("optimistic v3 submission is not supported for this fork"),
456460
};
457461

458-
Ok(HeaderSubmissionOptimisticV3 {
462+
Ok(SubmitHeaderRequest {
459463
url: builder_url.to_vec(),
460464
tx_count: tx_count as u32,
461465
submission,
@@ -503,7 +507,10 @@ fn submit_block_to_relays(
503507
maybe_adjustment_data,
504508
relay.optimistic_v3_bid_adjustment_required(),
505509
)
506-
.map(|request| (config.clone(), request))
510+
.map(|request| (config.clone(), SubmitHeaderRequestWithMetadata {
511+
submission: request,
512+
metadata: bid_metadata.clone()
513+
}))
507514
.inspect_err(|error| {
508515
error!(parent: submission_span, ?error, "Unable to create optimistic V3 request");
509516
})
@@ -547,7 +554,7 @@ fn submit_block_to_relays(
547554
async fn submit_bid_to_the_relay(
548555
relay: &MevBoostRelayBidSubmitter,
549556
submit_block_request: SubmitBlockRequestWithMetadata,
550-
optimistic_v3_request: Option<(OptimisticV3Config, HeaderSubmissionOptimisticV3)>,
557+
optimistic_v3_request: Option<(OptimisticV3Config, SubmitHeaderRequestWithMetadata)>,
551558
registration: ValidatorSlotData,
552559
optimistic: bool,
553560
cancel: CancellationToken,

crates/rbuilder/src/mev_boost/mod.rs

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ use alloy_rpc_types_beacon::BlsPublicKey;
44
use flate2::{write::GzEncoder, Compression};
55
use governor::{DefaultDirectRateLimiter, Quota, RateLimiter};
66
use rbuilder_primitives::mev_boost::{
7-
HeaderSubmissionOptimisticV3, KnownRelay, MevBoostRelayID, RelayMode,
8-
SubmitBlockRequestNoBlobs, SubmitBlockRequestWithMetadata, ValidatorRegistration,
7+
KnownRelay, MevBoostRelayID, RelayMode, SubmitBlockRequestNoBlobs,
8+
SubmitBlockRequestWithMetadata, SubmitHeaderRequestWithMetadata, ValidatorRegistration,
99
ValidatorSlotData, MEV_BOOST_SLOT_INFO_REQUEST_TIMEOUT,
1010
};
1111
use reqwest::{
@@ -35,6 +35,7 @@ const BUNDLE_HASHES_HEADER: &str = "Bundle-Hashes";
3535
const TOP_BID_HEADER: &str = "Top-Bid";
3636
const BLOXROUTE_SHARE_HEADER: &str = "share";
3737
const BLOXROUTE_BUILDER_VALUE_HEADER: &str = "builder-value";
38+
const X_SEQUENCE_HEADER: &str = "x-sequence";
3839

3940
const JSON_CONTENT_TYPE: &str = "application/json";
4041
const SSZ_CONTENT_TYPE: &str = "application/octet-stream";
@@ -343,7 +344,7 @@ impl MevBoostRelayBidSubmitter {
343344

344345
pub async fn submit_optimistic_v3(
345346
&self,
346-
data: HeaderSubmissionOptimisticV3,
347+
data: SubmitHeaderRequestWithMetadata,
347348
registration: ValidatorSlotData,
348349
) -> Result<(), SubmitBlockErr> {
349350
self.client
@@ -702,6 +703,7 @@ impl RelayClient {
702703
} = submission_with_metadata;
703704

704705
let mut headers = HeaderMap::new();
706+
headers.insert(X_SEQUENCE_HEADER, metadata.sequence.into());
705707
self.add_auth_headers(&mut headers)
706708
.map_err(|_| SubmitBlockErr::InvalidHeader)?;
707709

@@ -899,11 +901,17 @@ impl RelayClient {
899901

900902
pub async fn submit_optimistic_v3(
901903
&self,
902-
request: &HeaderSubmissionOptimisticV3,
904+
request: &SubmitHeaderRequestWithMetadata,
903905
registration: &ValidatorSlotData,
904906
cancellations: bool,
905907
) -> Result<(), SubmitBlockErr> {
908+
let SubmitHeaderRequestWithMetadata {
909+
submission,
910+
metadata,
911+
} = request;
912+
906913
let mut headers = HeaderMap::new();
914+
headers.insert(X_SEQUENCE_HEADER, metadata.sequence.into());
907915
self.add_auth_headers(&mut headers)
908916
.map_err(|_| SubmitBlockErr::InvalidHeader)?;
909917

@@ -912,7 +920,7 @@ impl RelayClient {
912920
url.query_pairs_mut()
913921
.append_pair("cancellations", if cancellations { "1" } else { "0" });
914922

915-
let body = request.as_ssz_bytes();
923+
let body = submission.as_ssz_bytes();
916924
headers.insert(CONTENT_TYPE, HeaderValue::from_static(SSZ_CONTENT_TYPE));
917925

918926
let response = self
@@ -1284,6 +1292,7 @@ mod tests {
12841292
let sub_relay = SubmitBlockRequestWithMetadata {
12851293
submission,
12861294
metadata: BidMetadata {
1295+
sequence: 0,
12871296
value: BidValueMetadata {
12881297
coinbase_reward: Default::default(),
12891298
top_competitor_bid: None,

0 commit comments

Comments
 (0)