mirror of
https://github.com/sigp/lighthouse.git
synced 2026-06-10 01:26:44 +00:00
Move parent-known/envelope-imported check onto AwaitingParent
Encapsulate the "is this block's parent in a state where we can process the child?" check as `AwaitingParent::is_parent_imported(cx)`. The block Downloaded arm in continue_requests now calls this single method instead of inlining a fork-choice lookup. For Gloas this adds a real new gate: if the child's bid identifies the parent as full (bid.parent_block_hash == parent.execution_status block hash), we additionally require the parent's envelope to be imported via ForkChoice::is_payload_received. A full Gloas parent without its envelope hasn't realised its post-state yet, so the child can't be processed against it. The previous block-only check let the child proceed too early. Rename `AwaitingParent::parent_hash` → `gloas_bid_parent_hash` to make the intent explicit (it's bid metadata, only Some post-Gloas) and add a matching getter. Drop `SignedBeaconBlock::execution_hash` (no remaining callers; `get_data_peers` now extracts the bid inline). Also simplifies `get_data_peers` to take `&SignedBeaconBlock` directly and gate on `signed_execution_payload_bid().is_ok()` rather than threading slot/spec for a fork-name check.
This commit is contained in:
@@ -326,7 +326,7 @@ impl<T: BeaconChainTypes> BlockLookups<T> {
|
||||
// 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,
|
||||
};
|
||||
|
||||
@@ -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<ExecutionBlockHash>,
|
||||
gloas_bid_parent_hash: Option<ExecutionBlockHash>,
|
||||
}
|
||||
|
||||
impl AwaitingParent {
|
||||
pub fn is_parent_imported<T: BeaconChainTypes>(&self, cx: &mut SyncNetworkContext<T>) -> 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<ExecutionBlockHash> {
|
||||
self.parent_hash
|
||||
pub fn gloas_bid_parent_hash(&self) -> Option<ExecutionBlockHash> {
|
||||
self.gloas_bid_parent_hash
|
||||
}
|
||||
|
||||
pub fn from_block<E: EthSpec>(block: &SignedBeaconBlock<E>) -> 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<T: BeaconChainTypes> SingleBlockLookup<T> {
|
||||
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<T: BeaconChainTypes> SingleBlockLookup<T> {
|
||||
else {
|
||||
break;
|
||||
};
|
||||
let peers = self
|
||||
.get_data_peers::<T::EthSpec>(
|
||||
block.slot(),
|
||||
block.execution_hash(),
|
||||
cx.spec(),
|
||||
)
|
||||
.map_err(LookupRequestError::InternalError)?;
|
||||
let peers = self.get_data_peers::<T::EthSpec>(&block);
|
||||
self.data_request = Some(DataRequest {
|
||||
peers,
|
||||
state: DataRequestState::new(
|
||||
@@ -809,13 +844,7 @@ impl<T: BeaconChainTypes> SingleBlockLookup<T> {
|
||||
else {
|
||||
break;
|
||||
};
|
||||
let peers = self
|
||||
.get_data_peers::<T::EthSpec>(
|
||||
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<T: BeaconChainTypes> SingleBlockLookup<T> {
|
||||
Ok(LookupResult::Pending)
|
||||
}
|
||||
|
||||
fn get_data_peers<E: EthSpec>(
|
||||
&self,
|
||||
slot: Slot,
|
||||
execution_hash: Option<ExecutionBlockHash>,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<PeerSet, String> {
|
||||
Ok(if spec.fork_name_at_slot::<E>(slot).gloas_enabled() {
|
||||
let Some(execution_hash) = execution_hash else {
|
||||
return Err("execution_hash is None post gloas".to_string());
|
||||
};
|
||||
fn get_data_peers<E: EthSpec>(&self, block: &SignedBeaconBlock<E>) -> 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<T: Clone> std::fmt::Debug for DownloadState<T> {
|
||||
|
||||
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),
|
||||
}
|
||||
|
||||
@@ -361,16 +361,6 @@ impl<E: EthSpec, Payload: AbstractExecPayload<E>> SignedBeaconBlock<E, Payload>
|
||||
.unwrap_or(0)
|
||||
}
|
||||
|
||||
pub fn execution_hash(&self) -> Option<ExecutionBlockHash> {
|
||||
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 {
|
||||
|
||||
Reference in New Issue
Block a user