diff --git a/crates/rbuilder/benches/benchmarks/mev_boost.rs b/crates/rbuilder/benches/benchmarks/mev_boost.rs index 25c1059ec..a1ef4ff82 100644 --- a/crates/rbuilder/benches/benchmarks/mev_boost.rs +++ b/crates/rbuilder/benches/benchmarks/mev_boost.rs @@ -10,6 +10,7 @@ use rbuilder::mev_boost::{ use reth::primitives::SealedBlock; use reth_chainspec::SEPOLIA; use reth_primitives::kzg::Blob; +use ssz::Encode; use std::{fs, path::PathBuf, sync::Arc}; fn mev_boost_serialize_submit_block(data: DenebSubmitBlockRequest) { diff --git a/crates/rbuilder/src/live_builder/block_output/relay_submit.rs b/crates/rbuilder/src/live_builder/block_output/relay_submit.rs index 77784f96f..4406cab9e 100644 --- a/crates/rbuilder/src/live_builder/block_output/relay_submit.rs +++ b/crates/rbuilder/src/live_builder/block_output/relay_submit.rs @@ -8,10 +8,10 @@ use crate::{ }, primitives::mev_boost::{MevBoostRelayBidSubmitter, MevBoostRelayID}, telemetry::{ - add_relay_submit_time, add_subsidy_value, inc_conn_relay_errors, - inc_failed_block_simulations, inc_initiated_submissions, inc_other_relay_errors, - inc_relay_accepted_submissions, inc_subsidized_blocks, inc_too_many_req_relay_errors, - mark_submission_start_time, + add_relay_submission_stats, add_relay_submit_time, add_subsidy_value, + inc_conn_relay_errors, inc_failed_block_simulations, inc_initiated_submissions, + inc_other_relay_errors, inc_relay_accepted_submissions, inc_subsidized_blocks, + inc_too_many_req_relay_errors, mark_submission_start_time, }, utils::{duration_ms, error_storage::store_error_event}, }; @@ -203,6 +203,7 @@ async fn run_submit_to_relays_job( top_competitor_bid: block.trace.seen_competition_bid, }, order_ids: executed_orders.map(|o| o.id()).collect(), + sealed_at: block.trace.orders_sealed_at, }; let submission_span = info_span!( @@ -402,10 +403,11 @@ async fn submit_bid_to_the_relay( }; let submit_time = submit_start.elapsed(); match relay_result { - Ok(()) => { + Ok(stats) => { trace!("Block submitted to the relay successfully"); add_relay_submit_time(relay.id(), submit_time); inc_relay_accepted_submissions(relay.id(), optimistic); + add_relay_submission_stats(relay.id(), stats); } Err(SubmitBlockErr::PayloadDelivered | SubmitBlockErr::PastSlot) => { trace!("Block already delivered by the relay, cancelling"); diff --git a/crates/rbuilder/src/live_builder/order_input/mod.rs b/crates/rbuilder/src/live_builder/order_input/mod.rs index e5ebf9b59..6b96aeb19 100644 --- a/crates/rbuilder/src/live_builder/order_input/mod.rs +++ b/crates/rbuilder/src/live_builder/order_input/mod.rs @@ -20,7 +20,10 @@ use jsonrpsee::RpcModule; use parking_lot::Mutex; use std::{net::Ipv4Addr, path::PathBuf, sync::Arc, time::Duration}; use std::{path::Path, time::Instant}; -use tokio::{sync::mpsc, task::JoinHandle}; +use tokio::{ + sync::mpsc, + task::{spawn_blocking, JoinHandle}, +}; use tokio_util::sync::CancellationToken; use tracing::{debug, error, info, trace, warn}; @@ -202,7 +205,7 @@ pub async fn start_orderpool_jobs

( header_receiver: mpsc::Receiver

, ) -> eyre::Result<(JoinHandle<()>, OrderPoolSubscriber)> where - P: StateProviderFactory + 'static, + P: StateProviderFactory + Clone + 'static, { if config.ignore_cancellable_orders { warn!("ignore_cancellable_orders is set to true, some order input is ignored"); @@ -332,7 +335,7 @@ async fn spawn_clean_orderpool_job

( global_cancellation: CancellationToken, ) -> eyre::Result> where - P: StateProviderFactory + 'static, + P: StateProviderFactory + Clone + 'static, { let mut header_receiver: mpsc::Receiver

= header_receiver; @@ -343,32 +346,38 @@ where tokio::select! { header = header_receiver.recv() => { if let Some(header) = header { - let current_block = header.number; - set_current_block(current_block); - let state = match provider_factory.latest() { - Ok(state) => state, - Err(err) => { - error!("Failed to get latest state: {}", err); - // @Metric error count - continue; - } - }; - - let mut orderpool = orderpool.lock(); - let start = Instant::now(); - - 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!( - current_block, - tx_count, - bundle_count, - update_time_ms = update_time.as_millis(), - "Cleaned orderpool", - ); + let provider_factory = provider_factory.clone(); + let orderpool = orderpool.clone(); + let res = spawn_blocking(move || { + let current_block = header.number; + set_current_block(current_block); + let state = match provider_factory.latest() { + Ok(state) => state, + Err(err) => { + error!(?err, "Failed to get latest state"); + return; + } + }; + + let mut orderpool = orderpool.lock(); + let start = Instant::now(); + + 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!( + current_block, + tx_count, + bundle_count, + update_time_ms = update_time.as_millis(), + "Cleaned orderpool", + ); + }).await; + if let Err(err) = res { + error!(?err, "Clean orderpool error"); + } } else { info!("Clean orderpool job: channel ended"); if !global_cancellation.is_cancelled(){ diff --git a/crates/rbuilder/src/mev_boost/mod.rs b/crates/rbuilder/src/mev_boost/mod.rs index 2cf5b0d49..379aa20a8 100644 --- a/crates/rbuilder/src/mev_boost/mod.rs +++ b/crates/rbuilder/src/mev_boost/mod.rs @@ -5,6 +5,8 @@ pub mod rpc; pub mod sign_payload; pub mod submission; +use crate::utils::{offset_datetime_to_timestamp_us, timestamp_now_us}; + use super::utils::u256decimal_serde_helper; use alloy_primitives::{Address, BlockHash, Bytes, U256}; @@ -18,8 +20,13 @@ use reqwest::{ use serde::{Deserialize, Serialize}; use serde_with::{serde_as, DisplayFromStr}; use ssz::Encode; -use std::{io::Write, str::FromStr}; +use std::{ + io::Write, + str::FromStr, + time::{Duration, Instant}, +}; use submission::{SubmitBlockRequestNoBlobs, SubmitBlockRequestWithMetadata}; +use tokio::task::spawn_blocking; use url::Url; pub use error::*; @@ -28,6 +35,8 @@ pub use sign_payload::*; const TOTAL_PAYMENT_HEADER: &str = "Total-Payment"; const BUNDLE_HASHES_HEADER: &str = "Bundle-Hashes"; const TOP_BID_HEADER: &str = "Top-Bid"; +const SUBMIT_START_TIME_US: &str = "Submit-Start-Time-Us"; +const BLOCK_SEAL_TIME_US: &str = "Block-Seal-Time-Us"; const BLOXROUTE_SHARE_HEADER: &str = "share"; const BLOXROUTE_BUILDER_VALUE_HEADER: &str = "builder-value"; @@ -357,6 +366,22 @@ impl std::fmt::Debug for SubmitBlockErr { } } +#[derive(Debug, Clone, Default)] +pub struct RelaySubmitStats { + /// time spent between starting submission and doing request + pub send_preparation_time: Duration, + /// time spent compressing payload using gzip, its counted in send_preparation_time + pub send_compression_time: Duration, + /// time spent writing request + pub send_write_request_time: Duration, + /// time spent reading response + pub send_read_request_time: Duration, + /// size in bytes of original payload (before compression if present) + pub original_payload_size: usize, + /// size in bytes of sent payload (after compression if present) + pub sent_payload_size: usize, +} + // Data API impl RelayClient { async fn get_one_delivered_payload( @@ -508,7 +533,9 @@ impl RelayClient { gzip: bool, fake_relay: bool, cancellations: bool, + stats: &mut RelaySubmitStats, ) -> Result { + let preparation_start = Instant::now(); let url = { let mut url = self.url.clone(); url.set_path("/relay/v1/builder/blocks"); @@ -544,20 +571,28 @@ impl RelayClient { self.add_auth_headers(&mut headers) .map_err(|_| SubmitBlockErr::InvalidHeader)?; + stats.original_payload_size = body_data.len(); + let compression_start = Instant::now(); // GZIP if gzip { headers.insert( CONTENT_ENCODING, HeaderValue::from_static(GZIP_CONTENT_ENCODING), ); - let mut encoder = GzEncoder::new(Vec::new(), Compression::default()); - encoder - .write_all(&body_data) - .map_err(|e| SubmitBlockErr::RPCSerializationError(e.to_string()))?; - body_data = encoder - .finish() - .map_err(|e| SubmitBlockErr::RPCSerializationError(e.to_string()))?; + body_data = spawn_blocking(move || { + let mut encoder = GzEncoder::new(Vec::new(), Compression::fast()); + encoder + .write_all(&body_data) + .map_err(|e| SubmitBlockErr::RPCSerializationError(e.to_string()))?; + encoder + .finish() + .map_err(|e| SubmitBlockErr::RPCSerializationError(e.to_string())) + }) + .await + .map_err(|e| SubmitBlockErr::RPCSerializationError(e.to_string()))??; } + stats.sent_payload_size = body_data.len(); + stats.send_compression_time = compression_start.elapsed(); // Set bloxroute specific headers. if self.is_bloxroute { @@ -613,12 +648,24 @@ impl RelayClient { }; builder = builder.header(BUNDLE_HASHES_HEADER, bundle_ids); } + + builder = builder + .header(SUBMIT_START_TIME_US, timestamp_now_us().to_string()) + .header( + BLOCK_SEAL_TIME_US, + offset_datetime_to_timestamp_us(submission_with_metadata.metadata.sealed_at), + ); } - Ok(builder + stats.send_preparation_time = preparation_start.elapsed(); + let send_start = Instant::now(); + let response = builder .send() .await - .map_err(|e| RelayError::RequestError(e.into()))?) + .map_err(|e| RelayError::RequestError(e.into()))?; + stats.send_write_request_time = send_start.elapsed(); + + Ok(response) } /// Submits the block (call_relay_submit_block) and processes some special errors. @@ -629,12 +676,19 @@ impl RelayClient { gzip: bool, fake_relay: bool, cancellations: bool, - ) -> Result<(), SubmitBlockErr> { + ) -> Result { + let mut stats = RelaySubmitStats::default(); let resp = self - .call_relay_submit_block(data, ssz, gzip, fake_relay, cancellations) + .call_relay_submit_block(data, ssz, gzip, fake_relay, cancellations, &mut stats) .await?; + + let read_start = Instant::now(); let status = resp.status(); + // always read full body to reuse TCP connection + let body_result = resp.bytes().await; + stats.send_read_request_time = read_start.elapsed(); + if status == StatusCode::TOO_MANY_REQUESTS { return Err(RelayError::TooManyRequests.into()); } @@ -642,17 +696,14 @@ impl RelayClient { return Err(RelayError::ConnectionError.into()); } - let data = resp - .bytes() - .await - .map_err(|e| RelayError::RequestError(e.into()))?; + let data = body_result.map_err(|e| RelayError::RequestError(e.into()))?; if status == StatusCode::OK && data.as_ref() == b"" { - return Ok(()); + return Ok(stats); } match serde_json::from_slice::>(&data) { - Ok(RelayResponse::Ok(_)) => Ok(()), + Ok(RelayResponse::Ok(_)) => Ok(stats), Ok(RelayResponse::Error(error)) => { let msg = error.message.as_str(); match msg { @@ -690,11 +741,11 @@ impl RelayClient { // bloxroute returns empty response in this format which we handle here because its not valid // jsonrpc response if data.as_ref() == b"{}\n" { - return Ok(()); + return Ok(stats); } let data_string = String::from_utf8_lossy(&data).to_string(); if is_ignorable_relay_error(status, &data_string) { - Ok(()) + Ok(stats) } else { Err(RelayError::UnknownRelayError(status, data_string).into()) } @@ -725,6 +776,7 @@ impl RelayClient { #[cfg(test)] mod tests { use submission::{BidMetadata, BidValueMetadata}; + use time::OffsetDateTime; use super::{rpc::TestDataGenerator, *}; use crate::mev_boost::{ @@ -884,6 +936,7 @@ mod tests { top_competitor_bid: None, }, order_ids: vec![], + sealed_at: OffsetDateTime::now_utc(), }, }; relay diff --git a/crates/rbuilder/src/mev_boost/submission.rs b/crates/rbuilder/src/mev_boost/submission.rs index 10a5f12f5..29f06d524 100644 --- a/crates/rbuilder/src/mev_boost/submission.rs +++ b/crates/rbuilder/src/mev_boost/submission.rs @@ -8,6 +8,7 @@ use alloy_rpc_types_engine::{BlobsBundleV1, ExecutionPayloadV2, ExecutionPayload use derive_more::Deref; use serde::{Deserialize, Serialize}; use ssz::DecodeError; +use time::OffsetDateTime; use super::adjustment::BidAdjustmentData; use crate::primitives::OrderId; @@ -367,6 +368,7 @@ impl ssz::Decode for CapellaSubmitBlockRequest { pub struct BidMetadata { pub value: BidValueMetadata, pub order_ids: Vec, + pub sealed_at: OffsetDateTime, } #[derive(Clone, Copy, Default, Debug)] diff --git a/crates/rbuilder/src/primitives/mev_boost.rs b/crates/rbuilder/src/primitives/mev_boost.rs index b72ed550c..19a49fea1 100644 --- a/crates/rbuilder/src/primitives/mev_boost.rs +++ b/crates/rbuilder/src/primitives/mev_boost.rs @@ -1,6 +1,6 @@ use crate::mev_boost::{ - submission::SubmitBlockRequestWithMetadata, RelayClient, RelayError, SubmitBlockErr, - ValidatorSlotData, + submission::SubmitBlockRequestWithMetadata, RelayClient, RelayError, RelaySubmitStats, + SubmitBlockErr, ValidatorSlotData, }; use alloy_primitives::{utils::parse_ether, Address, U256}; use governor::{DefaultDirectRateLimiter, Quota, RateLimiter}; @@ -192,7 +192,7 @@ impl MevBoostRelayBidSubmitter { pub async fn submit_block( &self, data: &SubmitBlockRequestWithMetadata, - ) -> Result<(), SubmitBlockErr> { + ) -> Result { self.client .submit_block( data, diff --git a/crates/rbuilder/src/telemetry/metrics/mod.rs b/crates/rbuilder/src/telemetry/metrics/mod.rs index 68f90e30f..07b285744 100644 --- a/crates/rbuilder/src/telemetry/metrics/mod.rs +++ b/crates/rbuilder/src/telemetry/metrics/mod.rs @@ -9,6 +9,7 @@ use crate::{ building::BuiltBlockTrace, live_builder::block_list_provider::{blocklist_hash, BlockList}, + mev_boost::RelaySubmitStats, primitives::mev_boost::MevBoostRelayID, utils::{build_info::Version, duration_ms}, }; @@ -337,6 +338,23 @@ register_metrics! { ) .unwrap(); + /// Relay submission request + + pub static RELAY_SUBMIT_BLOCK_SIZE: HistogramVec = HistogramVec::new( + HistogramOpts::new("relay_submit_block_size", "Size of the block that is submitted to the relay before and after compression. (bytes)") + .buckets(linear_buckets_range(1.0, 5_000_000.0, 500)), + &["compression"] + ) + .unwrap(); + + + pub static RELAY_SUBMIT_REQUEST_STEP_TIME: HistogramVec = HistogramVec::new( + HistogramOpts::new("relay_submit_request_step_time", "Time for different steps when doing request to the relay (ms)") + .buckets(exponential_buckets_range(0.01, 3000.0, 300)), + &["relay", "step"] + ) + .unwrap(); + } // This function should be called periodically to reset histogram metrics. @@ -375,6 +393,8 @@ pub fn reset_histogram_metrics() { ORDER_SIM_END_TO_FIRST_BUILD_STARTED_MIN_TIME.reset(); BLOCK_FILL_START_SEAL_END_TIME.reset(); BLOCK_SEAL_END_SUBMIT_START_TIME.reset(); + RELAY_SUBMIT_BLOCK_SIZE.reset(); + RELAY_SUBMIT_REQUEST_STEP_TIME.reset(); } pub(super) fn set_version(version: Version) { @@ -521,6 +541,27 @@ pub fn add_relay_submit_time(relay: &MevBoostRelayID, duration: Duration) { .observe(duration_ms(duration)); } +pub fn add_relay_submission_stats(relay: &MevBoostRelayID, stats: RelaySubmitStats) { + RELAY_SUBMIT_BLOCK_SIZE + .with_label_values(&["false"]) + .observe(stats.original_payload_size as f64); + RELAY_SUBMIT_BLOCK_SIZE + .with_label_values(&["true"]) + .observe(stats.sent_payload_size as f64); + RELAY_SUBMIT_REQUEST_STEP_TIME + .with_label_values(&[relay.as_str(), "preparation"]) + .observe(duration_ms(stats.send_preparation_time)); + RELAY_SUBMIT_REQUEST_STEP_TIME + .with_label_values(&[relay.as_str(), "compression"]) + .observe(duration_ms(stats.send_compression_time)); + RELAY_SUBMIT_REQUEST_STEP_TIME + .with_label_values(&[relay.as_str(), "write"]) + .observe(duration_ms(stats.send_write_request_time)); + RELAY_SUBMIT_REQUEST_STEP_TIME + .with_label_values(&[relay.as_str(), "read"]) + .observe(duration_ms(stats.send_read_request_time)); +} + const BIG_RPC_DATA_THRESHOLD: usize = 50000; const BIG_RPC_DATA_TEXT: &str = ">50K"; const SMALL_RPC_DATA_TEXT: &str = "<=50K"; diff --git a/crates/rbuilder/src/utils/mod.rs b/crates/rbuilder/src/utils/mod.rs index c8ee9aa50..7ba663759 100644 --- a/crates/rbuilder/src/utils/mod.rs +++ b/crates/rbuilder/src/utils/mod.rs @@ -113,6 +113,13 @@ pub fn timestamp_now_ms() -> u64 { .unwrap_or_default() } +/// Returns unix timestamp in microseconds +pub fn timestamp_now_us() -> u64 { + (time::OffsetDateTime::now_utc().unix_timestamp_nanos() / 1_000) + .try_into() + .unwrap_or_default() +} + pub fn gen_uid() -> u64 { rand::random() } diff --git a/crates/test-relay/src/main.rs b/crates/test-relay/src/main.rs index 31d50dfb1..2b10dc9d6 100644 --- a/crates/test-relay/src/main.rs +++ b/crates/test-relay/src/main.rs @@ -1,6 +1,6 @@ use crate::validation_api_client::ValidationAPIClient; use ahash::HashMap; -use metrics::spawn_metrics_server; +use metrics::{reset_histogram_metrics_test_relay, spawn_metrics_server}; use rbuilder::{ beacon_api_client::Client, mev_boost::RelayClient, @@ -8,7 +8,7 @@ use rbuilder::{ utils::tracing::{setup_tracing_subscriber, LoggerConfig}, }; use relay::spawn_relay_server; -use std::net::SocketAddr; +use std::{net::SocketAddr, time::Duration}; use tokio_util::sync::CancellationToken; use url::Url; @@ -19,6 +19,8 @@ pub mod metrics; pub mod relay; pub mod validation_api_client; +const HISTOGRAM_RESET_TIME: Duration = Duration::from_secs(60 * 10); // 10 min + #[derive(Parser, Debug)] struct Cli { #[clap( @@ -89,6 +91,11 @@ async fn main() -> eyre::Result<()> { }; setup_tracing_subscriber(config)?; + tokio::spawn(async { + tokio::time::sleep(HISTOGRAM_RESET_TIME).await; + reset_histogram_metrics_test_relay(); + }); + spawn_metrics_server(cli.metrics_address); let cl_clients = cli diff --git a/crates/test-relay/src/metrics.rs b/crates/test-relay/src/metrics.rs index 7491067b3..344b2677c 100644 --- a/crates/test-relay/src/metrics.rs +++ b/crates/test-relay/src/metrics.rs @@ -69,6 +69,12 @@ register_metrics! { .buckets(exponential_buckets_range(1.0, 3000.0, 100)), &[], ).unwrap(); + + pub static TIME_TO_RECEIVE: HistogramVec = HistogramVec::new( + HistogramOpts::new("time_to_receive", "Time from builder to relay receiving block (ms)") + .buckets(exponential_buckets_range(1.0, 10000.0, 500)), + &["builder", "kind"], + ).unwrap(); } pub fn inc_payloads_received(builder: &str) { @@ -107,6 +113,16 @@ pub fn add_payload_validation_time(duration: Duration) { .observe(duration_ms(duration)); } +pub fn add_time_to_receive(delta_us: u64, builder: &str, kind: &str) { + if delta_us == 0 { + return; + } + + TIME_TO_RECEIVE + .with_label_values(&[builder, kind]) + .observe(delta_us as f64 / 1000.0); +} + pub fn spawn_metrics_server(address: SocketAddr) { let metrics_route = warp::path!("debug" / "metrics" / "prometheus").and_then(metrics_handler); tokio::spawn(warp::serve(metrics_route).run(address)); @@ -115,3 +131,10 @@ pub fn spawn_metrics_server(address: SocketAddr) { async fn metrics_handler() -> Result { Ok(gather_prometheus_metrics(®ISTRY)) } + +pub fn reset_histogram_metrics_test_relay() { + WINNER_ADVANTAGE.reset(); + PAYLOAD_PROCESSING_TIME.reset(); + PAYLOAD_VALIDATION_TIME.reset(); + TIME_TO_RECEIVE.reset(); +} diff --git a/crates/test-relay/src/relay.rs b/crates/test-relay/src/relay.rs index 34f8eb90a..b24d636d2 100644 --- a/crates/test-relay/src/relay.rs +++ b/crates/test-relay/src/relay.rs @@ -1,7 +1,7 @@ use crate::{ metrics::{ - add_payload_processing_time, add_payload_validation_time, add_winning_bid, - inc_payload_validation_errors, inc_payloads_received, inc_relay_errors, + add_payload_processing_time, add_payload_validation_time, add_time_to_receive, + add_winning_bid, inc_payload_validation_errors, inc_payloads_received, inc_relay_errors, }, validation_api_client::{ValidationAPIClient, ValidationError}, }; @@ -18,6 +18,7 @@ use rbuilder::{ }, mev_boost::submission::SubmitBlockRequest, primitives::mev_boost::MevBoostRelaySlotInfoProvider, + utils::timestamp_now_us, }; use serde::{Deserialize, Serialize}; use ssz::Decode as _; @@ -129,10 +130,25 @@ pub fn spawn_relay_server( .and(body::bytes()) .and(warp::header::("content-type")) .and(warp::header::optional::("content-encoding")) + .and(warp::header::optional::("Submit-Start-Time-Us")) + .and(warp::header::optional::("Block-Seal-Time-Us")) .then( - |state: RelayState, query, body, content_type, content_encoding| async move { + |state: RelayState, + query, + body, + content_type, + content_encoding, + submit_start_timestamp_us, + block_seal_timestamp_us| async move { state - .handle_block(query, body, content_type, content_encoding) + .handle_block( + query, + body, + content_type, + content_encoding, + submit_start_timestamp_us, + block_seal_timestamp_us, + ) .await }, ); @@ -176,8 +192,12 @@ impl RelayState { body: Bytes, content_type: String, content_encoding: Option, + submit_start_timestamp_us: Option, + block_seal_timestamp_us: Option, ) -> Box { let processing_start = Instant::now(); + let start_timestamp_us = timestamp_now_us(); + let cancel = match query.cancellations { Some(1) => true, Some(0) | None => false, @@ -240,6 +260,25 @@ impl RelayState { inc_payloads_received(&builder_id); + if let Some(builder_ts) = block_seal_timestamp_us { + add_time_to_receive( + start_timestamp_us + .checked_sub(builder_ts) + .unwrap_or_default(), + &builder_id, + "seal_end", + ); + } + if let Some(builder_ts) = submit_start_timestamp_us { + add_time_to_receive( + start_timestamp_us + .checked_sub(builder_ts) + .unwrap_or_default(), + &builder_id, + "submit_start", + ); + } + let (withdrawals_root, registered_gas_limit, parent_beacon_block_root) = { let pending_slot = self.pending_slot_data.lock(); let pending_slot = if pending_slot.is_some() {