diff --git a/beacon_node/network/src/sync/block_lookups/mod.rs b/beacon_node/network/src/sync/block_lookups/mod.rs index 689820a39f..d73dd83248 100644 --- a/beacon_node/network/src/sync/block_lookups/mod.rs +++ b/beacon_node/network/src/sync/block_lookups/mod.rs @@ -326,7 +326,7 @@ impl BlockLookups { // Child's peers can serve block, and data + payload if the parent is full. // In Gloas, data and payload are coupled: empty blocks have neither. // Pre-Gloas: data is always needed with block, payload is never needed. - let peer_type = match awaiting_parent.parent_hash() { + let peer_type = match awaiting_parent.gloas_bid_parent_hash() { Some(parent_hash) => PeerType::PostGloas(parent_hash), None => PeerType::PreGloas, }; diff --git a/beacon_node/network/src/sync/block_lookups/single_block_lookup.rs b/beacon_node/network/src/sync/block_lookups/single_block_lookup.rs index cbb0f51ab9..faf120beef 100644 --- a/beacon_node/network/src/sync/block_lookups/single_block_lookup.rs +++ b/beacon_node/network/src/sync/block_lookups/single_block_lookup.rs @@ -7,9 +7,9 @@ use crate::sync::network_context::{ LookupRequestResult, PeerGroup, ReqId, RpcRequestSendError, RpcResponseError, SendErrorProcessor, SyncNetworkContext, }; -use beacon_chain::BeaconChainTypes; use beacon_chain::BlockProcessStatus; use beacon_chain::block_verification_types::AsBlock; +use beacon_chain::{BeaconChainTypes, ExecutionStatus}; use educe::Educe; use lighthouse_network::service::api_types::Id; use parking_lot::RwLock; @@ -36,27 +36,77 @@ use types::{ #[derive(Debug, Clone, Copy)] pub struct AwaitingParent { parent_root: Hash256, - parent_hash: Option, + gloas_bid_parent_hash: Option, } impl AwaitingParent { + pub fn is_parent_imported(&self, cx: &mut SyncNetworkContext) -> bool { + if self.parent_root == Hash256::ZERO { + // Zero hash is the parent of the genesis block — not a real block, so no + // parent-known check is needed. Fall through to send the block for processing. + return true; + } + + if let Some(parent_block) = cx + .chain + .canonical_head + .fork_choice_read_lock() + .get_block(&self.parent_root) + { + if let Some(gloas_bid_parent_hash) = self.gloas_bid_parent_hash { + // Post-gloas block, check if it's FULL or EMPTY + let parent_hash = match parent_block.execution_status { + ExecutionStatus::Valid(hash) => hash, + ExecutionStatus::Invalid(hash) => hash, + ExecutionStatus::Optimistic(hash) => hash, + ExecutionStatus::Irrelevant(_) => { + // This should never happen! + return false; + } + }; + let is_full = gloas_bid_parent_hash == parent_hash; + if is_full { + // Post-gloas block FULL, we need the payload to be imported first + cx.chain + .canonical_head + .fork_choice_read_lock() + .is_payload_received(&self.parent_root) + } else { + // Post-gloas block EMPTY, and block is imported + true + } + } else { + // Pre-gloas block + true + } + } else { + // Parent is unknown + false + } + } + + pub fn parent_is_genesis(&self) -> bool { + self.parent_root == Hash256::ZERO + } + pub fn parent_root(&self) -> Hash256 { self.parent_root } - pub fn parent_hash(&self) -> Option { - self.parent_hash + pub fn gloas_bid_parent_hash(&self) -> Option { + self.gloas_bid_parent_hash } pub fn from_block(block: &SignedBeaconBlock) -> Self { - let parent_hash = if let Ok(bid) = block.message().body().signed_execution_payload_bid() { - Some(bid.message.parent_block_hash) - } else { - None - }; Self { parent_root: block.message().parent_root(), - parent_hash, + gloas_bid_parent_hash: if let Ok(bid) = + block.message().body().signed_execution_payload_bid() + { + Some(bid.message.parent_block_hash) + } else { + None + }, } } @@ -72,7 +122,7 @@ impl AwaitingParent { } else { Ok(Self { parent_root, - parent_hash: None, + gloas_bid_parent_hash: None, }) } } @@ -530,7 +580,7 @@ pub enum PeerType { impl PeerType { pub fn from_awaiting_parent(awaiting_parent: AwaitingParent) -> Self { - match awaiting_parent.parent_hash() { + match awaiting_parent.gloas_bid_parent_hash() { Some(parent_hash) => Self::PostGloas(parent_hash), None => Self::PreGloas, } @@ -706,18 +756,9 @@ impl SingleBlockLookup { break; } - let parent_root = block.parent_root(); - // Zero hash is the parent of the genesis block — not a real block, so no - // parent-known check is needed. Fall through to send the block for processing. - if parent_root != Hash256::ZERO - && cx - .chain - .canonical_head - .fork_choice_read_lock() - .get_block(&parent_root) - .is_none() - { - let awaiting_parent = AwaitingParent::from_block(block); + let awaiting_parent = AwaitingParent::from_block(block); + + if !awaiting_parent.is_parent_imported(cx) { self.awaiting_parent = Some(awaiting_parent); return Ok(LookupResult::ParentUnknown { awaiting_parent, @@ -753,13 +794,7 @@ impl SingleBlockLookup { else { break; }; - let peers = self - .get_data_peers::( - block.slot(), - block.execution_hash(), - cx.spec(), - ) - .map_err(LookupRequestError::InternalError)?; + let peers = self.get_data_peers::(&block); self.data_request = Some(DataRequest { peers, state: DataRequestState::new( @@ -809,13 +844,7 @@ impl SingleBlockLookup { else { break; }; - let peers = self - .get_data_peers::( - block.slot(), - block.execution_hash(), - cx.spec(), - ) - .map_err(LookupRequestError::InternalError)?; + let peers = self.get_data_peers(&block); self.payload_request = Some(PayloadRequest { peers, state: PayloadRequestState::new(block.slot(), cx.spec()), @@ -883,24 +912,16 @@ impl SingleBlockLookup { Ok(LookupResult::Pending) } - fn get_data_peers( - &self, - slot: Slot, - execution_hash: Option, - spec: &ChainSpec, - ) -> Result { - Ok(if spec.fork_name_at_slot::(slot).gloas_enabled() { - let Some(execution_hash) = execution_hash else { - return Err("execution_hash is None post gloas".to_string()); - }; + fn get_data_peers(&self, block: &SignedBeaconBlock) -> PeerSet { + if let Ok(bid) = block.message().body().signed_execution_payload_bid() { self.gloas_child_peers .write() - .entry(execution_hash) + .entry(bid.message.block_hash) .or_default() .clone() } else { self.peers.clone() - }) + } } // -- Processing result handlers -- @@ -1363,7 +1384,7 @@ impl std::fmt::Debug for DownloadState { impl std::fmt::Display for AwaitingParent { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self.parent_hash { + match self.gloas_bid_parent_hash { Some(parent_hash) => write!(f, "{}/{}", self.parent_root, parent_hash), None => write!(f, "{}", self.parent_root), } diff --git a/consensus/types/src/block/signed_beacon_block.rs b/consensus/types/src/block/signed_beacon_block.rs index c1f01ae332..764ed43400 100644 --- a/consensus/types/src/block/signed_beacon_block.rs +++ b/consensus/types/src/block/signed_beacon_block.rs @@ -361,16 +361,6 @@ impl> SignedBeaconBlock .unwrap_or(0) } - pub fn execution_hash(&self) -> Option { - if let Ok(bid) = self.message().body().signed_execution_payload_bid() { - return Some(bid.message.block_hash); - } - if let Ok(payload) = self.message().body().execution_payload() { - return Some(payload.block_hash()); - } - None - } - /// Used for displaying commitments in logs. pub fn commitments_formatted(&self) -> String { let Ok(commitments) = self.message().body().blob_kzg_commitments() else {