Import payload flow

This commit is contained in:
Eitan Seri- Levi
2026-02-11 23:34:53 -08:00
parent 9f972d1743
commit f637a68e04
11 changed files with 599 additions and 60 deletions

View File

@@ -1,28 +0,0 @@
use task_executor::JoinHandle;
use types::{EthSpec, FullPayload};
use crate::{
BeaconChainTypes, PayloadVerificationOutcome,
payload_envelope_verification::{MaybeAvailableEnvelope, PayloadEnvelopeImportData},
};
/// Used to await the result of executing payload with an EE.
pub type PayloadVerificationHandle<E: EthSpec> =
JoinHandle<Option<Result<PayloadVerificationOutcome, FullPayload<E>>>>;
/// A wrapper around a `SignedBeaconBlock` that indicates that this block is fully verified and
/// ready to import into the `BeaconChain`. The validation includes:
///
/// - Parent is known
/// - Signatures
/// - State root check
/// - Block processing
///
/// Note: a `ExecutionPendingEnvelope` is not _forever_ valid to be imported, it may later become invalid
/// due to finality or some other event. A `ExecutionPendingEnvelope` should be imported into the
/// `BeaconChain` immediately after it is instantiated.
pub struct ExecutionPendingEnvelope<T: BeaconChainTypes> {
pub block: MaybeAvailableEnvelope<T::EthSpec>,
pub import_data: PayloadEnvelopeImportData<T::EthSpec>,
pub payload_verification_handle: PayloadVerificationHandle<T::EthSpec>,
}

View File

@@ -3,14 +3,14 @@ use std::sync::Arc;
use educe::Educe;
use slot_clock::SlotClock;
use state_processing::{VerifySignatures, envelope_processing::process_execution_payload_envelope};
use tracing::debug;
use tracing::{Span, debug};
use types::{
EthSpec, SignedBeaconBlock, SignedExecutionPayloadEnvelope,
consts::gloas::BUILDER_INDEX_SELF_BUILD,
};
use crate::{
BeaconChain, BeaconChainError, BeaconChainTypes, NotifyExecutionLayer,
BeaconChain, BeaconChainError, BeaconChainTypes, BlockError, NotifyExecutionLayer,
PayloadVerificationOutcome,
payload_envelope_verification::{
EnvelopeError, EnvelopeImportData, EnvelopeProcessingSnapshot, ExecutionPendingEnvelope,
@@ -181,7 +181,7 @@ impl<T: BeaconChainTypes> IntoExecutionPendingEnvelope<T> for GossipVerifiedEnve
self,
chain: &Arc<BeaconChain<T>>,
notify_execution_layer: NotifyExecutionLayer,
) -> Result<ExecutionPendingEnvelope<T>, EnvelopeError> {
) -> Result<ExecutionPendingEnvelope<T::EthSpec>, BlockError> {
let signed_envelope = self.signed_envelope;
let envelope = &signed_envelope.message;
let payload = &envelope.payload;
@@ -255,4 +255,63 @@ impl<T: BeaconChainTypes> IntoExecutionPendingEnvelope<T> for GossipVerifiedEnve
payload_verification_handle,
})
}
fn envelope(&self) -> &Arc<SignedExecutionPayloadEnvelope<T::EthSpec>> {
&self.signed_envelope
}
}
impl<T: BeaconChainTypes> BeaconChain<T> {
/// Returns `Ok(GossipVerifiedEnvelope)` if the supplied `envelope` should be forwarded onto the
/// gossip network. The envelope is not imported into the chain, it is just partially verified.
///
/// The returned `GossipVerifiedEnvelope` should be provided to `Self::process_execution_payload_envelope` immediately
/// after it is returned, unless some other circumstance decides it should not be imported at
/// all.
///
/// ## Errors
///
/// Returns an `Err` if the given envelope was invalid, or an error was encountered during
pub async fn verify_envelope_for_gossip(
self: &Arc<Self>,
envelope: Arc<SignedExecutionPayloadEnvelope<T::EthSpec>>,
) -> Result<GossipVerifiedEnvelope<T>, EnvelopeError> {
let chain = self.clone();
let span = Span::current();
self.task_executor
.clone()
.spawn_blocking_handle(
move || {
let _guard = span.enter();
let slot = envelope.slot();
let beacon_block_root = envelope.message.beacon_block_root;
match GossipVerifiedEnvelope::new(envelope, &chain) {
Ok(verified) => {
debug!(
%slot,
?beacon_block_root,
"Successfully verified gossip envelope"
);
Ok(verified)
}
Err(e) => {
debug!(
error = e.to_string(),
?beacon_block_root,
%slot,
"Rejected gossip envelope"
);
Err(e)
}
}
},
"gossip_envelope_verification_handle",
)
.ok_or(BeaconChainError::RuntimeShutdown)?
.await
.map_err(BeaconChainError::TokioJoin)?
}
}

View File

@@ -34,17 +34,16 @@ use state_processing::{
use tracing::instrument;
use types::{
BeaconState, BeaconStateError, ChainSpec, DataColumnSidecarList, EthSpec, ExecutionBlockHash,
Hash256, SignedBeaconBlock, SignedExecutionPayloadEnvelope, Slot,
ExecutionPayloadEnvelope, Hash256, SignedBeaconBlock, SignedExecutionPayloadEnvelope, Slot,
};
use crate::{
AvailabilityProcessingStatus, BeaconChain, BeaconChainError, BeaconChainTypes,
ExecutionPayloadError, NotifyExecutionLayer, PayloadVerificationOutcome,
block_verification::PayloadVerificationHandle, block_verification_types::BlockImportData,
BeaconChain, BeaconChainError, BeaconChainTypes, BlockError, ExecutionPayloadError,
NotifyExecutionLayer, PayloadVerificationOutcome,
block_verification::PayloadVerificationHandle,
payload_envelope_verification::gossip_verified_envelope::GossipVerifiedEnvelope,
};
pub mod execution_pending_envelope;
pub mod gossip_verified_envelope;
mod payload_notifier;
mod tests;
@@ -54,12 +53,14 @@ pub trait IntoExecutionPendingEnvelope<T: BeaconChainTypes>: Sized {
self,
chain: &Arc<BeaconChain<T>>,
notify_execution_layer: NotifyExecutionLayer,
) -> Result<ExecutionPendingEnvelope<T>, EnvelopeError>;
) -> Result<ExecutionPendingEnvelope<T::EthSpec>, BlockError>;
fn envelope(&self) -> &Arc<SignedExecutionPayloadEnvelope<T::EthSpec>>;
}
pub struct ExecutionPendingEnvelope<T: BeaconChainTypes> {
pub signed_envelope: MaybeAvailableEnvelope<T::EthSpec>,
pub import_data: EnvelopeImportData<T::EthSpec>,
pub struct ExecutionPendingEnvelope<E: EthSpec> {
pub signed_envelope: MaybeAvailableEnvelope<E>,
pub import_data: EnvelopeImportData<E>,
pub payload_verification_handle: PayloadVerificationHandle,
}
@@ -82,6 +83,25 @@ pub struct AvailableEnvelope<E: EthSpec> {
pub spec: Arc<ChainSpec>,
}
impl<E: EthSpec> AvailableEnvelope<E> {
pub fn message(&self) -> &ExecutionPayloadEnvelope<E> {
&self.envelope.message
}
#[allow(clippy::type_complexity)]
pub fn deconstruct(
self,
) -> (
Arc<SignedExecutionPayloadEnvelope<E>>,
DataColumnSidecarList<E>,
) {
let AvailableEnvelope {
envelope, columns, ..
} = self;
(envelope, columns)
}
}
pub enum MaybeAvailableEnvelope<E: EthSpec> {
Available(AvailableEnvelope<E>),
AvailabilityPending {
@@ -204,6 +224,12 @@ pub enum EnvelopeError {
ExecutionPayloadError(ExecutionPayloadError),
}
impl std::fmt::Display for EnvelopeError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?}", self)
}
}
impl From<BeaconChainError> for EnvelopeError {
fn from(e: BeaconChainError) -> Self {
EnvelopeError::BeaconChainError(Arc::new(e))
@@ -248,7 +274,7 @@ impl From<EnvelopeProcessingError> for EnvelopeError {
pub(crate) fn load_snapshot<T: BeaconChainTypes>(
envelope: &SignedExecutionPayloadEnvelope<T::EthSpec>,
chain: &BeaconChain<T>,
) -> Result<EnvelopeProcessingSnapshot<T::EthSpec>, EnvelopeError> {
) -> Result<EnvelopeProcessingSnapshot<T::EthSpec>, BlockError> {
// Reject any block if its block is not known to fork choice.
//
// A block that is not in fork choice is either:
@@ -263,8 +289,8 @@ pub(crate) fn load_snapshot<T: BeaconChainTypes>(
let fork_choice_read_lock = chain.canonical_head.fork_choice_read_lock();
let beacon_block_root = envelope.beacon_block_root();
let Some(proto_beacon_block) = fork_choice_read_lock.get_block(&beacon_block_root) else {
return Err(EnvelopeError::BlockRootUnknown {
block_root: beacon_block_root,
return Err(BlockError::ParentUnknown {
parent_root: beacon_block_root,
});
};
drop(fork_choice_read_lock);
@@ -278,7 +304,7 @@ pub(crate) fn load_snapshot<T: BeaconChainTypes>(
let state = chain
.store
.get_hot_state(&block_state_root, cache_state)
.map_err(|e| EnvelopeError::BeaconChainError(Arc::new(e.into())))?
.map_err(|e| BlockError::BeaconChainError(Box::new(e.into())))?
.ok_or_else(|| {
BeaconChainError::DBInconsistent(format!(
"Missing state for envelope block {block_state_root:?}",
@@ -299,11 +325,15 @@ impl<T: BeaconChainTypes> IntoExecutionPendingEnvelope<T>
self,
chain: &Arc<BeaconChain<T>>,
notify_execution_layer: NotifyExecutionLayer,
) -> Result<ExecutionPendingEnvelope<T>, EnvelopeError> {
) -> Result<ExecutionPendingEnvelope<T::EthSpec>, BlockError> {
// TODO(EIP-7732): figure out how this should be refactored..
GossipVerifiedEnvelope::new(self, chain)?
.into_execution_pending_envelope(chain, notify_execution_layer)
}
fn envelope(&self) -> &Arc<SignedExecutionPayloadEnvelope<T::EthSpec>> {
self
}
}
#[derive(Clone, Debug, PartialEq)]

View File

@@ -8,7 +8,7 @@ use types::{SignedBeaconBlock, SignedExecutionPayloadEnvelope};
use crate::{
BeaconChain, BeaconChainTypes, BlockError, ExecutionPayloadError, NotifyExecutionLayer,
execution_payload::notify_new_payload,
execution_payload::notify_new_payload, payload_envelope_verification::EnvelopeError,
};
/// Used to await the result of executing payload with a remote EE.

View File

@@ -1,10 +1,13 @@
use std::sync::Arc;
use crate::{AvailabilityProcessingStatus, BeaconChain, BeaconChainTypes, payload_envelope_verification::{ExecutedEnvelope, ExecutionPendingEnvelope}};
use crate::{
AvailabilityProcessingStatus, BeaconChain, BeaconChainTypes,
payload_envelope_verification::{ExecutedEnvelope, ExecutionPendingEnvelope},
};
async fn import_execution_pending_envelope<T: BeaconChainTypes>(
chain: Arc<BeaconChain<T>>,
execution_pending_envelope: ExecutionPendingEnvelope<T>,
execution_pending_envelope: ExecutionPendingEnvelope<T::EthSpec>,
) -> Result<AvailabilityProcessingStatus, String> {
match chain
.clone()