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::{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); 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), 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(), )) } CoreBlockId::Genesis => Ok((chain.genesis_block_root, false)), 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)) } 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)) } CoreBlockId::Slot(slot) => { let execution_optimistic = chain .is_optimistic_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 )) }) })?; Ok((root, execution_optimistic)) } 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_block(root) .map_err(BeaconChainError::ForkChoiceError) .map_err(warp_utils::reject::beacon_chain_error)?; Ok((*root, execution_optimistic)) } else { return 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), 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(), )) } CoreBlockId::Slot(slot) => { let (root, execution_optimistic) = 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)) } None => Err(warp_utils::reject::custom_not_found(format!( "beacon block with root {}", root ))), }) } _ => { let (root, execution_optimistic) = 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)) } } } /// Return the `SignedBeaconBlock` identified by `self`. pub async fn full_block( &self, chain: &BeaconChain, ) -> Result<(Arc>, ExecutionOptimistic), 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(), )) } CoreBlockId::Slot(slot) => { let (root, execution_optimistic) = 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)) } None => Err(warp_utils::reject::custom_not_found(format!( "beacon block with root {}", root ))), }) } _ => { let (root, execution_optimistic) = 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)) .ok_or_else(|| { warp_utils::reject::custom_not_found(format!( "beacon block with root {}", root )) }) }) } } } } 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) } }