Merge branch 'unstable' into gloas-parent-envelope-unknown-lookup

This commit is contained in:
Eitan Seri-Levi
2026-04-02 21:43:18 +09:00
committed by GitHub
11 changed files with 99 additions and 103 deletions

View File

@@ -130,7 +130,7 @@ use store::{
};
use task_executor::{RayonPoolType, ShutdownReason, TaskExecutor};
use tokio_stream::Stream;
use tracing::{Span, debug, debug_span, error, info, info_span, instrument, trace, warn};
use tracing::{debug, debug_span, error, info, info_span, instrument, trace, warn};
use tree_hash::TreeHash;
use types::data::{ColumnIndex, FixedBlobSidecarList};
use types::execution::BlockProductionVersion;
@@ -2761,6 +2761,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
/// or already-known).
///
/// This method is potentially long-running and should not run on the core executor.
#[instrument(skip_all, level = "debug")]
pub fn filter_chain_segment(
self: &Arc<Self>,
chain_segment: Vec<RangeSyncBlock<T::EthSpec>>,
@@ -2888,12 +2889,8 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
// Filter uninteresting blocks from the chain segment in a blocking task.
let chain = self.clone();
let filter_chain_segment = debug_span!("filter_chain_segment");
let filtered_chain_segment_future = self.spawn_blocking_handle(
move || {
let _guard = filter_chain_segment.enter();
chain.filter_chain_segment(chain_segment)
},
move || chain.filter_chain_segment(chain_segment),
"filter_chain_segment",
);
let mut filtered_chain_segment = match filtered_chain_segment_future.await {
@@ -2924,12 +2921,8 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
std::mem::swap(&mut blocks, &mut filtered_chain_segment);
let chain = self.clone();
let current_span = Span::current();
let signature_verification_future = self.spawn_blocking_handle(
move || {
let _guard = current_span.enter();
signature_verify_chain_segment(blocks, &chain)
},
move || signature_verify_chain_segment(blocks, &chain),
"signature_verify_chain_segment",
);
@@ -3019,12 +3012,10 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
block: Arc<SignedBeaconBlock<T::EthSpec>>,
) -> Result<GossipVerifiedBlock<T>, BlockError> {
let chain = self.clone();
let span = Span::current();
self.task_executor
.clone()
.spawn_blocking_handle(
move || {
let _guard = span.enter();
let slot = block.slot();
let graffiti_string = block.message().body().graffiti().as_utf8_lossy();
@@ -3332,11 +3323,9 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
let data_availability_checker = self.data_availability_checker.clone();
let current_span = Span::current();
let result = self
.task_executor
.spawn_blocking_with_rayon_async(RayonPoolType::HighPriority, move || {
let _guard = current_span.enter();
data_availability_checker.reconstruct_data_columns(&block_root)
})
.await
@@ -3801,7 +3790,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
consensus_context,
} = import_data;
// Record the time at which this block's blobs became available.
// Record the time at which this block's blobs/data columns became available.
if let Some(blobs_available) = block.blobs_available_timestamp() {
self.block_times_cache.write().set_time_blob_observed(
block_root,
@@ -3810,16 +3799,10 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
);
}
// TODO(das) record custody column available timestamp
let block_root = {
// Capture the current span before moving into the blocking task
let current_span = tracing::Span::current();
let chain = self.clone();
self.spawn_blocking_handle(
move || {
// Enter the captured span in the blocking thread
let _guard = current_span.enter();
chain.import_block(
block,
block_root,
@@ -4530,15 +4513,10 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
//
// Load the parent state from disk.
let chain = self.clone();
let span = Span::current();
let (state, state_root_opt) = self
.task_executor
.spawn_blocking_handle(
move || {
let _guard =
debug_span!(parent: span, "load_state_for_block_production").entered();
chain.load_state_for_block_production(slot)
},
move || chain.load_state_for_block_production(slot),
"load_state_for_block_production",
)
.ok_or(BlockProductionError::ShuttingDown)?
@@ -4962,13 +4940,10 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
.graffiti_calculator
.get_graffiti(graffiti_settings)
.await;
let span = Span::current();
let mut partial_beacon_block = self
.task_executor
.spawn_blocking_handle(
move || {
let _guard =
debug_span!(parent: span, "produce_partial_beacon_block").entered();
chain.produce_partial_beacon_block(
state,
state_root_opt,
@@ -5004,14 +4979,10 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
match block_contents_type {
BlockProposalContentsType::Full(block_contents) => {
let chain = self.clone();
let span = Span::current();
let beacon_block_response = self
.task_executor
.spawn_blocking_handle(
move || {
let _guard =
debug_span!(parent: span, "complete_partial_beacon_block")
.entered();
chain.complete_partial_beacon_block(
partial_beacon_block,
Some(block_contents),
@@ -5028,14 +4999,10 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
}
BlockProposalContentsType::Blinded(block_contents) => {
let chain = self.clone();
let span = Span::current();
let beacon_block_response = self
.task_executor
.spawn_blocking_handle(
move || {
let _guard =
debug_span!(parent: span, "complete_partial_beacon_block")
.entered();
chain.complete_partial_beacon_block(
partial_beacon_block,
Some(block_contents),
@@ -5053,13 +5020,10 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
}
} else {
let chain = self.clone();
let span = Span::current();
let beacon_block_response = self
.task_executor
.spawn_blocking_handle(
move || {
let _guard =
debug_span!(parent: span, "complete_partial_beacon_block").entered();
chain.complete_partial_beacon_block(
partial_beacon_block,
None,
@@ -5077,6 +5041,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
}
#[allow(clippy::too_many_arguments)]
#[instrument(skip_all, level = "debug")]
fn produce_partial_beacon_block(
self: &Arc<Self>,
mut state: BeaconState<T::EthSpec>,
@@ -5321,6 +5286,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
})
}
#[instrument(skip_all, level = "debug")]
fn complete_partial_beacon_block<Payload: AbstractExecPayload<T::EthSpec>>(
&self,
partial_beacon_block: PartialBeaconBlock<T::EthSpec>,

View File

@@ -19,7 +19,7 @@ use state_processing::{
};
use state_processing::{VerifyOperation, state_advance::complete_state_advance};
use task_executor::JoinHandle;
use tracing::{Instrument, Span, debug, debug_span, error, instrument, trace, warn};
use tracing::{Instrument, debug, debug_span, error, instrument, trace, warn};
use tree_hash::TreeHash;
use types::consts::gloas::BUILDER_INDEX_SELF_BUILD;
use types::{
@@ -87,15 +87,10 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
//
// Load the parent state from disk.
let chain = self.clone();
let span = Span::current();
let (state, state_root_opt) = self
.task_executor
.spawn_blocking_handle(
move || {
let _guard =
debug_span!(parent: span, "load_state_for_block_production").entered();
chain.load_state_for_block_production(slot)
},
move || chain.load_state_for_block_production(slot),
"load_state_for_block_production",
)
.ok_or(BlockProductionError::ShuttingDown)?
@@ -135,13 +130,10 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
.graffiti_calculator
.get_graffiti(graffiti_settings)
.await;
let span = Span::current();
let (partial_beacon_block, state) = self
.task_executor
.spawn_blocking_handle(
move || {
let _guard =
debug_span!(parent: span, "produce_partial_beacon_block_gloas").entered();
chain.produce_partial_beacon_block_gloas(
state,
state_root_opt,
@@ -175,12 +167,9 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
//
// Complete the block with the execution payload bid.
let chain = self.clone();
let span = Span::current();
self.task_executor
.spawn_blocking_handle(
move || {
let _guard =
debug_span!(parent: span, "complete_partial_beacon_block_gloas").entered();
chain.complete_partial_beacon_block_gloas(
partial_beacon_block,
execution_payload_bid,
@@ -198,6 +187,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
#[allow(clippy::too_many_arguments)]
#[allow(clippy::type_complexity)]
#[instrument(skip_all, level = "debug")]
fn produce_partial_beacon_block_gloas(
self: &Arc<Self>,
mut state: BeaconState<T::EthSpec>,
@@ -432,6 +422,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
/// - `pending_state` is the state post block application (prior to payload application)
/// - `block_value` is the consensus-layer rewards for `block`
#[allow(clippy::type_complexity)]
#[instrument(skip_all, level = "debug")]
fn complete_partial_beacon_block_gloas(
&self,
partial_beacon_block: PartialBeaconBlock<T::EthSpec>,

View File

@@ -15,6 +15,7 @@ mod gloas;
impl<T: BeaconChainTypes> BeaconChain<T> {
/// Load a beacon state from the database for block production. This is a long-running process
/// that should not be performed in an `async` context.
#[instrument(skip_all, level = "debug")]
pub(crate) fn load_state_for_block_production(
self: &Arc<Self>,
slot: Slot,

View File

@@ -58,7 +58,6 @@ use store::{
Error as StoreError, KeyValueStore, KeyValueStoreOp, StoreConfig, iter::StateRootsIterator,
};
use task_executor::{JoinHandle, ShutdownReason};
use tracing::info_span;
use tracing::{debug, error, info, instrument, warn};
use types::*;
@@ -528,22 +527,15 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
/// such a case it's critical that the `BeaconChain` keeps importing blocks so that the
/// situation can be rectified. We avoid returning an error here so that calling functions
/// can't abort block import because an error is returned here.
#[instrument(name = "lh_recompute_head_at_slot", skip(self), level = "info", fields(slot = %current_slot))]
pub async fn recompute_head_at_slot(self: &Arc<Self>, current_slot: Slot) {
let span = info_span!(
"lh_recompute_head_at_slot",
slot = %current_slot
);
metrics::inc_counter(&metrics::FORK_CHOICE_REQUESTS);
let _timer = metrics::start_timer(&metrics::FORK_CHOICE_TIMES);
let chain = self.clone();
match self
.spawn_blocking_handle(
move || {
let _guard = span.enter();
chain.recompute_head_at_slot_internal(current_slot)
},
move || chain.recompute_head_at_slot_internal(current_slot),
"recompute_head_internal",
)
.await
@@ -1379,8 +1371,8 @@ fn observe_head_block_delays<E: EthSpec, S: SlotClock>(
.as_millis() as i64,
);
// The time from the start of the slot when all blobs have been observed. Technically this
// is the time we last saw a blob related to this block/slot.
// The time from the start of the slot when all blobs/data columns have been observed. Technically this
// is the time we last saw a blob/data column related to this block/slot.
metrics::set_gauge(
&metrics::BEACON_BLOB_DELAY_ALL_OBSERVED_SLOT_START,
block_delays

View File

@@ -282,8 +282,11 @@ impl<E: EthSpec> PendingComponents<E> {
.flatten()
.map(|blob| blob.seen_timestamp())
.max(),
// TODO(das): To be fixed with https://github.com/sigp/lighthouse/pull/6850
AvailableBlockData::DataColumns(_) => None,
AvailableBlockData::DataColumns(_) => self
.verified_data_columns
.iter()
.map(|data_column| data_column.seen_timestamp())
.max(),
};
let AvailabilityPendingExecutedBlock {

View File

@@ -5,6 +5,7 @@ use crate::kzg_utils::{reconstruct_data_columns, validate_data_columns};
use crate::observed_data_sidecars::{
Error as ObservedDataSidecarsError, ObservationKey, ObservationStrategy, Observe,
};
use crate::validator_monitor::timestamp_now;
use crate::{BeaconChain, BeaconChainError, BeaconChainTypes, metrics};
use educe::Educe;
use fork_choice::ProtoBlock;
@@ -16,6 +17,7 @@ use ssz_types::VariableList;
use std::iter;
use std::marker::PhantomData;
use std::sync::Arc;
use std::time::Duration;
use tracing::{debug, instrument};
use types::data::ColumnIndex;
use types::{
@@ -320,25 +322,34 @@ impl<T: BeaconChainTypes, O: ObservationStrategy> GossipVerifiedDataColumn<T, O>
#[ssz(struct_behaviour = "transparent")]
pub struct KzgVerifiedDataColumn<E: EthSpec> {
data: Arc<DataColumnSidecar<E>>,
#[ssz(skip_serializing, skip_deserializing)]
seen_timestamp: Duration,
}
impl<E: EthSpec> KzgVerifiedDataColumn<E> {
pub fn new(
data_column: Arc<DataColumnSidecar<E>>,
kzg: &Kzg,
seen_timestamp: Duration,
) -> Result<Self, (Option<ColumnIndex>, KzgError)> {
verify_kzg_for_data_column(data_column, kzg)
verify_kzg_for_data_column(data_column, kzg, seen_timestamp)
}
/// Mark a data column as KZG verified. Caller must ONLY use this on columns constructed
/// from EL blobs.
pub fn from_execution_verified(data_column: Arc<DataColumnSidecar<E>>) -> Self {
Self { data: data_column }
Self {
data: data_column,
seen_timestamp: timestamp_now(),
}
}
/// Create a `KzgVerifiedDataColumn` from `DataColumnSidecar` for testing ONLY.
pub(crate) fn __new_for_testing(data_column: Arc<DataColumnSidecar<E>>) -> Self {
Self { data: data_column }
Self {
data: data_column,
seen_timestamp: timestamp_now(),
}
}
pub fn from_batch_with_scoring(
@@ -348,7 +359,10 @@ impl<E: EthSpec> KzgVerifiedDataColumn<E> {
verify_kzg_for_data_column_list(data_columns.iter(), kzg)?;
Ok(data_columns
.into_iter()
.map(|column| Self { data: column })
.map(|column| Self {
data: column,
seen_timestamp: timestamp_now(),
})
.collect())
}
@@ -407,6 +421,8 @@ impl<E: EthSpec> CustodyDataColumn<E> {
#[ssz(struct_behaviour = "transparent")]
pub struct KzgVerifiedCustodyDataColumn<E: EthSpec> {
data: Arc<DataColumnSidecar<E>>,
#[ssz(skip_serializing, skip_deserializing)]
seen_timestamp: Duration,
}
impl<E: EthSpec> KzgVerifiedCustodyDataColumn<E> {
@@ -414,6 +430,7 @@ impl<E: EthSpec> KzgVerifiedCustodyDataColumn<E> {
/// include this column
pub fn from_asserted_custody(kzg_verified: KzgVerifiedDataColumn<E>) -> Self {
Self {
seen_timestamp: kzg_verified.seen_timestamp,
data: kzg_verified.to_data_column(),
}
}
@@ -422,10 +439,12 @@ impl<E: EthSpec> KzgVerifiedCustodyDataColumn<E> {
pub fn new(
data_column: CustodyDataColumn<E>,
kzg: &Kzg,
seen_timestamp: Duration,
) -> Result<Self, (Option<ColumnIndex>, KzgError)> {
verify_kzg_for_data_column(data_column.clone_arc(), kzg)?;
verify_kzg_for_data_column(data_column.clone_arc(), kzg, seen_timestamp)?;
Ok(Self {
data: data_column.data,
seen_timestamp,
})
}
@@ -443,10 +462,15 @@ impl<E: EthSpec> KzgVerifiedCustodyDataColumn<E> {
spec,
)?;
let seen_timestamp = timestamp_now();
Ok(all_data_columns
.into_iter()
.map(|data| {
KzgVerifiedCustodyDataColumn::from_asserted_custody(KzgVerifiedDataColumn { data })
KzgVerifiedCustodyDataColumn::from_asserted_custody(KzgVerifiedDataColumn {
data,
seen_timestamp,
})
})
.collect::<Vec<_>>())
}
@@ -464,6 +488,10 @@ impl<E: EthSpec> KzgVerifiedCustodyDataColumn<E> {
pub fn index(&self) -> ColumnIndex {
*self.data.index()
}
pub fn seen_timestamp(&self) -> Duration {
self.seen_timestamp
}
}
/// Complete kzg verification for a `DataColumnSidecar`.
@@ -473,10 +501,14 @@ impl<E: EthSpec> KzgVerifiedCustodyDataColumn<E> {
pub fn verify_kzg_for_data_column<E: EthSpec>(
data_column: Arc<DataColumnSidecar<E>>,
kzg: &Kzg,
seen_timestamp: Duration,
) -> Result<KzgVerifiedDataColumn<E>, (Option<ColumnIndex>, KzgError)> {
let _timer = metrics::start_timer(&metrics::KZG_VERIFICATION_DATA_COLUMN_SINGLE_TIMES);
validate_data_columns(kzg, iter::once(&data_column))?;
Ok(KzgVerifiedDataColumn { data: data_column })
Ok(KzgVerifiedDataColumn {
data: data_column,
seen_timestamp,
})
}
/// Complete kzg verification for a list of `DataColumnSidecar`s.
@@ -538,8 +570,9 @@ pub fn validate_data_column_sidecar_for_gossip_fulu<T: BeaconChainTypes, O: Obse
verify_slot_higher_than_parent(&parent_block, column_slot)?;
verify_proposer_and_signature(data_column_fulu, &parent_block, chain)?;
let kzg = &chain.kzg;
let kzg_verified_data_column = verify_kzg_for_data_column(data_column.clone(), kzg)
.map_err(|(_, e)| GossipDataColumnError::InvalidKzgProof(e))?;
let kzg_verified_data_column =
verify_kzg_for_data_column(data_column.clone(), kzg, timestamp_now())
.map_err(|(_, e)| GossipDataColumnError::InvalidKzgProof(e))?;
chain
.observed_slashable

View File

@@ -32,7 +32,7 @@ use mockall_double::double;
use ssz_types::FixedVector;
use state_processing::per_block_processing::deneb::kzg_commitment_to_versioned_hash;
use std::sync::Arc;
use tracing::{Span, debug, instrument, warn};
use tracing::{debug, instrument, warn};
use types::data::{BlobSidecarError, DataColumnSidecarError};
use types::{
BeaconStateError, Blob, BlobSidecar, ColumnIndex, EthSpec, FullPayload, Hash256, KzgProofs,
@@ -356,12 +356,10 @@ async fn compute_custody_columns_to_import<T: BeaconChainTypes>(
let spec = chain_adapter.spec().clone();
let chain_adapter_cloned = chain_adapter.clone();
let custody_columns_indices = custody_columns_indices.to_vec();
let current_span = Span::current();
chain_adapter
.executor()
.spawn_blocking_handle(
move || {
let _guard = current_span.enter();
let mut timer = metrics::start_timer_vec(
&metrics::DATA_COLUMN_SIDECAR_COMPUTATION,
&[&blobs.len().to_string()],

View File

@@ -4,7 +4,7 @@ use educe::Educe;
use eth2::types::{EventKind, SseExecutionPayloadGossip};
use parking_lot::{Mutex, RwLock};
use store::DatabaseBlock;
use tracing::{Span, debug};
use tracing::debug;
use types::{
ChainSpec, EthSpec, ExecutionPayloadBid, ExecutionPayloadEnvelope, Hash256, SignedBeaconBlock,
SignedExecutionPayloadEnvelope, Slot, consts::gloas::BUILDER_INDEX_SELF_BUILD,
@@ -270,12 +270,10 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
envelope: Arc<SignedExecutionPayloadEnvelope<T::EthSpec>>,
) -> Result<GossipVerifiedEnvelope<T>, EnvelopeError> {
let chain = self.clone();
let span = Span::current();
self.task_executor
.clone()
.spawn_blocking_handle(
move || {
let _guard = span.enter();
let slot = envelope.slot();
let beacon_block_root = envelope.message.beacon_block_root;

View File

@@ -1,11 +1,12 @@
use std::sync::Arc;
use std::time::Duration;
use eth2::types::{EventKind, SseExecutionPayload};
use fork_choice::PayloadVerificationStatus;
use slot_clock::SlotClock;
use store::StoreOp;
use tracing::{debug, error, info, info_span, instrument, warn};
use types::{BeaconState, BlockImportSource, Hash256, Slot};
use types::{BeaconState, BlockImportSource, Hash256, SignedExecutionPayloadEnvelope};
use super::{
AvailableEnvelope, AvailableExecutedEnvelope, EnvelopeError, EnvelopeImportData,
@@ -191,13 +192,9 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
} = import_data;
let block_root = {
// Capture the current span before moving into the blocking task
let current_span = tracing::Span::current();
let chain = self.clone();
self.spawn_blocking_handle(
move || {
// Enter the captured span in the blocking thread
let _guard = current_span.enter();
chain.import_execution_payload_envelope(
envelope,
block_root,
@@ -225,7 +222,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
signed_envelope: AvailableEnvelope<T::EthSpec>,
block_root: Hash256,
state: BeaconState<T::EthSpec>,
_payload_verification_status: PayloadVerificationStatus,
payload_verification_status: PayloadVerificationStatus,
) -> Result<Hash256, EnvelopeError> {
// Everything in this initial section is on the hot path for processing the envelope.
// Take an upgradable read lock on fork choice so we can check if this block has already
@@ -317,8 +314,9 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
metrics::stop_timer(db_write_timer);
self.import_envelope_update_metrics_and_events(
signed_envelope,
block_root,
signed_envelope.slot(),
payload_verification_status,
envelope_time_imported,
);
@@ -327,10 +325,12 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
fn import_envelope_update_metrics_and_events(
&self,
signed_envelope: Arc<SignedExecutionPayloadEnvelope<T::EthSpec>>,
block_root: Hash256,
envelope_slot: Slot,
payload_verification_status: PayloadVerificationStatus,
envelope_time_imported: Duration,
) {
let envelope_slot = signed_envelope.slot();
let envelope_delay_total =
get_slot_delay_ms(envelope_time_imported, envelope_slot, &self.slot_clock);
@@ -349,6 +349,17 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
);
}
// TODO(gloas) emit SSE event for envelope import (similar to SseBlock for blocks).
if let Some(event_handler) = self.event_handler.as_ref()
&& event_handler.has_execution_payload_subscribers()
{
event_handler.register(EventKind::ExecutionPayload(SseExecutionPayload {
slot: envelope_slot,
builder_index: signed_envelope.message.builder_index,
block_hash: signed_envelope.block_hash(),
block_root,
state_root: signed_envelope.message.state_root,
execution_optimistic: payload_verification_status.is_optimistic(),
}));
}
}
}

View File

@@ -146,12 +146,8 @@ pub async fn publish_block<T: BeaconChainTypes, B: IntoGossipVerifiedBlock<T>>(
let slot = block.message().slot();
let sender_clone = network_tx.clone();
let build_sidecar_task_handle = spawn_build_data_sidecar_task(
chain.clone(),
block.clone(),
unverified_blobs,
current_span.clone(),
)?;
let build_sidecar_task_handle =
spawn_build_data_sidecar_task(chain.clone(), block.clone(), unverified_blobs)?;
// Gossip verify the block and blobs/data columns separately.
let gossip_verified_block_result = unverified_block.into_gossip_verified_block(&chain);
@@ -358,7 +354,6 @@ fn spawn_build_data_sidecar_task<T: BeaconChainTypes>(
chain: Arc<BeaconChain<T>>,
block: Arc<SignedBeaconBlock<T::EthSpec, FullPayload<T::EthSpec>>>,
proofs_and_blobs: UnverifiedBlobs<T>,
current_span: Span,
) -> Result<impl Future<Output = BuildDataSidecarTaskResult<T>>, Rejection> {
chain
.clone()
@@ -368,7 +363,7 @@ fn spawn_build_data_sidecar_task<T: BeaconChainTypes>(
let Some((kzg_proofs, blobs)) = proofs_and_blobs else {
return Ok((vec![], vec![]));
};
let _guard = debug_span!(parent: current_span, "build_data_sidecars").entered();
let _span = debug_span!("build_data_sidecars").entered();
let peer_das_enabled = chain.spec.is_peer_das_enabled_for_epoch(block.epoch());
if !peer_das_enabled {