Gloas attestation payload reprocess (#9440)

Handle payload-present attestations before the payload is seen (gloas)

A gloas beacon_attestation with index == 1 claims a past block's payload is already present. If we haven't seen that block's payload envelope yet, we shouldn't reject it the envelope may just be in flight.

So instead we IGNORE it (new AttnError::UnknownPayloadEnvelope), ask sync to fetch the envelope, and park the attestation in the reprocess queue. When the envelope is imported, the parked attestations are released and  re-verified.

The envelope lookup itself is stubbed here and wired up in #9155 or a follow up PR


  


Co-Authored-By: dapplion <35266934+dapplion@users.noreply.github.com>

Co-Authored-By: Eitan Seri-Levi <eserilev@ucsc.edu>
This commit is contained in:
Lion - dapplion
2026-06-17 14:44:21 +02:00
committed by GitHub
parent 3bc9148e0e
commit a46620155b
22 changed files with 893 additions and 170 deletions

View File

@@ -156,6 +156,11 @@ pub enum SyncMessage<E: EthSpec> {
/// manager to attempt to find the block matching the unknown hash.
UnknownBlockHashFromAttestation(PeerId, Hash256),
/// A peer has sent a payload-present attestation (`index == 1`) for a block whose execution
/// payload envelope we have not seen. This triggers the manager to fetch the payload envelope
/// for `block_root` via `ExecutionPayloadEnvelopesByRoot`.
UnknownPayloadEnvelopeFromAttestation(PeerId, Hash256),
/// A peer has disconnected.
Disconnect(PeerId),
@@ -260,6 +265,10 @@ pub struct SyncManager<T: BeaconChainTypes> {
/// may forward us thousands of a attestations, each one triggering an individual event. Only
/// one event is useful, the rest generating log noise and wasted cycles
notified_unknown_roots: LRUTimeCache<(PeerId, Hash256)>,
/// Debounce duplicated `UnknownPayloadEnvelopeFromAttestation` for the same root/peer tuple,
/// for the same reason as `notified_unknown_roots`: a peer may forward many payload-present
/// attestations for a block whose execution payload envelope we have not yet seen.
notified_unknown_payload_roots: LRUTimeCache<(PeerId, Hash256)>,
}
/// Spawns a new `SyncManager` thread which has a weak reference to underlying beacon
@@ -320,6 +329,9 @@ impl<T: BeaconChainTypes> SyncManager<T> {
notified_unknown_roots: LRUTimeCache::new(Duration::from_secs(
NOTIFIED_UNKNOWN_ROOT_EXPIRY_SECONDS,
)),
notified_unknown_payload_roots: LRUTimeCache::new(Duration::from_secs(
NOTIFIED_UNKNOWN_ROOT_EXPIRY_SECONDS,
)),
}
}
@@ -895,6 +907,22 @@ impl<T: BeaconChainTypes> SyncManager<T> {
self.handle_unknown_block_root(peer_id, block_root);
}
}
SyncMessage::UnknownPayloadEnvelopeFromAttestation(peer_id, block_root) => {
if !self
.notified_unknown_payload_roots
.contains(&(peer_id, block_root))
{
self.notified_unknown_payload_roots
.insert((peer_id, block_root));
// TODO(gloas): trigger a payload-envelope lookup for `block_root` via
// `ExecutionPayloadEnvelopesByRoot`. Wired up in the gloas lookup-sync PR (#9155).
debug!(
?block_root,
?peer_id,
"Received unknown payload envelope from attestation"
);
}
}
SyncMessage::Disconnect(peer_id) => {
debug!(%peer_id, "Received disconnected message");
self.peer_disconnect(&peer_id);

View File

@@ -1989,7 +1989,7 @@ impl TestRig {
block: Arc<SignedBeaconBlock<E>>,
) {
match self.import_block_to_da_checker(block).await {
AvailabilityProcessingStatus::Imported(_) => {
AvailabilityProcessingStatus::Imported(..) => {
panic!("block removed from da_checker, available")
}
AvailabilityProcessingStatus::MissingComponents(_, block_root) => {