Gloas payload envelope processing (#8806)

Co-Authored-By: Eitan Seri- Levi <eserilev@gmail.com>

Co-Authored-By: Eitan Seri-Levi <eserilev@ucsc.edu>

Co-Authored-By: Jimmy Chen <jchen.tc@gmail.com>

Co-Authored-By: Michael Sproul <michael@sigmaprime.io>

Co-Authored-By: Michael Sproul <michaelsproul@users.noreply.github.com>
This commit is contained in:
Eitan Seri-Levi
2026-03-09 14:23:34 +09:00
committed by GitHub
parent 537c2ba8b3
commit 7dab32dd16
19 changed files with 1813 additions and 127 deletions

View File

@@ -539,6 +539,16 @@ pub static SYNC_RPC_REQUEST_TIME: LazyLock<Result<HistogramVec>> = LazyLock::new
)
});
/*
* Execution Payload Envelope Delay Metrics
*/
pub static ENVELOPE_DELAY_GOSSIP: LazyLock<Result<IntGauge>> = LazyLock::new(|| {
try_create_int_gauge(
"payload_envelope_delay_gossip",
"The first time we see this payload envelope from gossip as a delay from the start of the slot",
)
});
/*
* Block Delay Metrics
*/

View File

@@ -4,7 +4,6 @@ use crate::{
service::NetworkMessage,
sync::SyncMessage,
};
use beacon_chain::blob_verification::{GossipBlobError, GossipVerifiedBlob};
use beacon_chain::block_verification_types::AsBlock;
use beacon_chain::data_column_verification::{GossipDataColumnError, GossipVerifiedDataColumn};
use beacon_chain::store::Error;
@@ -19,6 +18,10 @@ use beacon_chain::{
sync_committee_verification::{self, Error as SyncCommitteeError},
validator_monitor::{get_block_delay_ms, get_slot_delay_ms},
};
use beacon_chain::{
blob_verification::{GossipBlobError, GossipVerifiedBlob},
payload_envelope_verification::gossip_verified_envelope::GossipVerifiedEnvelope,
};
use beacon_processor::{Work, WorkEvent};
use lighthouse_network::{Client, MessageAcceptance, MessageId, PeerAction, PeerId, ReportSource};
use logging::crit;
@@ -3248,25 +3251,166 @@ impl<T: BeaconChainTypes> NetworkBeaconProcessor<T> {
}
}
pub async fn process_gossip_execution_payload(
#[allow(clippy::too_many_arguments)]
#[instrument(
name = "lh_process_execution_payload_envelope",
parent = None,
level = "debug",
skip_all,
fields(beacon_block_root = tracing::field::Empty),
)]
pub async fn process_gossip_execution_payload_envelope(
self: Arc<Self>,
message_id: MessageId,
peer_id: PeerId,
envelope: Arc<SignedExecutionPayloadEnvelope<T::EthSpec>>,
seen_timestamp: Duration,
) {
if let Some(gossip_verified_envelope) = self
.process_gossip_unverified_execution_payload_envelope(
message_id,
peer_id,
envelope.clone(),
seen_timestamp,
)
.await
{
let beacon_block_root = gossip_verified_envelope.signed_envelope.beacon_block_root();
Span::current().record("beacon_block_root", beacon_block_root.to_string());
// TODO(gloas) in process_gossip_block here we check_and_insert on the duplicate cache
// before calling gossip_verified_block. We need this to ensure we dont try to execute the
// payload multiple times.
self.process_gossip_verified_execution_payload_envelope(
peer_id,
gossip_verified_envelope,
)
.await;
}
}
async fn process_gossip_unverified_execution_payload_envelope(
self: &Arc<Self>,
message_id: MessageId,
peer_id: PeerId,
execution_payload: SignedExecutionPayloadEnvelope<T::EthSpec>,
envelope: Arc<SignedExecutionPayloadEnvelope<T::EthSpec>>,
seen_duration: Duration,
) -> Option<GossipVerifiedEnvelope<T>> {
let envelope_delay =
get_slot_delay_ms(seen_duration, envelope.slot(), &self.chain.slot_clock);
let verification_result = self
.chain
.clone()
.verify_envelope_for_gossip(envelope.clone())
.await;
let verified_envelope = match verification_result {
Ok(verified_envelope) => {
metrics::set_gauge(
&metrics::ENVELOPE_DELAY_GOSSIP,
envelope_delay.as_millis() as i64,
);
// Write the time the envelope was observed into the delay cache.
self.chain.envelope_times_cache.write().set_time_observed(
verified_envelope.signed_envelope.beacon_block_root(),
envelope.slot(),
seen_duration,
Some(peer_id.to_string()),
);
info!(
slot = %verified_envelope.signed_envelope.slot(),
root = ?verified_envelope.signed_envelope.beacon_block_root(),
"New envelope received"
);
self.propagate_validation_result(message_id, peer_id, MessageAcceptance::Accept);
verified_envelope
}
// TODO(gloas) penalize peers accordingly
Err(_) => return None,
};
let envelope_slot = verified_envelope.signed_envelope.slot();
let beacon_block_root = verified_envelope.signed_envelope.beacon_block_root();
match self.chain.slot() {
// We only need to do a simple check about the envelope slot vs the current slot because
// `verify_envelope_for_gossip` already ensures that the envelope slot is within tolerance
// for envelope imports.
Ok(current_slot) if envelope_slot > current_slot => {
warn!(
?envelope_slot,
?beacon_block_root,
msg = "if this happens consistently, check system clock",
"envelope arrived early"
);
// TODO(gloas) update metrics to note how early the envelope arrived
let inner_self = self.clone();
let _process_fn = Box::pin(async move {
inner_self
.process_gossip_verified_execution_payload_envelope(
peer_id,
verified_envelope,
)
.await;
});
// TODO(gloas) send to reprocess queue
None
}
Ok(_) => Some(verified_envelope),
Err(e) => {
error!(
error = ?e,
%envelope_slot,
?beacon_block_root,
location = "envelope gossip",
"Failed to defer envelope import"
);
None
}
}
}
async fn process_gossip_verified_execution_payload_envelope(
self: Arc<Self>,
_peer_id: PeerId,
verified_envelope: GossipVerifiedEnvelope<T>,
) {
// TODO(EIP-7732): Implement proper execution payload envelope gossip processing.
// This should integrate with the envelope_verification.rs module once it's implemented.
let _processing_start_time = Instant::now();
let beacon_block_root = verified_envelope.signed_envelope.beacon_block_root();
trace!(
%peer_id,
builder_index = execution_payload.message.builder_index,
slot = %execution_payload.message.slot,
beacon_block_root = %execution_payload.message.beacon_block_root,
"Processing execution payload envelope"
);
#[allow(clippy::result_large_err)]
let result = self
.chain
.process_execution_payload_envelope(
beacon_block_root,
verified_envelope,
NotifyExecutionLayer::Yes,
BlockImportSource::Gossip,
|| Ok(()),
)
.await;
// For now, ignore all envelopes since verification is not implemented
self.propagate_validation_result(message_id, peer_id, MessageAcceptance::Ignore);
// TODO(gloas) metrics
// register_process_result_metrics(&result, metrics::BlockSource::Gossip, "envelope");
match &result {
Ok(AvailabilityProcessingStatus::Imported(_))
| Ok(AvailabilityProcessingStatus::MissingComponents(_, _)) => {
// Nothing to do
}
Err(_) => {
// TODO(gloas) implement peer penalties
}
}
}
pub fn process_gossip_execution_payload_bid(

View File

@@ -429,11 +429,17 @@ impl<T: BeaconChainTypes> NetworkBeaconProcessor<T> {
message_id: MessageId,
peer_id: PeerId,
execution_payload: Box<SignedExecutionPayloadEnvelope<T::EthSpec>>,
seen_timestamp: Duration,
) -> Result<(), Error<T::EthSpec>> {
let processor = self.clone();
let process_fn = async move {
processor
.process_gossip_execution_payload(message_id, peer_id, *execution_payload)
.process_gossip_execution_payload_envelope(
message_id,
peer_id,
Arc::new(*execution_payload),
seen_timestamp,
)
.await
};

View File

@@ -493,6 +493,7 @@ impl<T: BeaconChainTypes> Router<T> {
message_id,
peer_id,
signed_execution_payload_envelope,
timestamp_now(),
),
)
}