Gloas: envelope peer penalties and REJECT/IGNORE mapping (#8981)

Closes #8949


  Implements peer penalties and REJECT/IGNORE message propagation for `SignedExecutionPayloadEnvelope` gossip handling, completing follow-up work from #8806.

Feedback on the error classification would be appreciated.

### Key Implementation Details

- Maps all 15 `EnvelopeError` variants to REJECT/IGNORE based on [Gloas p2p spec](https://github.com/ethereum/consensus-specs/blob/master/specs/gloas/p2p-interface.md#execution_payload)
- Follows `ExecutionPayloadError` handling pattern from block gossip (`penalize_peer()` method)
- Uses explicit variant matching (rather than catch-all `_`) for type safety
- Applies `LowToleranceError` penalty for protocol violations (invalid signatures, mismatches, etc.)
- Ignores without penalty for spec-defined cases (unknown block root, prior to finalization) and internal errors


Co-Authored-By: 0u-Y <yyw1000@naver.com>

Co-Authored-By: Eitan Seri-Levi <eserilev@gmail.com>
This commit is contained in:
YoungWoo Yang
2026-04-15 01:41:56 +09:00
committed by GitHub
parent b40a178111
commit d3c13c4cf0

View File

@@ -3337,63 +3337,112 @@ impl<T: BeaconChainTypes> NetworkBeaconProcessor<T> {
verified_envelope
}
Err(e) => {
match e {
EnvelopeError::ExecutionPayloadError(ref epe) if !epe.penalize_peer() => {
self.propagate_validation_result(
message_id,
peer_id,
MessageAcceptance::Ignore,
);
}
Err(EnvelopeError::BlockRootUnknown { block_root }) => {
let envelope_slot = envelope.slot();
EnvelopeError::BadSignature
| EnvelopeError::BuilderIndexMismatch { .. }
| EnvelopeError::SlotMismatch { .. }
| EnvelopeError::BlockHashMismatch { .. }
| EnvelopeError::UnknownValidator { .. }
| EnvelopeError::IncorrectBlockProposer { .. }
| EnvelopeError::ExecutionPayloadError(_)
| EnvelopeError::EnvelopeProcessingError(_)
| EnvelopeError::BlockError(_) => {
self.propagate_validation_result(
message_id,
peer_id,
MessageAcceptance::Reject,
);
self.gossip_penalize_peer(
peer_id,
PeerAction::LowToleranceError,
"gossip_envelope_low",
);
}
debug!(
?block_root,
%envelope_slot,
"Envelope references unknown block, deferring to reprocess queue"
);
EnvelopeError::BlockRootUnknown { block_root } => {
let envelope_slot = envelope.slot();
self.propagate_validation_result(message_id, peer_id, MessageAcceptance::Ignore);
debug!(
?block_root,
%envelope_slot,
"Envelope references unknown block, deferring to reprocess queue"
);
let inner_self = self.clone();
let chain = self.chain.clone();
let process_fn = Box::pin(async move {
match chain.verify_envelope_for_gossip(envelope).await {
Ok(verified_envelope) => {
inner_self
.process_gossip_verified_execution_payload_envelope(
peer_id,
verified_envelope,
)
.await;
}
Err(e) => {
debug!(
error = ?e,
"Deferred envelope failed verification"
self.propagate_validation_result(
message_id.clone(),
peer_id,
MessageAcceptance::Ignore,
);
let inner_self = self.clone();
let chain = self.chain.clone();
let process_fn = Box::pin(async move {
match chain.verify_envelope_for_gossip(envelope).await {
Ok(verified_envelope) => {
inner_self
.process_gossip_verified_execution_payload_envelope(
peer_id,
verified_envelope,
)
.await;
}
Err(e) => {
debug!(
error = ?e,
"Deferred envelope failed verification"
);
}
}
});
if self
.beacon_processor_send
.try_send(WorkEvent {
drop_during_sync: false,
work: Work::Reprocess(
ReprocessQueueMessage::UnknownBlockForEnvelope(
QueuedGossipEnvelope {
beacon_block_slot: envelope_slot,
beacon_block_root: block_root,
process_fn,
},
),
),
})
.is_err()
{
error!(
%envelope_slot,
?block_root,
"Failed to defer envelope import"
);
}
}
});
if self
.beacon_processor_send
.try_send(WorkEvent {
drop_during_sync: false,
work: Work::Reprocess(ReprocessQueueMessage::UnknownBlockForEnvelope(
QueuedGossipEnvelope {
beacon_block_slot: envelope_slot,
beacon_block_root: block_root,
process_fn,
},
)),
})
.is_err()
{
error!(
%envelope_slot,
?block_root,
"Failed to defer envelope import"
);
EnvelopeError::PriorToFinalization { .. }
| EnvelopeError::OptimisticSyncNotSupported { .. }
| EnvelopeError::BeaconChainError(_)
| EnvelopeError::BeaconStateError(_)
| EnvelopeError::BlockProcessingError(_)
| EnvelopeError::InternalError(_) => {
self.propagate_validation_result(
message_id,
peer_id,
MessageAcceptance::Ignore,
);
}
}
return None;
}
// TODO(gloas) penalize peers accordingly
Err(_) => return None,
};
let envelope_slot = verified_envelope.signed_envelope.slot();
@@ -3441,7 +3490,7 @@ impl<T: BeaconChainTypes> NetworkBeaconProcessor<T> {
async fn process_gossip_verified_execution_payload_envelope(
self: Arc<Self>,
_peer_id: PeerId,
peer_id: PeerId,
verified_envelope: GossipVerifiedEnvelope<T>,
) {
let _processing_start_time = Instant::now();
@@ -3467,9 +3516,39 @@ impl<T: BeaconChainTypes> NetworkBeaconProcessor<T> {
| Ok(AvailabilityProcessingStatus::MissingComponents(_, _)) => {
// Nothing to do
}
Err(_) => {
// TODO(gloas) implement peer penalties
}
Err(e) => match e {
EnvelopeError::ExecutionPayloadError(epe) if !epe.penalize_peer() => {}
EnvelopeError::BadSignature
| EnvelopeError::BuilderIndexMismatch { .. }
| EnvelopeError::SlotMismatch { .. }
| EnvelopeError::BlockHashMismatch { .. }
| EnvelopeError::UnknownValidator { .. }
| EnvelopeError::IncorrectBlockProposer { .. }
| EnvelopeError::ExecutionPayloadError(_) => {
self.gossip_penalize_peer(
peer_id,
PeerAction::LowToleranceError,
"gossip_envelope_processing_low",
);
}
EnvelopeError::EnvelopeProcessingError(_)
| EnvelopeError::BlockError(_)
| EnvelopeError::BlockRootUnknown { .. } => {
self.gossip_penalize_peer(
peer_id,
PeerAction::LowToleranceError,
"gossip_envelope_processing_error",
);
}
EnvelopeError::PriorToFinalization { .. }
| EnvelopeError::OptimisticSyncNotSupported { .. }
| EnvelopeError::BeaconChainError(_)
| EnvelopeError::BeaconStateError(_)
| EnvelopeError::BlockProcessingError(_)
| EnvelopeError::InternalError(_) => {}
},
}
}