diff --git a/beacon_node/beacon_chain/src/beacon_chain.rs b/beacon_node/beacon_chain/src/beacon_chain.rs index caa73401e..e5bb002d5 100644 --- a/beacon_node/beacon_chain/src/beacon_chain.rs +++ b/beacon_node/beacon_chain/src/beacon_chain.rs @@ -2486,7 +2486,7 @@ impl BeaconChain { while let Some((_root, block)) = filtered_chain_segment.first() { // Determine the epoch of the first block in the remaining segment. - let start_epoch = block.slot().epoch(T::EthSpec::slots_per_epoch()); + let start_epoch = block.epoch(); // The `last_index` indicates the position of the first block in an epoch greater // than the current epoch: partitioning the blocks into a run of blocks in the same @@ -2494,9 +2494,7 @@ impl BeaconChain { // the same `BeaconState`. let last_index = filtered_chain_segment .iter() - .position(|(_root, block)| { - block.slot().epoch(T::EthSpec::slots_per_epoch()) > start_epoch - }) + .position(|(_root, block)| block.epoch() > start_epoch) .unwrap_or(filtered_chain_segment.len()); let mut blocks = filtered_chain_segment.split_off(last_index); @@ -3162,7 +3160,7 @@ impl BeaconChain { // Sync aggregate. if let Ok(sync_aggregate) = block.body().sync_aggregate() { // `SyncCommittee` for the sync_aggregate should correspond to the duty slot - let duty_epoch = block.slot().epoch(T::EthSpec::slots_per_epoch()); + let duty_epoch = block.epoch(); match self.sync_committee_at_epoch(duty_epoch) { Ok(sync_committee) => { @@ -3429,7 +3427,7 @@ impl BeaconChain { parent_block_slot: Slot, ) { // Do not write to eth1 finalization cache for blocks older than 5 epochs. - if block.slot().epoch(T::EthSpec::slots_per_epoch()) + 5 < current_epoch { + if block.epoch() + 5 < current_epoch { return; } @@ -5860,6 +5858,29 @@ impl BeaconChain { .flatten() } + /// The epoch since which we cater blob data upon a request 'ByRoot'. + /// `None` if the `Eip4844` fork is disabled. + pub fn data_availability_boundary_by_root_rpc_request(&self) -> Option { + self.spec + .eip4844_fork_epoch + .map(|fork_epoch| { + self.epoch().ok().map(|current_epoch| { + vec![ + fork_epoch, + current_epoch.saturating_sub(*MIN_EPOCHS_FOR_BLOBS_SIDECARS_REQUESTS), + self.canonical_head + .cached_head() + .finalized_checkpoint() + .epoch, + ] + .into_iter() + .max() + }) + }) + .flatten() + .flatten() + } + /// Returns `true` if we are at or past the `Eip4844` fork. This will always return `false` if /// the `Eip4844` fork is disabled. pub fn is_data_availability_check_required(&self) -> Result { diff --git a/beacon_node/network/src/beacon_processor/worker/rpc_methods.rs b/beacon_node/network/src/beacon_processor/worker/rpc_methods.rs index f08ffe1e6..62e6934ff 100644 --- a/beacon_node/network/src/beacon_processor/worker/rpc_methods.rs +++ b/beacon_node/network/src/beacon_processor/worker/rpc_methods.rs @@ -246,19 +246,39 @@ impl Worker { "request_root" => ?root ); } - Ok((Some(_), None)) => { - debug!( - self.log, - "Peer requested block and blob, but no blob found"; - "peer" => %peer_id, - "request_root" => ?root - ); - self.send_error_response( - peer_id, - RPCResponseErrorCode::ResourceUnavailable, - "No blob for requested block".into(), - request_id, - ); + Ok((Some(block), None)) => { + let data_availability_boundary_by_root = self.chain.data_availability_boundary_by_root_rpc_request(); + let block_epoch = block.epoch(); + + if Some(block_epoch) >= data_availability_boundary_by_root { + debug!( + self.log, + "Peer requested block and blob that should be available, but no blob found"; + "peer" => %peer_id, + "request_root" => ?root, + "data_availability_boundary_by_root" => data_availability_boundary_by_root, + ); + self.send_error_response( + peer_id, + RPCResponseErrorCode::ResourceUnavailable, + "No blob for requested block.".into(), + request_id, + ); + } else { + debug!( + self.log, + "Peer requested block and blob older than the data availability boundary for ByRoot request, no blob found"; + "peer" => %peer_id, + "request_root" => ?root, + "data_availability_boundary_by_root" => data_availability_boundary_by_root, + ); + self.send_error_response( + peer_id, + RPCResponseErrorCode::ResourceUnavailable, + format!("No blob for requested block. Requested blob is older than the data availability boundary for a ByRoot request, currently at epoch {:?}", data_availability_boundary_by_root), + request_id, + ); + } send_response = false; break; } @@ -592,15 +612,33 @@ impl Worker { "start_slot" => req.start_slot, ); + let start_slot = Slot::from(req.start_slot); + let start_epoch = start_slot.epoch(T::EthSpec::slots_per_epoch()); + let data_availability_boundary = self.chain.data_availability_boundary(); + + if Some(start_epoch) < data_availability_boundary { + let oldest_blob_slot = self + .chain + .store + .get_blob_info() + .map(|blob_info| blob_info.oldest_blob_slot); + + debug!(self.log, "Range request start slot is older than data availability boundary"; "requested_slot" => req.start_slot, "oldest_known_slot" => ?oldest_blob_slot, "data_availability_boundary" => data_availability_boundary); + + return self.send_error_response( + peer_id, + RPCResponseErrorCode::ResourceUnavailable, + format!("Requested start slot in epoch {}. Data availability boundary is currently at epoch {:?}", start_epoch, data_availability_boundary), + request_id, + ); + } + // Should not send more than max request blocks if req.count > MAX_REQUEST_BLOBS_SIDECARS { req.count = MAX_REQUEST_BLOBS_SIDECARS; } - let forwards_block_root_iter = match self - .chain - .forwards_iter_block_roots(Slot::from(req.start_slot)) - { + let forwards_block_root_iter = match self.chain.forwards_iter_block_roots(start_slot) { Ok(iter) => iter, Err(BeaconChainError::HistoricalBlockError( HistoricalBlockError::BlockOutOfRange { diff --git a/consensus/types/src/signed_beacon_block.rs b/consensus/types/src/signed_beacon_block.rs index f57169c72..89d063365 100644 --- a/consensus/types/src/signed_beacon_block.rs +++ b/consensus/types/src/signed_beacon_block.rs @@ -195,7 +195,7 @@ impl> SignedBeaconBlock } let domain = spec.get_domain( - self.slot().epoch(E::slots_per_epoch()), + self.epoch(), Domain::BeaconProposer, fork, genesis_validators_root, @@ -227,6 +227,11 @@ impl> SignedBeaconBlock self.message().slot() } + /// Convenience accessor for the block's epoch. + pub fn epoch(&self) -> Epoch { + self.message().slot().epoch(E::slots_per_epoch()) + } + /// Convenience accessor for the block's parent root. pub fn parent_root(&self) -> Hash256 { self.message().parent_root() diff --git a/consensus/types/src/signed_block_and_blobs.rs b/consensus/types/src/signed_block_and_blobs.rs index c589fbcfe..9d8f4f627 100644 --- a/consensus/types/src/signed_block_and_blobs.rs +++ b/consensus/types/src/signed_block_and_blobs.rs @@ -1,5 +1,7 @@ use crate::signed_beacon_block::BlobReconstructionError; -use crate::{BlobsSidecar, EthSpec, Hash256, SignedBeaconBlock, SignedBeaconBlockEip4844, Slot}; +use crate::{ + BlobsSidecar, Epoch, EthSpec, Hash256, SignedBeaconBlock, SignedBeaconBlockEip4844, Slot, +}; use derivative::Derivative; use serde_derive::{Deserialize, Serialize}; use ssz::{Decode, DecodeError}; @@ -74,6 +76,14 @@ impl BlockWrapper { } } } + pub fn epoch(&self) -> Epoch { + match &self.0 { + BlockWrapperInner::Block(block) => block.epoch(), + BlockWrapperInner::BlockAndBlob(block_sidecar_pair) => { + block_sidecar_pair.beacon_block.epoch() + } + } + } pub fn block(&self) -> &SignedBeaconBlock { match &self.0 { BlockWrapperInner::Block(block) => &block,