diff --git a/crates/common/src/local_cache.rs b/crates/common/src/local_cache.rs index ee3ae355..8d6e8587 100644 --- a/crates/common/src/local_cache.rs +++ b/crates/common/src/local_cache.rs @@ -255,6 +255,12 @@ impl LocalCache { CACHE_SIZE.with_label_values(&["merged_blocks"]).set(self.merged_blocks.len() as f64); } + pub fn set_merged_block_header_served(&self, block_hash: &B256, time_ns: u64) { + if let Some(mut b) = self.merged_blocks.get_mut(block_hash) { + b.trace.header_served_time_ns = Some(time_ns); + } + } + pub fn get_merged_block(&self, block_hash: &B256) -> Option { self.merged_blocks.get(block_hash).map(|b| b.value().clone()) } diff --git a/crates/database/src/postgres/migrations/V48__add_header_served_time_ns.sql b/crates/database/src/postgres/migrations/V48__add_header_served_time_ns.sql new file mode 100644 index 00000000..c4b4383e --- /dev/null +++ b/crates/database/src/postgres/migrations/V48__add_header_served_time_ns.sql @@ -0,0 +1,2 @@ +ALTER TABLE merged_blocks + ADD COLUMN header_served_time_ns BIGINT; \ No newline at end of file diff --git a/crates/database/src/postgres/postgres_db_service.rs b/crates/database/src/postgres/postgres_db_service.rs index e7e478f1..b6f3ec24 100644 --- a/crates/database/src/postgres/postgres_db_service.rs +++ b/crates/database/src/postgres/postgres_db_service.rs @@ -2770,6 +2770,7 @@ impl PostgresDatabaseService { sim_start_time_ns: i64, sim_end_time_ns: i64, finalize_time_ns: i64, + header_served_time_ns: Option, inserted_at: SystemTime, } @@ -2794,12 +2795,13 @@ impl PostgresDatabaseService { sim_start_time_ns: block.trace.sim_start_time_ns as i64, sim_end_time_ns: block.trace.sim_end_time_ns as i64, finalize_time_ns: block.trace.finalize_time_ns as i64, + header_served_time_ns: block.trace.header_served_time_ns.map(|v| v as i64), inserted_at: SystemTime::now(), }); } // Flatten into SQL params - const FIELD_COUNT: usize = 16; + const FIELD_COUNT: usize = 17; let mut params: Vec<&(dyn ToSql + Sync)> = Vec::with_capacity(structured_blocks.len() * FIELD_COUNT); for block in &structured_blocks { @@ -2818,6 +2820,7 @@ impl PostgresDatabaseService { params.push(&block.sim_start_time_ns); params.push(&block.sim_end_time_ns); params.push(&block.finalize_time_ns); + params.push(&block.header_served_time_ns); params.push(&block.inserted_at); } @@ -2838,6 +2841,7 @@ impl PostgresDatabaseService { sim_start_time_ns, sim_end_time_ns, finalize_time_ns, + header_served_time_ns, inserted_at ) VALUES ", ); diff --git a/crates/relay/src/api/proposer/get_header.rs b/crates/relay/src/api/proposer/get_header.rs index 6023078f..e85370af 100644 --- a/crates/relay/src/api/proposer/get_header.rs +++ b/crates/relay/src/api/proposer/get_header.rs @@ -98,7 +98,7 @@ impl ProposerApi { trace!("done sleep"); - let Ok(rx) = proposer_api.auctioneer_handle.get_header(params) else { + let Ok(rx) = proposer_api.auctioneer_handle.get_header(params, is_mev_boost) else { error!("failed to send get_header to auctioneer"); return Err(ProposerApiError::InternalServerError); }; diff --git a/crates/relay/src/auctioneer/block_merger.rs b/crates/relay/src/auctioneer/block_merger.rs index 7b53fe75..8b4b191f 100644 --- a/crates/relay/src/auctioneer/block_merger.rs +++ b/crates/relay/src/auctioneer/block_merger.rs @@ -169,7 +169,11 @@ impl BlockMerger { } #[timed] - pub fn get_header(&self, original_bid: &PayloadEntry) -> Option { + pub fn get_header( + &self, + original_bid: &PayloadEntry, + is_mev_boost: bool, + ) -> Option { trace!("fetching merged header"); let start_time = Instant::now(); let entry = self.best_merged_block.as_ref()?; @@ -191,6 +195,10 @@ impl BlockMerger { record_step("get_header", start_time.elapsed()); trace!("fetched merged header"); + if is_mev_boost { + self.local_cache.set_merged_block_header_served(entry.bid.block_hash(), utcnow_ns()); + } + if self.config.block_merging_config.is_dry_run { info!("dry run mode enabled, not returning merged header"); return None; @@ -307,6 +315,7 @@ impl BlockMerger { sim_start_time_ns: 0, sim_end_time_ns: 0, finalize_time_ns: 0, + header_served_time_ns: None, }, }), block_hash: base_block_hash, diff --git a/crates/relay/src/auctioneer/get_header.rs b/crates/relay/src/auctioneer/get_header.rs index 1d195606..164e3f5d 100644 --- a/crates/relay/src/auctioneer/get_header.rs +++ b/crates/relay/src/auctioneer/get_header.rs @@ -23,9 +23,11 @@ impl Context { slot_data: &SlotData, res_tx: oneshot::Sender, producers: &mut HelixSpineProducers, + is_mev_boost: bool, ) { assert_eq!(params.slot, self.bid_slot.as_u64(), "params should already be validated!"); - let _ = res_tx.send(self.get_header(params.parent_hash, slot_data, producers)); + let _ = + res_tx.send(self.get_header(params.parent_hash, slot_data, producers, is_mev_boost)); } #[timed] @@ -34,6 +36,7 @@ impl Context { parent_hash: B256, slot_data: &SlotData, producers: &mut HelixSpineProducers, + is_mev_boost: bool, ) -> GetHeaderResult { let Some(best_block_hash) = self.bid_sorter.get_header(&parent_hash) else { warn!(%parent_hash, "no bids for this fork"); @@ -45,7 +48,7 @@ impl Context { return Err(ProposerApiError::NoBidPrepared); }; - if let Some(merged_bid) = self.block_merger.get_header(original_bid) { + if let Some(merged_bid) = self.block_merger.get_header(original_bid, is_mev_boost) { return Ok(merged_bid); }; diff --git a/crates/relay/src/auctioneer/handle.rs b/crates/relay/src/auctioneer/handle.rs index 279a550b..be2135c6 100644 --- a/crates/relay/src/auctioneer/handle.rs +++ b/crates/relay/src/auctioneer/handle.rs @@ -50,11 +50,17 @@ impl AuctioneerHandle { pub fn get_header( &self, params: GetHeaderParams, + is_mev_boost: bool, ) -> Result, ChannelFull> { let (tx, rx) = oneshot::channel(); trace!("sending to auctioneer"); self.auctioneer - .try_send(Event::GetHeader { params, res_tx: tx, span: tracing::Span::current() }) + .try_send(Event::GetHeader { + params, + res_tx: tx, + span: tracing::Span::current(), + is_mev_boost, + }) .map_err(|_| ChannelFull)?; Ok(rx) } diff --git a/crates/relay/src/auctioneer/mod.rs b/crates/relay/src/auctioneer/mod.rs index 429f16ee..c1afa644 100644 --- a/crates/relay/src/auctioneer/mod.rs +++ b/crates/relay/src/auctioneer/mod.rs @@ -406,7 +406,10 @@ impl State { } // get_header - (State::Sorting(slot_data), Event::GetHeader { params, res_tx, span }) => { + ( + State::Sorting(slot_data), + Event::GetHeader { params, res_tx, span, is_mev_boost }, + ) => { let _guard = span.enter(); trace!("received in auctioneer"); @@ -425,7 +428,7 @@ impl State { warn!(req =% params.pubkey, this =% slot_data.registration_data.entry.registration.message.pubkey, "get header for mismatched proposer"); let _ = res_tx.send(Err(ProposerApiError::NoBidPrepared)); } else { - ctx.handle_get_header(params, slot_data, res_tx, producers) + ctx.handle_get_header(params, slot_data, res_tx, producers, is_mev_boost) } trace!("finished processing"); diff --git a/crates/relay/src/auctioneer/types.rs b/crates/relay/src/auctioneer/types.rs index 1a71a52c..cefa0891 100644 --- a/crates/relay/src/auctioneer/types.rs +++ b/crates/relay/src/auctioneer/types.rs @@ -454,6 +454,7 @@ pub enum Event { params: GetHeaderParams, res_tx: oneshot::Sender, span: tracing::Span, + is_mev_boost: bool, }, // Receive multiple of these potentially, assume some light validation GetPayload { diff --git a/crates/relay/src/block_merging/mod.rs b/crates/relay/src/block_merging/mod.rs index 0f12e354..f2f65cdd 100644 --- a/crates/relay/src/block_merging/mod.rs +++ b/crates/relay/src/block_merging/mod.rs @@ -64,6 +64,8 @@ fn merged_block_to_response(m: MergedBlockV1) -> BlockMergeResponse { sim_start_time_ns: m.trace.sim_start_ns, sim_end_time_ns: m.trace.sim_end_ns, finalize_time_ns: m.trace.finalize_ns, + header_served_time_ns: None, /* filled in by the auctioneer when it sends the header + * to the proposer */ }, } } diff --git a/crates/types/src/block_merging.rs b/crates/types/src/block_merging.rs index 4bfd4e52..29cc4342 100644 --- a/crates/types/src/block_merging.rs +++ b/crates/types/src/block_merging.rs @@ -269,6 +269,7 @@ pub struct MergedBlockTrace { pub sim_start_time_ns: u64, pub sim_end_time_ns: u64, pub finalize_time_ns: u64, + pub header_served_time_ns: Option, } #[cfg(test)]