use crate::{state_id::checkpoint_slot_and_execution_optimistic, ExecutionOptimistic}; use beacon_chain::{BeaconChain, BeaconChainError, BeaconChainTypes, WhenSlotSkipped}; use eth2::types::BlockId as CoreBlockId; use std::fmt; use std::str::FromStr; use std::sync::Arc; use types::{BlobsSidecar, EthSpec, Hash256, SignedBeaconBlock, SignedBlindedBeaconBlock, Slot}; /// Wraps `eth2::types::BlockId` and provides a simple way to obtain a block or root for a given /// `BlockId`. #[derive(Debug)] pub struct BlockId(pub CoreBlockId); type Finalized = bool; impl BlockId { pub fn from_slot(slot: Slot) -> Self { Self(CoreBlockId::Slot(slot)) } pub fn from_root(root: Hash256) -> Self { Self(CoreBlockId::Root(root)) } /// Return the block root identified by `self`. pub fn root( &self, chain: &BeaconChain, ) -> Result<(Hash256, ExecutionOptimistic, Finalized), warp::Rejection> { match &self.0 { CoreBlockId::Head => { let (cached_head, execution_status) = chain .canonical_head .head_and_execution_status() .map_err(warp_utils::reject::beacon_chain_error)?; Ok(( cached_head.head_block_root(), execution_status.is_optimistic_or_invalid(), false, )) } CoreBlockId::Genesis => Ok((chain.genesis_block_root, false, true)), CoreBlockId::Finalized => { let finalized_checkpoint = chain.canonical_head.cached_head().finalized_checkpoint(); let (_slot, execution_optimistic) = checkpoint_slot_and_execution_optimistic(chain, finalized_checkpoint)?; Ok((finalized_checkpoint.root, execution_optimistic, true)) } CoreBlockId::Justified => { let justified_checkpoint = chain.canonical_head.cached_head().justified_checkpoint(); let (_slot, execution_optimistic) = checkpoint_slot_and_execution_optimistic(chain, justified_checkpoint)?; Ok((justified_checkpoint.root, execution_optimistic, false)) } CoreBlockId::Slot(slot) => { let execution_optimistic = chain .is_optimistic_or_invalid_head() .map_err(warp_utils::reject::beacon_chain_error)?; let root = chain .block_root_at_slot(*slot, WhenSlotSkipped::None) .map_err(warp_utils::reject::beacon_chain_error) .and_then(|root_opt| { root_opt.ok_or_else(|| { warp_utils::reject::custom_not_found(format!( "beacon block at slot {}", slot )) }) })?; let finalized = *slot <= chain .canonical_head .cached_head() .finalized_checkpoint() .epoch .start_slot(T::EthSpec::slots_per_epoch()); Ok((root, execution_optimistic, finalized)) } CoreBlockId::Root(root) => { // This matches the behaviour of other consensus clients (e.g. Teku). if root == &Hash256::zero() { return Err(warp_utils::reject::custom_not_found(format!( "beacon block with root {}", root ))); }; if chain .store .block_exists(root) .map_err(BeaconChainError::DBError) .map_err(warp_utils::reject::beacon_chain_error)? { let execution_optimistic = chain .canonical_head .fork_choice_read_lock() .is_optimistic_or_invalid_block(root) .map_err(BeaconChainError::ForkChoiceError) .map_err(warp_utils::reject::beacon_chain_error)?; let blinded_block = chain .get_blinded_block(root) .map_err(warp_utils::reject::beacon_chain_error)? .ok_or_else(|| { warp_utils::reject::custom_not_found(format!( "beacon block with root {}", root )) })?; let block_slot = blinded_block.slot(); let finalized = chain .is_finalized_block(root, block_slot) .map_err(warp_utils::reject::beacon_chain_error)?; Ok((*root, execution_optimistic, finalized)) } else { Err(warp_utils::reject::custom_not_found(format!( "beacon block with root {}", root ))) } } } } /// Return the `SignedBeaconBlock` identified by `self`. pub fn blinded_block( &self, chain: &BeaconChain, ) -> Result< ( SignedBlindedBeaconBlock, ExecutionOptimistic, Finalized, ), warp::Rejection, > { match &self.0 { CoreBlockId::Head => { let (cached_head, execution_status) = chain .canonical_head .head_and_execution_status() .map_err(warp_utils::reject::beacon_chain_error)?; Ok(( cached_head.snapshot.beacon_block.clone_as_blinded(), execution_status.is_optimistic_or_invalid(), false, )) } CoreBlockId::Slot(slot) => { let (root, execution_optimistic, finalized) = self.root(chain)?; chain .get_blinded_block(&root) .map_err(warp_utils::reject::beacon_chain_error) .and_then(|block_opt| match block_opt { Some(block) => { if block.slot() != *slot { return Err(warp_utils::reject::custom_not_found(format!( "slot {} was skipped", slot ))); } Ok((block, execution_optimistic, finalized)) } None => Err(warp_utils::reject::custom_not_found(format!( "beacon block with root {}", root ))), }) } _ => { let (root, execution_optimistic, finalized) = self.root(chain)?; let block = chain .get_blinded_block(&root) .map_err(warp_utils::reject::beacon_chain_error) .and_then(|root_opt| { root_opt.ok_or_else(|| { warp_utils::reject::custom_not_found(format!( "beacon block with root {}", root )) }) })?; Ok((block, execution_optimistic, finalized)) } } } /// Return the `SignedBeaconBlock` identified by `self`. pub async fn full_block( &self, chain: &BeaconChain, ) -> Result< ( Arc>, ExecutionOptimistic, Finalized, ), warp::Rejection, > { match &self.0 { CoreBlockId::Head => { let (cached_head, execution_status) = chain .canonical_head .head_and_execution_status() .map_err(warp_utils::reject::beacon_chain_error)?; Ok(( cached_head.snapshot.beacon_block.clone(), execution_status.is_optimistic_or_invalid(), false, )) } CoreBlockId::Slot(slot) => { let (root, execution_optimistic, finalized) = self.root(chain)?; chain .get_block(&root) .await .map_err(warp_utils::reject::beacon_chain_error) .and_then(|block_opt| match block_opt { Some(block) => { if block.slot() != *slot { return Err(warp_utils::reject::custom_not_found(format!( "slot {} was skipped", slot ))); } Ok((Arc::new(block), execution_optimistic, finalized)) } None => Err(warp_utils::reject::custom_not_found(format!( "beacon block with root {}", root ))), }) } _ => { let (root, execution_optimistic, finalized) = self.root(chain)?; chain .get_block(&root) .await .map_err(warp_utils::reject::beacon_chain_error) .and_then(|block_opt| { block_opt .map(|block| (Arc::new(block), execution_optimistic, finalized)) .ok_or_else(|| { warp_utils::reject::custom_not_found(format!( "beacon block with root {}", root )) }) }) } } } /// Return the `BlobsSidecar` identified by `self`. pub async fn blobs_sidecar( &self, chain: &BeaconChain, ) -> Result>, warp::Rejection> { let root = self.root(chain)?.0; let Some(data_availability_boundary) = chain.data_availability_boundary() else { return Err(warp_utils::reject::custom_not_found("Eip4844 fork disabled".into())); }; match chain.get_blobs(&root, data_availability_boundary) { Ok(Some(blob)) => Ok(Arc::new(blob)), Ok(None) => Err(warp_utils::reject::custom_not_found(format!( "Blob with block root {} is not in the store", root ))), Err(e) => Err(warp_utils::reject::beacon_chain_error(e)), } } } impl FromStr for BlockId { type Err = String; fn from_str(s: &str) -> Result { CoreBlockId::from_str(s).map(Self) } } impl fmt::Display for BlockId { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.0) } }