//! The incremental processing steps (e.g., signatures verified but not the state transition) is //! represented as a sequence of wrapper-types around the envelope. There is a linear progression of //! types, starting at a `SignedExecutionPayloadEnvelope` and finishing with an `AvailableExecutedEnvelope` (see //! diagram below). //! //! ```ignore //! SignedExecutionPayloadEnvelope //! | //! ▼ //! GossipVerifiedEnvelope //! | //! ▼ //! ExecutionPendingEnvelope //! | //! await //! ▼ //! ExecutedEnvelope //! //! ``` use state_processing::envelope_processing::EnvelopeProcessingError; use std::sync::Arc; use store::Error as DBError; use strum::AsRefStr; use tracing::instrument; use types::{ BeaconState, BeaconStateError, DataColumnSidecarList, EthSpec, ExecutionBlockHash, ExecutionPayloadEnvelope, Hash256, SignedExecutionPayloadEnvelope, Slot, }; use crate::{ BeaconChainError, BeaconChainTypes, BeaconStore, BlockError, ExecutionPayloadError, PayloadVerificationOutcome, }; pub mod execution_pending_envelope; pub mod gossip_verified_envelope; pub mod import; mod payload_notifier; pub use execution_pending_envelope::ExecutionPendingEnvelope; #[derive(Debug)] pub struct AvailableEnvelope { envelope: Arc>, pub columns: DataColumnSidecarList, } impl AvailableEnvelope { pub fn new( envelope: Arc>, columns: DataColumnSidecarList, ) -> Self { Self { envelope, columns } } pub fn message(&self) -> &ExecutionPayloadEnvelope { &self.envelope.message } #[allow(clippy::type_complexity)] pub fn deconstruct( self, ) -> ( Arc>, DataColumnSidecarList, ) { let AvailableEnvelope { envelope, columns, .. } = self; (envelope, columns) } } /// This snapshot is to be used for verifying a payload envelope. #[derive(Debug, Clone)] pub struct EnvelopeProcessingSnapshot { /// This state is equivalent to the `self.beacon_block.state_root()` before applying the envelope. pub pre_state: BeaconState, pub state_root: Hash256, pub beacon_block_root: Hash256, } /// A payload envelope that has completed all envelope processing checks, verification /// by an EL client but does not have all requisite columns to get imported into /// fork choice. pub struct AvailabilityPendingExecutedEnvelope { pub envelope: Arc>, pub block_root: Hash256, pub payload_verification_outcome: PayloadVerificationOutcome, } impl AvailabilityPendingExecutedEnvelope { pub fn new( envelope: Arc>, block_root: Hash256, payload_verification_outcome: PayloadVerificationOutcome, ) -> Self { Self { envelope, block_root, payload_verification_outcome, } } } /// A payload envelope that has completed all payload processing checks including verification /// by an EL client **and** has all requisite blob data to be imported into fork choice. pub struct AvailableExecutedEnvelope { pub envelope: AvailableEnvelope, pub block_root: Hash256, pub payload_verification_outcome: PayloadVerificationOutcome, } impl AvailableExecutedEnvelope { pub fn new( envelope: AvailableEnvelope, block_root: Hash256, payload_verification_outcome: PayloadVerificationOutcome, ) -> Self { Self { envelope, block_root, payload_verification_outcome, } } } #[derive(Debug, AsRefStr)] pub enum EnvelopeError { /// The envelope's block root is unknown. BlockRootUnknown { block_root: Hash256 }, /// The signature is invalid. BadSignature, /// The builder index doesn't match the committed bid BuilderIndexMismatch { committed_bid: u64, envelope: u64 }, /// The envelope slot doesn't match the block SlotMismatch { block: Slot, envelope: Slot }, /// The validator index is unknown UnknownValidator { proposer_index: u64 }, /// The block hash doesn't match the committed bid BlockHashMismatch { committed_bid: ExecutionBlockHash, envelope: ExecutionBlockHash, }, /// The block's proposer_index does not match the locally computed proposer IncorrectBlockProposer { proposer_index: u64, local_shuffling: u64, }, /// The slot belongs to a block that is from a slot prior than /// to most recently finalized slot PriorToFinalization { payload_slot: Slot, latest_finalized_slot: Slot, }, /// Some Beacon Chain Error BeaconChainError(Arc), /// Some Beacon State error BeaconStateError(BeaconStateError), /// Some EnvelopeProcessingError EnvelopeProcessingError(EnvelopeProcessingError), /// Error verifying the execution payload ExecutionPayloadError(ExecutionPayloadError), /// An error from importing the envelope. ImportError(BlockError), } impl std::fmt::Display for EnvelopeError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{:?}", self) } } impl From for EnvelopeError { fn from(e: BeaconChainError) -> Self { EnvelopeError::BeaconChainError(Arc::new(e)) } } impl From for EnvelopeError { fn from(e: ExecutionPayloadError) -> Self { EnvelopeError::ExecutionPayloadError(e) } } impl From for EnvelopeError { fn from(e: BeaconStateError) -> Self { EnvelopeError::BeaconStateError(e) } } impl From for EnvelopeError { fn from(e: DBError) -> Self { EnvelopeError::BeaconChainError(Arc::new(BeaconChainError::DBError(e))) } } impl From for EnvelopeError { fn from(e: EnvelopeProcessingError) -> Self { match e { EnvelopeProcessingError::BadSignature => EnvelopeError::BadSignature, EnvelopeProcessingError::BeaconStateError(e) => EnvelopeError::BeaconStateError(e), EnvelopeProcessingError::BlockHashMismatch { committed_bid, envelope, } => EnvelopeError::BlockHashMismatch { committed_bid, envelope, }, e => EnvelopeError::EnvelopeProcessingError(e), } } } #[instrument(skip_all, level = "debug", fields(beacon_block_root = %beacon_block_root))] /// Load state from store given a known state root and block root. /// Use this when the proto block has already been looked up from fork choice. pub(crate) fn load_snapshot_from_state_root( beacon_block_root: Hash256, block_state_root: Hash256, store: &BeaconStore, ) -> Result, EnvelopeError> { // TODO(EIP-7732): add metrics here // We can use `get_hot_state` here rather than `get_advanced_hot_state` because the envelope // must be from the same slot as its block (so no advance is required). let cache_state = true; let state = store .get_hot_state(&block_state_root, cache_state) .map_err(EnvelopeError::from)? .ok_or_else(|| { BeaconChainError::DBInconsistent(format!( "Missing state for envelope block {block_state_root:?}", )) })?; Ok(EnvelopeProcessingSnapshot { pre_state: state, state_root: block_state_root, beacon_block_root, }) }