Breakup RPCBlock into LookupBlock & RangeSyncBlock (#8860)

Co-Authored-By: Mark Mackey <mark@sigmaprime.io>
This commit is contained in:
ethDreamer
2026-03-13 14:22:29 -05:00
committed by GitHub
parent 02137492f3
commit 6ca610d918
25 changed files with 505 additions and 669 deletions

View File

@@ -13,7 +13,7 @@ use crate::block_verification::{
signature_verify_chain_segment, verify_header_signature,
};
use crate::block_verification_types::{
AsBlock, AvailableExecutedBlock, BlockImportData, ExecutedBlock, RpcBlock,
AsBlock, AvailableExecutedBlock, BlockImportData, ExecutedBlock, RangeSyncBlock,
};
pub use crate::canonical_head::CanonicalHead;
use crate::chain_config::ChainConfig;
@@ -137,7 +137,7 @@ use types::*;
pub type ForkChoiceError = fork_choice::Error<crate::ForkChoiceStoreError>;
/// Alias to appease clippy.
type HashBlockTuple<E> = (Hash256, RpcBlock<E>);
type HashBlockTuple<E> = (Hash256, RangeSyncBlock<E>);
// These keys are all zero because they get stored in different columns, see `DBColumn` type.
pub const BEACON_CHAIN_DB_KEY: Hash256 = Hash256::ZERO;
@@ -2746,7 +2746,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
/// This method is potentially long-running and should not run on the core executor.
pub fn filter_chain_segment(
self: &Arc<Self>,
chain_segment: Vec<RpcBlock<T::EthSpec>>,
chain_segment: Vec<RangeSyncBlock<T::EthSpec>>,
) -> Result<Vec<HashBlockTuple<T::EthSpec>>, Box<ChainSegmentResult>> {
// This function will never import any blocks.
let imported_blocks = vec![];
@@ -2855,7 +2855,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
/// `Self::process_block`.
pub async fn process_chain_segment(
self: &Arc<Self>,
chain_segment: Vec<RpcBlock<T::EthSpec>>,
chain_segment: Vec<RangeSyncBlock<T::EthSpec>>,
notify_execution_layer: NotifyExecutionLayer,
) -> ChainSegmentResult {
for block in chain_segment.iter() {

View File

@@ -50,7 +50,7 @@
use crate::beacon_snapshot::PreProcessingSnapshot;
use crate::blob_verification::GossipBlobError;
use crate::block_verification_types::{AsBlock, BlockImportData, RpcBlock};
use crate::block_verification_types::{AsBlock, BlockImportData, LookupBlock, RangeSyncBlock};
use crate::data_availability_checker::{
AvailabilityCheckError, AvailableBlock, AvailableBlockData, MaybeAvailableBlock,
};
@@ -585,7 +585,7 @@ pub(crate) fn process_block_slash_info<T: BeaconChainTypes, TErr: BlockBlobError
/// will be returned.
#[instrument(skip_all)]
pub fn signature_verify_chain_segment<T: BeaconChainTypes>(
mut chain_segment: Vec<(Hash256, RpcBlock<T::EthSpec>)>,
mut chain_segment: Vec<(Hash256, RangeSyncBlock<T::EthSpec>)>,
chain: &BeaconChain<T>,
) -> Result<Vec<SignatureVerifiedBlock<T>>, BlockError> {
if chain_segment.is_empty() {
@@ -616,24 +616,14 @@ pub fn signature_verify_chain_segment<T: BeaconChainTypes>(
let consensus_context =
ConsensusContext::new(block.slot()).set_current_block_root(block_root);
match block {
RpcBlock::FullyAvailable(available_block) => {
available_blocks.push(available_block.clone());
signature_verified_blocks.push(SignatureVerifiedBlock {
block: MaybeAvailableBlock::Available(available_block),
block_root,
parent: None,
consensus_context,
});
}
RpcBlock::BlockOnly { .. } => {
// RangeSync and BackfillSync already ensure that the chain segment is fully available
// so this shouldn't be possible in practice.
return Err(BlockError::InternalError(
"Chain segment is not fully available".to_string(),
));
}
}
let available_block = block.into_available_block();
available_blocks.push(available_block.clone());
signature_verified_blocks.push(SignatureVerifiedBlock {
block: MaybeAvailableBlock::Available(available_block),
block_root,
parent: None,
consensus_context,
});
}
chain
@@ -1300,11 +1290,11 @@ impl<T: BeaconChainTypes> IntoExecutionPendingBlock<T> for SignatureVerifiedBloc
}
}
impl<T: BeaconChainTypes> IntoExecutionPendingBlock<T> for RpcBlock<T::EthSpec> {
impl<T: BeaconChainTypes> IntoExecutionPendingBlock<T> for RangeSyncBlock<T::EthSpec> {
/// Verifies the `SignedBeaconBlock` by first transforming it into a `SignatureVerifiedBlock`
/// and then using that implementation of `IntoExecutionPendingBlock` to complete verification.
#[instrument(
name = "rpc_block_into_execution_pending_block_slashable",
name = "range_sync_block_into_execution_pending_block_slashable",
level = "debug"
skip_all,
)]
@@ -1318,24 +1308,51 @@ impl<T: BeaconChainTypes> IntoExecutionPendingBlock<T> for RpcBlock<T::EthSpec>
let block_root = check_block_relevancy(self.as_block(), block_root, chain)
.map_err(|e| BlockSlashInfo::SignatureNotChecked(self.signed_block_header(), e))?;
let maybe_available_block = match &self {
RpcBlock::FullyAvailable(available_block) => {
chain
.data_availability_checker
.verify_kzg_for_available_block(available_block)
.map_err(|e| {
BlockSlashInfo::SignatureNotChecked(
self.signed_block_header(),
BlockError::AvailabilityCheck(e),
)
})?;
MaybeAvailableBlock::Available(available_block.clone())
}
// No need to perform KZG verification unless we have a fully available block
RpcBlock::BlockOnly { block, block_root } => MaybeAvailableBlock::AvailabilityPending {
block_root: *block_root,
block: block.clone(),
},
let available_block = self.into_available_block();
chain
.data_availability_checker
.verify_kzg_for_available_block(&available_block)
.map_err(|e| {
BlockSlashInfo::SignatureNotChecked(
available_block.as_block().signed_block_header(),
BlockError::AvailabilityCheck(e),
)
})?;
let maybe_available_block = MaybeAvailableBlock::Available(available_block);
SignatureVerifiedBlock::check_slashable(maybe_available_block, block_root, chain)?
.into_execution_pending_block_slashable(block_root, chain, notify_execution_layer)
}
fn block(&self) -> &SignedBeaconBlock<T::EthSpec> {
self.as_block()
}
fn block_cloned(&self) -> Arc<SignedBeaconBlock<T::EthSpec>> {
self.block_cloned()
}
}
impl<T: BeaconChainTypes> IntoExecutionPendingBlock<T> for LookupBlock<T::EthSpec> {
/// Verifies the `SignedBeaconBlock` by first transforming it into a `SignatureVerifiedBlock`
/// and then using that implementation of `IntoExecutionPendingBlock` to complete verification.
#[instrument(
name = "lookup_block_into_execution_pending_block_slashable",
level = "debug"
skip_all,
)]
fn into_execution_pending_block_slashable(
self,
block_root: Hash256,
chain: &Arc<BeaconChain<T>>,
notify_execution_layer: NotifyExecutionLayer,
) -> Result<ExecutionPendingBlock<T>, BlockSlashInfo<BlockError>> {
// Perform an early check to prevent wasting time on irrelevant blocks.
let block_root = check_block_relevancy(self.as_block(), block_root, chain)
.map_err(|e| BlockSlashInfo::SignatureNotChecked(self.signed_block_header(), e))?;
let maybe_available_block = MaybeAvailableBlock::AvailabilityPending {
block_root,
block: self.block_cloned(),
};
SignatureVerifiedBlock::check_slashable(maybe_available_block, block_root, chain)?

View File

@@ -13,76 +13,70 @@ use types::{
SignedBeaconBlock, SignedBeaconBlockHeader, Slot,
};
/// A block that has been received over RPC. It has 2 internal variants:
///
/// 1. `FullyAvailable`: A fully available block. This can either be a pre-deneb block, a
/// post-Deneb block with blobs, a post-Fulu block with the columns the node is required to custody,
/// or a post-Deneb block that doesn't require blobs/columns. Hence, it is fully self contained w.r.t
/// verification. i.e. this block has all the required data to get verified and imported into fork choice.
///
/// 2. `BlockOnly`: This is a post-deneb block that requires blobs to be considered fully available.
#[derive(Clone, Educe)]
#[educe(Hash(bound(E: EthSpec)))]
pub enum RpcBlock<E: EthSpec> {
FullyAvailable(AvailableBlock<E>),
BlockOnly {
block: Arc<SignedBeaconBlock<E>>,
block_root: Hash256,
},
/// A wrapper around a `SignedBeaconBlock`. This varaint is constructed
/// when lookup sync only fetches a single block. It does not contain
/// any blobs or data columns.
pub struct LookupBlock<E: EthSpec> {
block: Arc<SignedBeaconBlock<E>>,
block_root: Hash256,
}
impl<E: EthSpec> Debug for RpcBlock<E> {
impl<E: EthSpec> LookupBlock<E> {
pub fn new(block: Arc<SignedBeaconBlock<E>>) -> Self {
let block_root = block.canonical_root();
Self { block, block_root }
}
pub fn block(&self) -> &SignedBeaconBlock<E> {
&self.block
}
pub fn block_root(&self) -> Hash256 {
self.block_root
}
pub fn block_cloned(&self) -> Arc<SignedBeaconBlock<E>> {
self.block.clone()
}
}
/// A fully available block that has been constructed by range sync.
/// The block contains all the data required to import into fork choice.
/// This includes any and all blobs/columns required, including zero if
/// none are required. This can happen if the block is pre-deneb or if
/// it's simply past the DA boundary.
#[derive(Clone, Educe)]
#[educe(Hash(bound(E: EthSpec)))]
pub struct RangeSyncBlock<E: EthSpec> {
block: AvailableBlock<E>,
}
impl<E: EthSpec> Debug for RangeSyncBlock<E> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "RpcBlock({:?})", self.block_root())
}
}
impl<E: EthSpec> RpcBlock<E> {
impl<E: EthSpec> RangeSyncBlock<E> {
pub fn block_root(&self) -> Hash256 {
match self {
RpcBlock::FullyAvailable(available_block) => available_block.block_root(),
RpcBlock::BlockOnly { block_root, .. } => *block_root,
}
self.block.block_root()
}
pub fn as_block(&self) -> &SignedBeaconBlock<E> {
match self {
RpcBlock::FullyAvailable(available_block) => available_block.block(),
RpcBlock::BlockOnly { block, .. } => block,
}
self.block.block()
}
pub fn block_cloned(&self) -> Arc<SignedBeaconBlock<E>> {
match self {
RpcBlock::FullyAvailable(available_block) => available_block.block_cloned(),
RpcBlock::BlockOnly { block, .. } => block.clone(),
}
self.block.block_cloned()
}
pub fn block_data(&self) -> Option<&AvailableBlockData<E>> {
match self {
RpcBlock::FullyAvailable(available_block) => Some(available_block.data()),
RpcBlock::BlockOnly { .. } => None,
}
pub fn block_data(&self) -> &AvailableBlockData<E> {
self.block.data()
}
}
impl<E: EthSpec> RpcBlock<E> {
/// Constructs an `RpcBlock` from a block and optional availability data.
///
/// This function creates an RpcBlock which can be in one of two states:
/// - `FullyAvailable`: When `block_data` is provided, the block contains all required
/// data for verification.
/// - `BlockOnly`: When `block_data` is `None`, the block may still need additional
/// data to be considered fully available (used during block lookups or when blobs
/// will arrive separately).
///
/// # Validation
///
/// When `block_data` is provided, this function validates that:
/// - Block data is not provided when not required.
/// - Required blobs are present and match the expected count.
/// - Required custody columns are included based on the nodes custody requirements.
impl<E: EthSpec> RangeSyncBlock<E> {
/// Constructs an `RangeSyncBlock` from a block and availability data.
///
/// # Errors
///
@@ -92,62 +86,41 @@ impl<E: EthSpec> RpcBlock<E> {
/// - `MissingCustodyColumns`: Block requires custody columns but they are incomplete.
pub fn new<T>(
block: Arc<SignedBeaconBlock<E>>,
block_data: Option<AvailableBlockData<E>>,
block_data: AvailableBlockData<E>,
da_checker: &DataAvailabilityChecker<T>,
spec: Arc<ChainSpec>,
) -> Result<Self, AvailabilityCheckError>
where
T: BeaconChainTypes<EthSpec = E>,
{
match block_data {
Some(block_data) => Ok(RpcBlock::FullyAvailable(AvailableBlock::new(
block, block_data, da_checker, spec,
)?)),
None => Ok(RpcBlock::BlockOnly {
block_root: block.canonical_root(),
block,
}),
}
let available_block = AvailableBlock::new(block, block_data, da_checker, spec)?;
Ok(Self {
block: available_block,
})
}
#[allow(clippy::type_complexity)]
pub fn deconstruct(
self,
) -> (
Hash256,
Arc<SignedBeaconBlock<E>>,
Option<AvailableBlockData<E>>,
) {
match self {
RpcBlock::FullyAvailable(available_block) => {
let (block_root, block, block_data) = available_block.deconstruct();
(block_root, block, Some(block_data))
}
RpcBlock::BlockOnly { block, block_root } => (block_root, block, None),
}
pub fn deconstruct(self) -> (Hash256, Arc<SignedBeaconBlock<E>>, AvailableBlockData<E>) {
self.block.deconstruct()
}
pub fn n_blobs(&self) -> usize {
if let Some(block_data) = self.block_data() {
match block_data {
AvailableBlockData::NoData | AvailableBlockData::DataColumns(_) => 0,
AvailableBlockData::Blobs(blobs) => blobs.len(),
}
} else {
0
match self.block_data() {
AvailableBlockData::NoData | AvailableBlockData::DataColumns(_) => 0,
AvailableBlockData::Blobs(blobs) => blobs.len(),
}
}
pub fn n_data_columns(&self) -> usize {
if let Some(block_data) = self.block_data() {
match block_data {
AvailableBlockData::NoData | AvailableBlockData::Blobs(_) => 0,
AvailableBlockData::DataColumns(columns) => columns.len(),
}
} else {
0
match self.block_data() {
AvailableBlockData::NoData | AvailableBlockData::Blobs(_) => 0,
AvailableBlockData::DataColumns(columns) => columns.len(),
}
}
pub fn into_available_block(self) -> AvailableBlock<E> {
self.block
}
}
/// A block that has gone through all pre-deneb block processing checks including block processing
@@ -412,7 +385,7 @@ impl<E: EthSpec> AsBlock<E> for AvailableBlock<E> {
}
}
impl<E: EthSpec> AsBlock<E> for RpcBlock<E> {
impl<E: EthSpec> AsBlock<E> for RangeSyncBlock<E> {
fn slot(&self) -> Slot {
self.as_block().slot()
}
@@ -432,24 +405,42 @@ impl<E: EthSpec> AsBlock<E> for RpcBlock<E> {
self.as_block().message()
}
fn as_block(&self) -> &SignedBeaconBlock<E> {
match self {
Self::BlockOnly {
block,
block_root: _,
} => block,
Self::FullyAvailable(available_block) => available_block.block(),
}
self.block.as_block()
}
fn block_cloned(&self) -> Arc<SignedBeaconBlock<E>> {
match self {
RpcBlock::FullyAvailable(available_block) => available_block.block_cloned(),
RpcBlock::BlockOnly {
block,
block_root: _,
} => block.clone(),
}
self.block.block_cloned()
}
fn canonical_root(&self) -> Hash256 {
self.as_block().canonical_root()
self.block.block_root()
}
}
impl<E: EthSpec> AsBlock<E> for LookupBlock<E> {
fn slot(&self) -> Slot {
self.block().slot()
}
fn epoch(&self) -> Epoch {
self.block().epoch()
}
fn parent_root(&self) -> Hash256 {
self.block().parent_root()
}
fn state_root(&self) -> Hash256 {
self.block().state_root()
}
fn signed_block_header(&self) -> SignedBeaconBlockHeader {
self.block().signed_block_header()
}
fn message(&self) -> BeaconBlockRef<'_, E> {
self.block().message()
}
fn as_block(&self) -> &SignedBeaconBlock<E> {
self.block()
}
fn block_cloned(&self) -> Arc<SignedBeaconBlock<E>> {
self.block_cloned()
}
fn canonical_root(&self) -> Hash256 {
self.block_root
}
}

View File

@@ -891,7 +891,7 @@ impl<E: EthSpec> MaybeAvailableBlock<E> {
mod test {
use super::*;
use crate::CustodyContext;
use crate::block_verification_types::RpcBlock;
use crate::block_verification_types::RangeSyncBlock;
use crate::custody_context::NodeCustodyType;
use crate::data_column_verification::CustodyDataColumn;
use crate::test_utils::{
@@ -1085,7 +1085,7 @@ mod test {
/// Regression test for KZG verification truncation bug (https://github.com/sigp/lighthouse/pull/7927)
#[test]
fn verify_kzg_for_rpc_blocks_should_not_truncate_data_columns_fulu() {
fn verify_kzg_for_range_sync_blocks_should_not_truncate_data_columns_fulu() {
let spec = Arc::new(ForkName::Fulu.make_genesis_spec(E::default_spec()));
let mut rng = StdRng::seed_from_u64(0xDEADBEEF0BAD5EEDu64);
let da_checker = new_da_checker(spec.clone());
@@ -1128,17 +1128,14 @@ mod test {
let block_data = AvailableBlockData::new_with_data_columns(custody_columns);
let da_checker = Arc::new(new_da_checker(spec.clone()));
RpcBlock::new(Arc::new(block), Some(block_data), &da_checker, spec.clone())
RangeSyncBlock::new(Arc::new(block), block_data, &da_checker, spec.clone())
.expect("should create RPC block with custody columns")
})
.collect::<Vec<_>>();
let available_blocks = blocks_with_columns
.iter()
.filter_map(|block| match block {
RpcBlock::FullyAvailable(available_block) => Some(available_block.clone()),
RpcBlock::BlockOnly { .. } => None,
})
.into_iter()
.map(|block| block.into_available_block())
.collect::<Vec<_>>();
// WHEN verifying all blocks together (totalling 256 data columns)

View File

@@ -1,5 +1,5 @@
use crate::blob_verification::GossipVerifiedBlob;
use crate::block_verification_types::{AsBlock, AvailableBlockData, RpcBlock};
use crate::block_verification_types::{AsBlock, AvailableBlockData, LookupBlock, RangeSyncBlock};
use crate::custody_context::NodeCustodyType;
use crate::data_availability_checker::DataAvailabilityChecker;
use crate::graffiti_calculator::GraffitiSettings;
@@ -823,20 +823,20 @@ where
mock_builder_server
}
pub fn get_head_block(&self) -> RpcBlock<E> {
pub fn get_head_block(&self) -> RangeSyncBlock<E> {
let block = self.chain.head_beacon_block();
let block_root = block.canonical_root();
self.build_rpc_block_from_store_blobs(Some(block_root), block)
self.build_range_sync_block_from_store_blobs(Some(block_root), block)
}
pub fn get_full_block(&self, block_root: &Hash256) -> RpcBlock<E> {
pub fn get_full_block(&self, block_root: &Hash256) -> RangeSyncBlock<E> {
let block = self
.chain
.get_blinded_block(block_root)
.unwrap()
.unwrap_or_else(|| panic!("block root does not exist in harness {block_root:?}"));
let full_block = self.chain.store.make_full_block(block_root, block).unwrap();
self.build_rpc_block_from_store_blobs(Some(*block_root), Arc::new(full_block))
self.build_range_sync_block_from_store_blobs(Some(*block_root), Arc::new(full_block))
}
pub fn get_all_validators(&self) -> Vec<usize> {
@@ -1340,15 +1340,12 @@ where
let signed_block = self.sign_beacon_block(block, state);
let block_root = signed_block.canonical_root();
let rpc_block = RpcBlock::BlockOnly {
block_root,
block: Arc::new(signed_block),
};
let lookup_block = LookupBlock::new(Arc::new(signed_block));
self.chain.slot_clock.set_slot(slot.as_u64());
self.chain
.process_block(
block_root,
rpc_block,
lookup_block,
NotifyExecutionLayer::No,
BlockImportSource::Lookup,
|| Ok(()),
@@ -2607,20 +2604,33 @@ where
.blob_kzg_commitments()
.is_ok_and(|c| !c.is_empty());
let is_available = !has_blob_commitments || blob_items.is_some();
let block_hash: SignedBeaconBlockHash = if !is_available {
self.chain
.process_block(
block_root,
LookupBlock::new(block),
NotifyExecutionLayer::Yes,
BlockImportSource::Lookup,
|| Ok(()),
)
.await?
.try_into()
.expect("block blobs are available")
} else {
let range_sync_block = self.build_range_sync_block_from_blobs(block, blob_items)?;
self.chain
.process_block(
block_root,
range_sync_block,
NotifyExecutionLayer::Yes,
BlockImportSource::RangeSync,
|| Ok(()),
)
.await?
.try_into()
.expect("block blobs are available")
};
let rpc_block = self.build_rpc_block_from_blobs(block, blob_items, is_available)?;
let block_hash: SignedBeaconBlockHash = self
.chain
.process_block(
block_root,
rpc_block,
NotifyExecutionLayer::Yes,
BlockImportSource::RangeSync,
|| Ok(()),
)
.await?
.try_into()
.expect("block blobs are available");
self.chain.recompute_head_at_current_slot().await;
Ok(block_hash)
}
@@ -2640,19 +2650,33 @@ where
.blob_kzg_commitments()
.is_ok_and(|c| !c.is_empty());
let is_available = !has_blob_commitments || blob_items.is_some();
let rpc_block = self.build_rpc_block_from_blobs(block, blob_items, is_available)?;
let block_hash: SignedBeaconBlockHash = self
.chain
.process_block(
block_root,
rpc_block,
NotifyExecutionLayer::Yes,
BlockImportSource::RangeSync,
|| Ok(()),
)
.await?
.try_into()
.expect("block blobs are available");
let block_hash: SignedBeaconBlockHash = if is_available {
let range_sync_block = self.build_range_sync_block_from_blobs(block, blob_items)?;
self.chain
.process_block(
block_root,
range_sync_block,
NotifyExecutionLayer::Yes,
BlockImportSource::RangeSync,
|| Ok(()),
)
.await?
.try_into()
.expect("block blobs are available")
} else {
self.chain
.process_block(
block_root,
LookupBlock::new(block),
NotifyExecutionLayer::Yes,
BlockImportSource::Lookup,
|| Ok(()),
)
.await?
.try_into()
.expect("block blobs are available")
};
self.chain.recompute_head_at_current_slot().await;
Ok(block_hash)
}
@@ -2735,13 +2759,13 @@ where
state_root
}
/// Builds an `Rpc` block from a `SignedBeaconBlock` and blobs or data columns retrieved from
/// Builds a `RangeSyncBlock` from a `SignedBeaconBlock` and blobs or data columns retrieved from
/// the database.
pub fn build_rpc_block_from_store_blobs(
pub fn build_range_sync_block_from_store_blobs(
&self,
block_root: Option<Hash256>,
block: Arc<SignedBeaconBlock<E>>,
) -> RpcBlock<E> {
) -> RangeSyncBlock<E> {
let block_root = block_root.unwrap_or_else(|| get_block_root(&block));
let has_blobs = block
.message()
@@ -2749,9 +2773,9 @@ where
.blob_kzg_commitments()
.is_ok_and(|c| !c.is_empty());
if !has_blobs {
return RpcBlock::new(
return RangeSyncBlock::new(
block,
Some(AvailableBlockData::NoData),
AvailableBlockData::NoData,
&self.chain.data_availability_checker,
self.chain.spec.clone(),
)
@@ -2768,9 +2792,9 @@ where
.unwrap();
let custody_columns = columns.into_iter().collect::<Vec<_>>();
let block_data = AvailableBlockData::new_with_data_columns(custody_columns);
RpcBlock::new(
RangeSyncBlock::new(
block,
Some(block_data),
block_data,
&self.chain.data_availability_checker,
self.chain.spec.clone(),
)
@@ -2783,9 +2807,9 @@ where
AvailableBlockData::NoData
};
RpcBlock::new(
RangeSyncBlock::new(
block,
Some(block_data),
block_data,
&self.chain.data_availability_checker,
self.chain.spec.clone(),
)
@@ -2793,18 +2817,17 @@ where
}
}
/// Builds an `RpcBlock` from a `SignedBeaconBlock` and `BlobsList`.
pub fn build_rpc_block_from_blobs(
/// Builds a `RangeSyncBlock` from a `SignedBeaconBlock` and `BlobsList`.
pub fn build_range_sync_block_from_blobs(
&self,
block: Arc<SignedBeaconBlock<E, FullPayload<E>>>,
blob_items: Option<(KzgProofs<E>, BlobsList<E>)>,
is_available: bool,
) -> Result<RpcBlock<E>, BlockError> {
) -> Result<RangeSyncBlock<E>, BlockError> {
Ok(if self.spec.is_peer_das_enabled_for_epoch(block.epoch()) {
let epoch = block.slot().epoch(E::slots_per_epoch());
let sampling_columns = self.chain.sampling_columns_for_epoch(epoch);
if blob_items.is_some_and(|(_, blobs)| !blobs.is_empty()) {
if blob_items.is_some_and(|(kzg_proofs, _)| !kzg_proofs.is_empty()) {
// Note: this method ignores the actual custody columns and just take the first
// `sampling_column_count` for testing purpose only, because the chain does not
// currently have any knowledge of the columns being custodied.
@@ -2812,33 +2835,17 @@ where
.into_iter()
.filter(|d| sampling_columns.contains(d.index()))
.collect::<Vec<_>>();
if is_available {
let block_data = AvailableBlockData::new_with_data_columns(columns);
RpcBlock::new(
block,
Some(block_data),
&self.chain.data_availability_checker,
self.chain.spec.clone(),
)?
} else {
RpcBlock::new(
block,
None,
&self.chain.data_availability_checker,
self.chain.spec.clone(),
)?
}
} else if is_available {
RpcBlock::new(
let block_data = AvailableBlockData::new_with_data_columns(columns);
RangeSyncBlock::new(
block,
Some(AvailableBlockData::NoData),
block_data,
&self.chain.data_availability_checker,
self.chain.spec.clone(),
)?
} else {
RpcBlock::new(
RangeSyncBlock::new(
block,
None,
AvailableBlockData::NoData,
&self.chain.data_availability_checker,
self.chain.spec.clone(),
)?
@@ -2850,27 +2857,18 @@ where
})
.transpose()
.unwrap();
if is_available {
let block_data = if let Some(blobs) = blobs {
AvailableBlockData::new_with_blobs(blobs)
} else {
AvailableBlockData::NoData
};
RpcBlock::new(
block,
Some(block_data),
&self.chain.data_availability_checker,
self.chain.spec.clone(),
)?
let block_data = if let Some(blobs) = blobs {
AvailableBlockData::new_with_blobs(blobs)
} else {
RpcBlock::new(
block,
None,
&self.chain.data_availability_checker,
self.chain.spec.clone(),
)?
}
AvailableBlockData::NoData
};
RangeSyncBlock::new(
block,
block_data,
&self.chain.data_availability_checker,
self.chain.spec.clone(),
)?
})
}