Skip to content

Commit d96e721

Browse files
authored
Relay submit improvements (#223)
## 📝 Summary Removed the sleep at the beginning of the relay submit loop since this is the responsibility of the bidding module. Replaced a 5ms polling for an await to improve bidding latency. ## 💡 Motivation and Context New root hash speed makes this 5ms latency important. --- ## ✅ I have completed the following steps: * [X] Run `make lint` * [X] Run `make test` * [ ] Added tests (if applicable)
1 parent ef24d9e commit d96e721

File tree

5 files changed

+42
-71
lines changed

5 files changed

+42
-71
lines changed

config-live-example.toml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,6 @@ max_concurrent_seals = 4
3232
# genesis_fork_version = "0x00112233"
3333

3434
sbundle_mergeabe_signers = []
35-
# slot_delta_to_start_submits_ms is usually negative since we start bidding BEFORE the slot start
36-
# slot_delta_to_start_submits_ms = -5000
3735
live_builders = ["mp-ordering", "mgp-ordering", "merging"]
3836

3937
[[relays]]

config-playground.toml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,6 @@ dry_run_validation_url = "http://localhost:8545"
2525
ignore_cancellable_orders = true
2626

2727
sbundle_mergeabe_signers = []
28-
# slot_delta_to_start_submits_ms is usually negative since we start bidding BEFORE the slot start
29-
# slot_delta_to_start_submits_ms = -5000
3028
live_builders = ["mp-ordering"]
3129

3230
[[relays]]

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

Lines changed: 41 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,9 @@ use alloy_primitives::{utils::format_ether, U256};
1919
use mockall::automock;
2020
use reth_chainspec::ChainSpec;
2121
use reth_primitives::SealedBlock;
22-
use std::{
23-
sync::{Arc, Mutex},
24-
time::Duration,
25-
};
26-
use tokio::time::{sleep, Instant};
22+
use std::sync::{Arc, Mutex};
23+
use tokio::sync::Notify;
24+
use tokio::time::Instant;
2725
use tokio_util::sync::CancellationToken;
2826
use tracing::{debug, error, event, info_span, trace, warn, Instrument, Level};
2927

@@ -36,40 +34,45 @@ const SIM_ERROR_CATEGORY: &str = "submit_block_simulation";
3634
const VALIDATION_ERROR_CATEGORY: &str = "validate_block_simulation";
3735

3836
/// Contains the best block so far.
39-
/// Building updates via compare_and_update while relay submitter polls via take_best_block
40-
#[derive(Debug, Clone)]
37+
/// Building updates via compare_and_update while relay submitter polls via take_best_block.
38+
/// A new block can be waited without polling via wait_for_change.
39+
#[derive(Debug, Default)]
4140
pub struct BestBlockCell {
42-
val: Arc<Mutex<Option<Block>>>,
43-
}
44-
45-
impl Default for BestBlockCell {
46-
fn default() -> Self {
47-
Self {
48-
val: Arc::new(Mutex::new(None)),
49-
}
50-
}
51-
}
52-
53-
impl BlockBuildingSink for BestBlockCell {
54-
fn new_block(&self, block: Block) {
55-
self.compare_and_update(block);
56-
}
41+
block: Mutex<Option<Block>>,
42+
block_notify: Notify,
5743
}
5844

5945
impl BestBlockCell {
6046
pub fn compare_and_update(&self, block: Block) {
61-
let mut best_block = self.val.lock().unwrap();
47+
let mut best_block = self.block.lock().unwrap();
6248
let old_value = best_block
6349
.as_ref()
6450
.map(|b| b.trace.bid_value)
6551
.unwrap_or_default();
6652
if block.trace.bid_value > old_value {
6753
*best_block = Some(block);
54+
self.block_notify.notify_one();
6855
}
6956
}
7057

7158
pub fn take_best_block(&self) -> Option<Block> {
72-
self.val.lock().unwrap().take()
59+
self.block.lock().unwrap().take()
60+
}
61+
62+
pub async fn wait_for_change(&self) {
63+
self.block_notify.notified().await
64+
}
65+
}
66+
67+
/// Adapts BestBlockCell to BlockBuildingSink by calling compare_and_update on new_block.
68+
#[derive(Debug)]
69+
struct BestBlockCellToBlockBuildingSink {
70+
best_block_cell: Arc<BestBlockCell>,
71+
}
72+
73+
impl BlockBuildingSink for BestBlockCellToBlockBuildingSink {
74+
fn new_block(&self, block: Block) {
75+
self.best_block_cell.compare_and_update(block);
7376
}
7477
}
7578

@@ -105,13 +108,8 @@ pub struct SubmissionConfig {
105108
pub optimistic_prevalidate_optimistic_blocks: bool,
106109

107110
pub bid_observer: Box<dyn BidObserver + Send + Sync>,
108-
/// Delta relative to slot_time at which we start to submit blocks. Usually negative since we need to start submitting BEFORE the slot time.
109-
pub slot_delta_to_start_submits: time::Duration,
110111
}
111112

112-
/// run_submit_to_relays_job waits at least MIN_TIME_BETWEEN_BLOCK_CHECK between new block polls to avoid 100% CPU
113-
const MIN_TIME_BETWEEN_BLOCK_CHECK: Duration = Duration::from_millis(5);
114-
115113
/// Values from [`BuiltBlockTrace`]
116114
struct BuiltBlockInfo {
117115
pub bid_value: U256,
@@ -130,7 +128,7 @@ struct BuiltBlockInfo {
130128
/// returns the best bid made
131129
#[allow(clippy::too_many_arguments)]
132130
async fn run_submit_to_relays_job(
133-
best_bid: BestBlockCell,
131+
best_bid: Arc<BestBlockCell>,
134132
slot_data: MevBoostSlotData,
135133
relays: Vec<MevBoostRelay>,
136134
config: Arc<SubmissionConfig>,
@@ -143,14 +141,6 @@ async fn run_submit_to_relays_job(
143141
slot_data.slot(),
144142
);
145143
let mut res = None;
146-
// first, sleep to slot time - slot_delta_to_start_submits
147-
{
148-
let submit_start_time = slot_data.timestamp() + config.slot_delta_to_start_submits;
149-
let sleep_duration = submit_start_time - time::OffsetDateTime::now_utc();
150-
if sleep_duration.is_positive() {
151-
sleep(sleep_duration.try_into().unwrap()).await;
152-
}
153-
}
154144

155145
let (normal_relays, optimistic_relays) = {
156146
let mut normal_relays = Vec::new();
@@ -166,18 +156,12 @@ async fn run_submit_to_relays_job(
166156
};
167157

168158
let mut last_bid_value = U256::from(0);
169-
let mut last_submit_time = Instant::now();
170159
'submit: loop {
171160
if cancel.is_cancelled() {
172161
break 'submit res;
173162
}
174163

175-
let time_since_submit = last_submit_time.elapsed();
176-
if time_since_submit < MIN_TIME_BETWEEN_BLOCK_CHECK {
177-
sleep(MIN_TIME_BETWEEN_BLOCK_CHECK - time_since_submit).await;
178-
}
179-
last_submit_time = Instant::now();
180-
164+
best_bid.wait_for_change().await;
181165
let block = if let Some(new_block) = best_bid.take_best_block() {
182166
if new_block.trace.bid_value > last_bid_value {
183167
last_bid_value = new_block.trace.bid_value;
@@ -348,26 +332,29 @@ async fn run_submit_to_relays_job(
348332
}
349333

350334
pub async fn run_submit_to_relays_job_and_metrics(
351-
best_bid: BestBlockCell,
335+
best_bid: Arc<BestBlockCell>,
352336
slot_data: MevBoostSlotData,
353337
relays: Vec<MevBoostRelay>,
354338
config: Arc<SubmissionConfig>,
355339
cancel: CancellationToken,
356340
competition_bid_value_source: Arc<dyn BidValueSource + Send + Sync>,
357341
) {
358-
let best_bid = run_submit_to_relays_job(
359-
best_bid.clone(),
342+
let last_build_block_info = run_submit_to_relays_job(
343+
best_bid,
360344
slot_data,
361345
relays,
362346
config,
363347
cancel,
364348
competition_bid_value_source,
365349
)
366350
.await;
367-
if let Some(best_bid) = best_bid {
368-
if best_bid.bid_value > best_bid.true_bid_value {
351+
if let Some(last_build_block_info) = last_build_block_info {
352+
if last_build_block_info.bid_value > last_build_block_info.true_bid_value {
369353
inc_subsidized_blocks(false);
370-
add_subsidy_value(best_bid.bid_value - best_bid.true_bid_value, false);
354+
add_subsidy_value(
355+
last_build_block_info.bid_value - last_build_block_info.true_bid_value,
356+
false,
357+
);
371358
}
372359
}
373360
}
@@ -540,7 +527,7 @@ impl BuilderSinkFactory for RelaySubmitSinkFactory {
540527
competition_bid_value_source: Arc<dyn BidValueSource + Send + Sync>,
541528
cancel: CancellationToken,
542529
) -> Box<dyn BlockBuildingSink> {
543-
let best_bid = BestBlockCell::default();
530+
let best_block_cell = Arc::new(BestBlockCell::default());
544531

545532
let relays = slot_data
546533
.relays
@@ -553,13 +540,13 @@ impl BuilderSinkFactory for RelaySubmitSinkFactory {
553540
})
554541
.collect();
555542
tokio::spawn(run_submit_to_relays_job_and_metrics(
556-
best_bid.clone(),
543+
best_block_cell.clone(),
557544
slot_data,
558545
relays,
559546
self.submission_config.clone(),
560547
cancel,
561548
competition_bid_value_source,
562549
));
563-
Box::new(best_bid)
550+
Box::new(BestBlockCellToBlockBuildingSink { best_block_cell })
564551
}
565552
}

crates/rbuilder/src/live_builder/config.rs

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,6 @@ use std::{
6363
use tracing::info;
6464
use url::Url;
6565

66-
/// From experience (Vitaly's) all generated blocks before slot_time-8sec end loosing (due to last moment orders?)
67-
const DEFAULT_SLOT_DELTA_TO_START_SUBMITS: time::Duration = time::Duration::milliseconds(-8000);
6866
/// We initialize the wallet with the last full day. This should be enough for any bidder.
6967
/// On debug I measured this to be < 300ms so it's not big deal.
7068
pub const WALLET_INIT_HISTORY_SIZE: Duration = Duration::from_secs(60 * 60 * 24);
@@ -120,9 +118,6 @@ pub struct L1Config {
120118
/// If true all optimistic submissions will be validated on nodes specified in `dry_run_validation_url`
121119
pub optimistic_prevalidate_optimistic_blocks: bool,
122120

123-
/// See [`SubmissionConfig`]
124-
slot_delta_to_start_submits_ms: Option<i64>,
125-
126121
/// How many seals we are going to be doing in parallel.
127122
/// Optimal value may change depending on the roothash computation caching strategies.
128123
pub max_concurrent_seals: u64,
@@ -146,7 +141,6 @@ impl Default for L1Config {
146141
optimistic_enabled: false,
147142
optimistic_max_bid_value_eth: "0.0".to_string(),
148143
optimistic_prevalidate_optimistic_blocks: false,
149-
slot_delta_to_start_submits_ms: None,
150144
cl_node_url: vec![EnvOrValue::from("http://127.0.0.1:3500")],
151145
max_concurrent_seals: DEFAULT_MAX_CONCURRENT_SEALS,
152146
genesis_fork_version: None,
@@ -204,12 +198,6 @@ impl L1Config {
204198
BLSBlockSigner::new(secret_key, signing_domain)
205199
}
206200

207-
pub fn slot_delta_to_start_submits(&self) -> time::Duration {
208-
self.slot_delta_to_start_submits_ms
209-
.map(time::Duration::milliseconds)
210-
.unwrap_or(DEFAULT_SLOT_DELTA_TO_START_SUBMITS)
211-
}
212-
213201
fn submission_config(
214202
&self,
215203
chain_spec: Arc<ChainSpec>,
@@ -258,7 +246,6 @@ impl L1Config {
258246
optimistic_signer,
259247
optimistic_max_bid_value: parse_ether(&self.optimistic_max_bid_value_eth)?,
260248
optimistic_prevalidate_optimistic_blocks: self.optimistic_prevalidate_optimistic_blocks,
261-
slot_delta_to_start_submits: self.slot_delta_to_start_submits(),
262249
bid_observer,
263250
})
264251
}

crates/rbuilder/src/primitives/serialize.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,7 @@ pub struct CancelShareBundle {
262262
pub key: ShareBundleReplacementKey,
263263
}
264264
/// Since we use the same API (mev_sendBundle) to get new bundles and also to cancel them we need this struct
265+
#[allow(clippy::large_enum_variant)]
265266
pub enum RawShareBundleDecodeResult {
266267
NewShareBundle(ShareBundle),
267268
CancelShareBundle(CancelShareBundle),

0 commit comments

Comments
 (0)