diff --git a/beacon_node/beacon_chain/src/builder.rs b/beacon_node/beacon_chain/src/builder.rs index 61c026e0a9..a86b118038 100644 --- a/beacon_node/beacon_chain/src/builder.rs +++ b/beacon_node/beacon_chain/src/builder.rs @@ -802,7 +802,24 @@ where .map_err(|e| format!("Error loading head execution envelope: {:?}", e))? .map(Arc::new) } else { - None + let latest_full_block_root_opt = fork_choice + .latest_parent_full_block(head_block_root, &self.spec) + .map_err(|e| { + format!( + "Error fetching latest full beacon block root from fork choice: {:?}", + e + ) + })?; + + if let Some(latest_full_block_root) = latest_full_block_root_opt { + store + .get_payload_envelope(&latest_full_block_root) + .map_err(|e| format!("Error loading latest execution envelope: {:?}", e))? + .map(Arc::new) + } else { + // TODO(gloas) handle the case where the non-finalized portion of the chain has no canonical payload envelopes. + None + } }; let mut head_snapshot = BeaconSnapshot { diff --git a/beacon_node/beacon_chain/src/canonical_head.rs b/beacon_node/beacon_chain/src/canonical_head.rs index 799ecff132..9135048122 100644 --- a/beacon_node/beacon_chain/src/canonical_head.rs +++ b/beacon_node/beacon_chain/src/canonical_head.rs @@ -200,9 +200,9 @@ impl CachedHead { Some(head_block_number) } else { // This fallback is strictly for the fork boundary case when self.snapshot.execution_envelope is `None`. - // Note: If there is a missed/orphaned envelope at the fork boundary we wont be able to get the block number using this fallback. - // We could try handling that edge case but it doesn't seem worth it. Returning `None` here just means that we don't - // emit a payload attributes SSE event further upstream. + // TODO(gloas) If there is a missed/orphaned envelope at the fork boundary we wont be able to get the block number using this fallback. + // We might want to try handling that edge case. Returning `None` here means that we don't emit a payload attributes SSE event which + // might be important for upstream consumers (i.e. the builder client). self.head_block_number().ok() } } @@ -345,7 +345,17 @@ impl CanonicalHead { .get_payload_envelope(&beacon_block_root)? .map(Arc::new) } else { - None + let latest_full_block_root_opt = + fork_choice.latest_parent_full_block(beacon_block_root, spec)?; + + if let Some(latest_full_block_root) = latest_full_block_root_opt { + store + .get_payload_envelope(&latest_full_block_root)? + .map(Arc::new) + } else { + // TODO(gloas) handle the case where the non-finalized portion of the chain has no canonical payload envelopes. + None + } }; let snapshot = BeaconSnapshot { @@ -744,7 +754,24 @@ impl BeaconChain { Some(envelope) } else { - None + let fork_choice = self.canonical_head.fork_choice_read_lock(); + let latest_full_block_root_opt = fork_choice + .latest_parent_full_block(new_view.head_block_root, &self.spec)?; + drop(fork_choice); + + if let Some(latest_full_block_root) = latest_full_block_root_opt { + let envelope = self + .store + .get_payload_envelope(&latest_full_block_root)? + .map(Arc::new) + .ok_or(Error::MissingExecutionPayloadEnvelope( + latest_full_block_root, + ))?; + Some(envelope) + } else { + // TODO(gloas) handle the case where the non-finalized portion of the chain has no canonical payload envelopes. + None + } }; let (_, beacon_state) = self .store diff --git a/consensus/fork_choice/src/fork_choice.rs b/consensus/fork_choice/src/fork_choice.rs index 2de8ce7d81..f05b2741cb 100644 --- a/consensus/fork_choice/src/fork_choice.rs +++ b/consensus/fork_choice/src/fork_choice.rs @@ -1565,6 +1565,22 @@ where } } + /// Returns the latest ancestor of `block_root` whose `PayloadStatus` is `Full`. + pub fn latest_parent_full_block( + &self, + block_root: Hash256, + spec: &ChainSpec, + ) -> Result, Error> { + if self.is_finalized_checkpoint_or_descendant(block_root) { + let proposer_boost_root = self.fc_store.proposer_boost_root(); + self.proto_array + .latest_parent_full_block::(block_root, proposer_boost_root, spec) + .map_err(Error::ProtoArrayError) + } else { + Err(Error::DoesNotDescendFromFinalizedCheckpoint) + } + } + /// Returns the canonical payload status of a block. See /// `ProtoArrayForkChoice::get_canonical_payload_status`. pub fn get_canonical_payload_status( diff --git a/consensus/proto_array/src/proto_array.rs b/consensus/proto_array/src/proto_array.rs index 8ac8354f06..05d1e469af 100644 --- a/consensus/proto_array/src/proto_array.rs +++ b/consensus/proto_array/src/proto_array.rs @@ -1276,6 +1276,29 @@ impl ProtoArray { } } + /// Returns the latest ancestor of `block_root` whose `PayloadStatus` is `Full`. + pub(crate) fn latest_parent_full_block( + &self, + block_root: Hash256, + proposer_boost_root: Hash256, + justified_balances: &JustifiedBalances, + spec: &ChainSpec, + ) -> Result, Error> { + for node in self.iter_nodes(&block_root) { + if self.get_canonical_payload_status::( + node.root(), + node.slot(), + proposer_boost_root, + justified_balances, + spec, + )? == PayloadStatus::Full + { + return Ok(Some(node.root())); + } + } + Ok(None) + } + /// Returns the canonical payload status of a block, matching the decision /// `get_head` would make between `(root, FULL)` and `(root, EMPTY)`. pub(crate) fn get_canonical_payload_status( diff --git a/consensus/proto_array/src/proto_array_fork_choice.rs b/consensus/proto_array/src/proto_array_fork_choice.rs index 96d2302266..6ab2398f48 100644 --- a/consensus/proto_array/src/proto_array_fork_choice.rs +++ b/consensus/proto_array/src/proto_array_fork_choice.rs @@ -1083,6 +1083,21 @@ impl ProtoArrayForkChoice { .unwrap_or(false) } + /// Returns the latest ancestor of `block_root` whose `PayloadStatus` is `Full`. + pub fn latest_parent_full_block( + &self, + block_root: Hash256, + proposer_boost_root: Hash256, + spec: &ChainSpec, + ) -> Result, Error> { + self.proto_array.latest_parent_full_block::( + block_root, + proposer_boost_root, + &self.balances, + spec, + ) + } + /// Returns the canonical payload status of a block, matching the decision /// `get_head` would make between `(root, FULL)` and `(root, EMPTY)`. pub fn get_canonical_payload_status(