Complete envelope-lookup functionality and tests

Implementation:
- payload_envelope_verification: implement the AvailabilityPending branch
  in the envelope import flow. Previously returned
  InternalError("Pending payload envelope not yet implemented") for any
  envelope whose data columns hadn't yet been received, blocking the
  end-to-end RPC import path. New `import_pending_execution_payload_envelope`
  marks the payload as received in fork choice and persists the envelope to
  the store; columns are still expected to arrive separately (gossip /
  engineGetBlobs / reconstruction) and persist their own ops.

- sync manager: short-circuit `handle_unknown_parent_envelope` when the
  parent's payload was received between gossip-verification and the trigger
  reaching sync. No lookup is created; the trigger is treated as a no-op.

- gossip→sync hook: when a Gloas envelope is imported via the gossip path,
  emit `SyncMessage::GossipEnvelopeImported { block_root }` so any lookups
  awaiting that parent envelope unblock without depending on the in-flight
  RPC response landing first. Closes the review-flagged race where a
  gossip-imported envelope left child lookups pinned.

Tests (3 new):
- envelope_already_received_skips_lookup — trigger after envelope already
  in fork choice creates zero lookups.
- happy_path_unknown_parent_envelope — end-to-end RPC import path: lookups
  complete, head advances to the gossip block.
- happy_path_unknown_parent_envelope_via_gossip — pending envelope-only
  lookup unblocked by a concurrent gossip import via the new sync hook.

Existing tests updated:
- bad_peer_envelope_rpc_failure / bad_peer_wrong_envelope_response now
  expect the lookup to retry and succeed (mirroring `bad_peer_*` tests for
  blocks/blobs/columns), reflecting the now-working import path.
This commit is contained in:
dapplion
2026-04-28 15:49:29 +02:00
parent 7e50d47082
commit 11684b0da0
5 changed files with 217 additions and 18 deletions

View File

@@ -154,6 +154,10 @@ pub enum SyncMessage<E: EthSpec> {
/// A block's parent is known but its execution payload envelope has not been received yet.
UnknownParentEnvelope(PeerId, Arc<SignedBeaconBlock<E>>, Hash256),
/// An execution payload envelope has been imported via the local gossip path.
/// Sync uses this to unblock any child lookups that were awaiting this parent envelope.
GossipEnvelopeImported { block_root: Hash256 },
/// A partial data column with an unknown parent has been received.
UnknownParentPartialDataColumn {
peer_id: PeerId,
@@ -961,6 +965,14 @@ impl<T: BeaconChainTypes> SyncManager<T> {
}),
);
}
SyncMessage::GossipEnvelopeImported { block_root } => {
debug!(
%block_root,
"Gossip-imported envelope; unblocking awaiting child lookups"
);
self.block_lookups
.continue_envelope_child_lookups(block_root, &mut self.network);
}
SyncMessage::UnknownParentPartialDataColumn {
peer_id,
block_root,
@@ -1096,6 +1108,21 @@ impl<T: BeaconChainTypes> SyncManager<T> {
slot: Slot,
block_component: BlockComponent<T::EthSpec>,
) {
// Defensive: if the parent's payload envelope was already received between when
// gossip-verification raised `ParentEnvelopeUnknown` and now, no lookup is needed.
if self
.chain
.canonical_head
.fork_choice_read_lock()
.is_payload_received(&parent_root)
{
debug!(
%block_root,
%parent_root,
"Parent envelope already received, skipping envelope lookup"
);
return;
}
match self.should_search_for_block(Some(slot), &peer_id) {
Ok(_) => {
if self.block_lookups.search_child_and_parent_envelope(