diff --git a/crates/rbuilder/src/building/builders/block_building_helper.rs b/crates/rbuilder/src/building/builders/block_building_helper.rs index a6075451..c4f253ca 100644 --- a/crates/rbuilder/src/building/builders/block_building_helper.rs +++ b/crates/rbuilder/src/building/builders/block_building_helper.rs @@ -409,9 +409,12 @@ where Err(err) => { if err.is_consistent_db_view_err() { let last_block_number = self.provider.last_block_number().unwrap_or_default(); + debug!( block_number, - last_block_number, "Can't build on this head, cancelling slot" + payload_id = self.building_ctx.payload_id, + last_block_number, + "Can't build on this head, cancelling slot" ); self.cancel_on_fatal_error.cancel(); } diff --git a/crates/rbuilder/src/building/builders/mod.rs b/crates/rbuilder/src/building/builders/mod.rs index 236efc1a..bc221ff4 100644 --- a/crates/rbuilder/src/building/builders/mod.rs +++ b/crates/rbuilder/src/building/builders/mod.rs @@ -6,7 +6,10 @@ pub mod parallel_builder; use crate::{ building::{BlockBuildingContext, BuiltBlockTrace, SimulatedOrderSink, Sorting}, - live_builder::{payload_events::MevBoostSlotData, simulation::SimulatedOrderCommand}, + live_builder::{ + payload_events::{InternalPayloadId, MevBoostSlotData}, + simulation::SimulatedOrderCommand, + }, primitives::{AccountNonce, OrderId, SimulatedOrder}, provider::StateProviderFactory, utils::{is_provider_factory_health_error, NonceCache}, @@ -254,12 +257,16 @@ pub struct BacktestSimulateBlockInput<'a, P> { /// Handles error from block filling stage. /// Answers if block filling should continue. -pub fn handle_building_error(err: eyre::Report) -> bool { +pub fn handle_building_error(err: eyre::Report, payload_id: InternalPayloadId) -> bool { // @Types let err_str = err.to_string(); if !err_str.contains("Profit too low") { if is_provider_factory_health_error(&err) { - info!(?err, "Cancelling building due to provider factory error"); + info!( + payload_id, + ?err, + "Cancelling building due to provider factory error" + ); return false; } else { warn!(?err, "Error filling orders"); diff --git a/crates/rbuilder/src/building/builders/ordering_builder.rs b/crates/rbuilder/src/building/builders/ordering_builder.rs index b0ff521e..dde57098 100644 --- a/crates/rbuilder/src/building/builders/ordering_builder.rs +++ b/crates/rbuilder/src/building/builders/ordering_builder.rs @@ -62,6 +62,7 @@ pub fn run_ordering_builder
(input: LiveBuilderInput
, config: &OrderingBuil
where
P: StateProviderFactory + Clone + 'static,
{
+ let payload_id = input.ctx.payload_id;
let mut order_intake_consumer = OrderIntakeConsumer::new(
input.provider.clone(),
input.input,
@@ -112,7 +113,7 @@ where
}
}
Err(err) => {
- if !handle_building_error(err) {
+ if !handle_building_error(err, payload_id) {
break 'building;
}
}
diff --git a/crates/rbuilder/src/building/builders/parallel_builder/block_building_result_assembler.rs b/crates/rbuilder/src/building/builders/parallel_builder/block_building_result_assembler.rs
index 4f558f03..6bbbf19b 100644
--- a/crates/rbuilder/src/building/builders/parallel_builder/block_building_result_assembler.rs
+++ b/crates/rbuilder/src/building/builders/parallel_builder/block_building_result_assembler.rs
@@ -161,7 +161,7 @@ where
}
Err(err) => {
let _span = info_span!("Parallel builder failed to build new block",run_id = self.run_id,version = version,err=?err).entered();
- if !handle_building_error(err) {
+ if !handle_building_error(err, self.ctx.payload_id) {
return false;
}
}
diff --git a/crates/rbuilder/src/building/mod.rs b/crates/rbuilder/src/building/mod.rs
index 33bab78f..3e3708c4 100644
--- a/crates/rbuilder/src/building/mod.rs
+++ b/crates/rbuilder/src/building/mod.rs
@@ -17,6 +17,7 @@ use reth_primitives::BlockBody;
use reth_primitives_traits::{proofs, Block as _};
use crate::{
+ live_builder::payload_events::InternalPayloadId,
primitives::{Order, OrderId, SimValue, SimulatedOrder, TransactionSignedEcRecoveredWithBlobs},
provider::RootHasher,
roothash::RootHashError,
@@ -93,6 +94,7 @@ pub struct BlockBuildingContext {
/// Version of the EVM that we are going to use
pub spec_id: SpecId,
pub root_hasher: Arc (
+ provider: P,
+ block_ctx: BlockBuildingContext,
+ block_cancellation: CancellationToken,
+) where
+ P: StateProviderFactory + Clone + 'static,
+{
+ loop {
+ std::thread::sleep(CHECK_LAST_BLOCK_INTERVAL);
+ if block_cancellation.is_cancelled() {
+ return;
+ }
+ let last_block_number = match provider.last_block_number() {
+ Ok(n) => n,
+ Err(err) => {
+ warn!(?err, "Failed to get last block number");
+ continue;
+ }
+ };
+ if last_block_number + 1 != block_ctx.block() {
+ info!(
+ reason = "last block number",
+ last_block_number,
+ block = block_ctx.block(),
+ payload_id = block_ctx.payload_id,
+ "Cancelling building job"
+ );
+ block_cancellation.cancel();
+ return;
+ }
+
+ let last_block_hash = match provider.block_hash(last_block_number) {
+ Ok(Some(h)) => h,
+ Ok(None) => {
+ warn!(err = "hash is missing", "Failed to get last block hash");
+ continue;
+ }
+ Err(err) => {
+ warn!(?err, "Failed to get last block hash");
+ continue;
+ }
+ };
+
+ let parent_hash = block_ctx.attributes.parent;
+ if last_block_hash != parent_hash {
+ info!(
+ reason = "last block hash",
+ ?last_block_hash,
+ ?parent_hash,
+ block = block_ctx.block(),
+ payload_id = block_ctx.payload_id,
+ "Cancelling building job"
+ );
+ block_cancellation.cancel();
+ return;
+ }
+ }
+}
diff --git a/crates/rbuilder/src/live_builder/mod.rs b/crates/rbuilder/src/live_builder/mod.rs
index d5f0a819..a4b4727e 100644
--- a/crates/rbuilder/src/live_builder/mod.rs
+++ b/crates/rbuilder/src/live_builder/mod.rs
@@ -209,33 +209,37 @@ where
let blocklist = self.blocklist_provider.get_blocklist()?;
if blocklist.contains(&payload.fee_recipient()) {
warn!(
- slot = payload.slot(),
- fee_recipient = ?payload.fee_recipient(),
- "Fee recipient is in blocklist"
- );
+ slot = payload.slot(),
+ fee_recipient = ?payload.fee_recipient(),
+ payload_id = payload.payload_id,
+ "Fee recipient is in blocklist"
+ );
continue;
}
let current_time = OffsetDateTime::now_utc();
// see if we can get parent header in a reasonable time
let time_to_slot = payload.timestamp() - current_time;
debug!(
- slot = payload.slot(),
- block = payload.block(),
- ?current_time,
- payload_timestamp = ?payload.timestamp(),
- ?time_to_slot,
- parent_hash = ?payload.parent_block_hash(),
- provider_head_state = ?ProviderHeadState::new(&self.provider),
- "Received payload, time till slot timestamp",
- );
+ slot = payload.slot(),
+ block = payload.block(),
+ payload_id = payload.payload_id,
+ ?current_time,
+ payload_timestamp = ?payload.timestamp(),
+ ?time_to_slot,
+ parent_hash = ?payload.parent_block_hash(),
+ provider_head_state = ?ProviderHeadState::new(&self.provider),
+ "Received payload, time till slot timestamp",
+ );
let time_until_slot_end = time_to_slot + timings.slot_proposal_duration;
if time_until_slot_end.is_negative() {
warn!(
- slot = payload.slot(),
- parent_hash = ?payload.parent_block_hash(),
- "Slot already ended, skipping block building"
- );
+ slot = payload.slot(),
+ block = payload.block(),
+ payload_id = payload.payload_id,
+ parent_hash = ?payload.parent_block_hash(),
+ "Slot already ended, skipping block building"
+ );
continue;
};
@@ -247,18 +251,19 @@ where
{
Ok(header) => header,
Err(err) => {
- warn!(parent_hash = ?payload.parent_block_hash(), ?err, "Failed to get parent header for new slot");
+ warn!(payload_id = payload.payload_id, parent_hash = ?payload.parent_block_hash(), ?err, "Failed to get parent header for new slot");
continue;
}
}
};
debug!(
- slot = payload.slot(),
- block = payload.block(),
- parent_hash = ?payload.parent_block_hash(),
- "Got header for slot"
- );
+ slot = payload.slot(),
+ block = payload.block(),
+ payload_id = payload.payload_id,
+ parent_hash = ?payload.parent_block_hash(),
+ "Got header for slot"
+ );
// notify the order pool that there is a new header
if let Err(err) = header_sender.send(parent_header.clone()).await {
@@ -280,6 +285,7 @@ where
self.extra_data.clone(),
None,
root_hasher,
+ payload.payload_id,
) {
mark_building_started(block_ctx.timestamp());
builder_pool.start_block_building(
diff --git a/crates/rbuilder/src/live_builder/order_input/mod.rs b/crates/rbuilder/src/live_builder/order_input/mod.rs
index c74a16d3..164010af 100644
--- a/crates/rbuilder/src/live_builder/order_input/mod.rs
+++ b/crates/rbuilder/src/live_builder/order_input/mod.rs
@@ -332,8 +332,8 @@ where
tokio::select! {
header = header_receiver.recv() => {
if let Some(header) = header {
- let block_number = header.number;
- set_current_block(block_number);
+ let current_block = header.number;
+ set_current_block(current_block);
let state = match provider_factory.latest() {
Ok(state) => state,
Err(err) => {
@@ -346,13 +346,13 @@ where
let mut orderpool = orderpool.lock();
let start = Instant::now();
- orderpool.head_updated(block_number, &state);
+ orderpool.head_updated(current_block, &state);
let update_time = start.elapsed();
let (tx_count, bundle_count) = orderpool.content_count();
set_ordepool_count(tx_count, bundle_count);
debug!(
- block_number,
+ current_block,
tx_count,
bundle_count,
update_time_ms = update_time.as_millis(),
diff --git a/crates/rbuilder/src/live_builder/payload_events/mod.rs b/crates/rbuilder/src/live_builder/payload_events/mod.rs
index de3d5ad9..0544423e 100644
--- a/crates/rbuilder/src/live_builder/payload_events/mod.rs
+++ b/crates/rbuilder/src/live_builder/payload_events/mod.rs
@@ -15,10 +15,12 @@ use crate::{
SlotSource,
},
primitives::mev_boost::{MevBoostRelayID, MevBoostRelaySlotInfoProvider},
+ utils::timestamp_ms_to_offset_datetime,
};
use alloy_eips::{merge::SLOT_DURATION, BlockNumHash};
use alloy_primitives::{utils::format_ether, Address, B256, U256};
use alloy_rpc_types_beacon::events::PayloadAttributesEvent;
+use derivative::Derivative;
use std::{collections::VecDeque, sync::Arc, time::Duration};
use tokio::{sync::mpsc, task::JoinHandle};
use tokio_util::sync::CancellationToken;
@@ -33,9 +35,13 @@ const NEW_PAYLOAD_RECV_TIMEOUT: Duration = SLOT_DURATION.saturating_mul(2);
/// One slot (12secs) is enough so we don't saturate any resource and we don't miss to many slots.
const CONSENSUS_CLIENT_RECONNECT_WAIT: Duration = SLOT_DURATION;
+/// Unique paload ID used to track payload across the builder.
+pub type InternalPayloadId = u64;
+
/// Data about a slot received from relays.
/// Contains the important information needed to build and submit the block.
-#[derive(Debug, Clone, PartialEq, Eq)]
+#[derive(Derivative)]
+#[derivative(Debug, Clone, PartialEq, Eq)]
pub struct MevBoostSlotData {
/// The .data.payload_attributes.suggested_fee_recipient is replaced
pub payload_attributes_event: PayloadAttributesEvent,
@@ -43,6 +49,8 @@ pub struct MevBoostSlotData {
/// List of relays agreeing to the slot_data. It may not contain all the relays (eg: errors, forks, validators registering only to some relays)
pub relays: Vec