mirror of
https://github.com/sigp/lighthouse.git
synced 2026-05-29 20:27:14 +00:00
Progress
This commit is contained in:
@@ -1147,7 +1147,6 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Returns the full block at the given root, if it's available in the database.
|
||||
///
|
||||
/// Should always return a full block for pre-merge and post-gloas blocks.
|
||||
|
||||
@@ -718,7 +718,8 @@ pub struct SignatureVerifiedBlock<T: BeaconChainTypes> {
|
||||
}
|
||||
|
||||
/// Used to await the result of executing payload with an EE.
|
||||
pub type PayloadVerificationHandle = JoinHandle<Option<Result<PayloadVerificationOutcome, BlockError>>>;
|
||||
pub type PayloadVerificationHandle =
|
||||
JoinHandle<Option<Result<PayloadVerificationOutcome, BlockError>>>;
|
||||
|
||||
/// A wrapper around a `SignedBeaconBlock` that indicates that this block is fully verified and
|
||||
/// ready to import into the `BeaconChain`. The validation includes:
|
||||
|
||||
@@ -113,7 +113,7 @@ impl<T: BeaconChainTypes> PayloadNotifier<T> {
|
||||
if let Some(precomputed_status) = self.payload_verification_status {
|
||||
Ok(precomputed_status)
|
||||
} else {
|
||||
notify_new_payload(&self.chain, self.block.message()).await
|
||||
notify_new_payload(&self.chain, self.block.message().tree_hash_root(), self.block.message().try_into()?).await
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -127,17 +127,18 @@ impl<T: BeaconChainTypes> PayloadNotifier<T> {
|
||||
/// contains a few extra checks by running `partially_verify_execution_payload` first:
|
||||
///
|
||||
/// https://github.com/ethereum/consensus-specs/blob/v1.1.9/specs/bellatrix/beacon-chain.md#notify_new_payload
|
||||
async fn notify_new_payload<T: BeaconChainTypes>(
|
||||
pub async fn notify_new_payload<T: BeaconChainTypes>(
|
||||
chain: &Arc<BeaconChain<T>>,
|
||||
block: BeaconBlockRef<'_, T::EthSpec>,
|
||||
beacon_block_root: Hash256,
|
||||
new_payload_request: NewPayloadRequest<'_, T::EthSpec>,
|
||||
) -> Result<PayloadVerificationStatus, BlockError> {
|
||||
let execution_layer = chain
|
||||
.execution_layer
|
||||
.as_ref()
|
||||
.ok_or(ExecutionPayloadError::NoExecutionConnection)?;
|
||||
|
||||
let execution_block_hash = block.execution_payload()?.block_hash();
|
||||
let new_payload_response = execution_layer.notify_new_payload(block.try_into()?).await;
|
||||
let execution_block_hash = new_payload_request.execution_payload_ref().block_hash();
|
||||
let new_payload_response = execution_layer.notify_new_payload(new_payload_request.clone()).await;
|
||||
|
||||
match new_payload_response {
|
||||
Ok(status) => match status {
|
||||
@@ -153,10 +154,10 @@ async fn notify_new_payload<T: BeaconChainTypes>(
|
||||
?validation_error,
|
||||
?latest_valid_hash,
|
||||
?execution_block_hash,
|
||||
root = ?block.tree_hash_root(),
|
||||
graffiti = block.body().graffiti().as_utf8_lossy(),
|
||||
proposer_index = block.proposer_index(),
|
||||
slot = %block.slot(),
|
||||
// root = ?block.tree_hash_root(),
|
||||
// graffiti = block.body().graffiti().as_utf8_lossy(),
|
||||
// proposer_index = block.proposer_index(),
|
||||
// slot = %block.slot(),
|
||||
method = "new_payload",
|
||||
"Invalid execution payload"
|
||||
);
|
||||
@@ -179,11 +180,11 @@ async fn notify_new_payload<T: BeaconChainTypes>(
|
||||
{
|
||||
// This block has not yet been applied to fork choice, so the latest block that was
|
||||
// imported to fork choice was the parent.
|
||||
let latest_root = block.parent_root();
|
||||
let latest_root = new_payload_request.parent_beacon_block_root()?;
|
||||
|
||||
chain
|
||||
.process_invalid_execution_payload(&InvalidationOperation::InvalidateMany {
|
||||
head_block_root: latest_root,
|
||||
head_block_root: *latest_root,
|
||||
always_invalidate_head: false,
|
||||
latest_valid_ancestor: latest_valid_hash,
|
||||
})
|
||||
@@ -198,10 +199,10 @@ async fn notify_new_payload<T: BeaconChainTypes>(
|
||||
warn!(
|
||||
?validation_error,
|
||||
?execution_block_hash,
|
||||
root = ?block.tree_hash_root(),
|
||||
graffiti = block.body().graffiti().as_utf8_lossy(),
|
||||
proposer_index = block.proposer_index(),
|
||||
slot = %block.slot(),
|
||||
// root = ?block.tree_hash_root(),
|
||||
// graffiti = block.body().graffiti().as_utf8_lossy(),
|
||||
// proposer_index = block.proposer_index(),
|
||||
// slot = %block.slot(),
|
||||
method = "new_payload",
|
||||
"Invalid execution payload block hash"
|
||||
);
|
||||
|
||||
@@ -43,7 +43,6 @@ pub mod observed_data_sidecars;
|
||||
pub mod observed_operations;
|
||||
mod observed_slashable;
|
||||
pub mod payload_envelope_verification;
|
||||
pub mod payload_envelope_verification_types;
|
||||
pub mod persisted_beacon_chain;
|
||||
pub mod persisted_custody;
|
||||
mod persisted_fork_choice;
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
use task_executor::JoinHandle;
|
||||
use types::{EthSpec, FullPayload};
|
||||
|
||||
use crate::{BeaconChainTypes, PayloadVerificationOutcome, payload_envelope_verification::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: MaybeAvailableBlock<T::EthSpec>,
|
||||
pub import_data: PayloadEnvelopeImportData<T::EthSpec>,
|
||||
pub payload_verification_handle: PayloadVerificationHandle<E>,
|
||||
}
|
||||
@@ -1,175 +1,14 @@
|
||||
//! The incremental processing steps (e.g., signatures verified but not the state transition) is
|
||||
//! represented as a sequence of wrapper-types around the block. There is a linear progression of
|
||||
//! types, starting at a `SignedBeaconBlock` and finishing with a `Fully VerifiedBlock` (see
|
||||
//! diagram below).
|
||||
//!
|
||||
//! ```ignore
|
||||
//! START
|
||||
//! |
|
||||
//! ▼
|
||||
//! SignedExecutionPayloadEnvelope
|
||||
//! |
|
||||
//! |---------------
|
||||
//! | |
|
||||
//! | ▼
|
||||
//! | GossipVerifiedEnvelope
|
||||
//! | |
|
||||
//! |---------------
|
||||
//! |
|
||||
//! ▼
|
||||
//! ExecutionPendingEnvelope
|
||||
//! |
|
||||
//! await
|
||||
//! |
|
||||
//! ▼
|
||||
//! END
|
||||
//!
|
||||
//! ```
|
||||
|
||||
use crate::NotifyExecutionLayer;
|
||||
use crate::block_verification::{PayloadVerificationHandle, PayloadVerificationOutcome};
|
||||
use crate::payload_envelope_verification_types::{EnvelopeImportData, MaybeAvailableEnvelope};
|
||||
use crate::execution_payload::PayloadNotifier;
|
||||
use crate::{BeaconChain, BeaconChainError, BeaconChainTypes};
|
||||
use educe::Educe;
|
||||
use slot_clock::SlotClock;
|
||||
use state_processing::envelope_processing::{EnvelopeProcessingError, process_execution_payload_envelope};
|
||||
use state_processing::{BlockProcessingError, VerifySignatures};
|
||||
use std::sync::Arc;
|
||||
use tracing::{debug, instrument};
|
||||
use types::{
|
||||
BeaconState, BeaconStateError, EthSpec, ExecutionBlockHash, Hash256, SignedBeaconBlock,
|
||||
SignedExecutionPayloadEnvelope, Slot,
|
||||
|
||||
use educe::Educe;
|
||||
use state_processing::{VerifySignatures, envelope_processing::process_execution_payload_envelope};
|
||||
use tracing::debug;
|
||||
use types::{EthSpec, SignedBeaconBlock, SignedExecutionPayloadEnvelope};
|
||||
|
||||
use crate::{
|
||||
BeaconChain, BeaconChainError, BeaconChainTypes, NotifyExecutionLayer, PayloadVerificationOutcome, payload_envelope_verification::{EnvelopeError, EnvelopeImportData, EnvelopeProcessingSnapshot, ExecutionPendingEnvelope, IntoExecutionPendingEnvelope, MaybeAvailableEnvelope, load_snapshot, payload_notifier::PayloadNotifier}
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
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 {
|
||||
builder_index: u64,
|
||||
},
|
||||
// The block hash doesn't match the committed bid
|
||||
BlockHashMismatch {
|
||||
committed_bid: ExecutionBlockHash,
|
||||
envelope: ExecutionBlockHash,
|
||||
},
|
||||
// Some Beacon Chain Error
|
||||
BeaconChainError(Arc<BeaconChainError>),
|
||||
// Some Beacon State error
|
||||
BeaconStateError(BeaconStateError),
|
||||
// Some BlockProcessingError (for electra operations)
|
||||
BlockProcessingError(BlockProcessingError),
|
||||
// Some EnvelopeProcessingError
|
||||
EnvelopeProcessingError(EnvelopeProcessingError),
|
||||
}
|
||||
|
||||
impl From<BeaconChainError> for EnvelopeError {
|
||||
fn from(e: BeaconChainError) -> Self {
|
||||
EnvelopeError::BeaconChainError(Arc::new(e))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<BeaconStateError> for EnvelopeError {
|
||||
fn from(e: BeaconStateError) -> Self {
|
||||
EnvelopeError::BeaconStateError(e)
|
||||
}
|
||||
}
|
||||
|
||||
/// Pull errors up from EnvelopeProcessingError to EnvelopeError
|
||||
impl From<EnvelopeProcessingError> 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,
|
||||
},
|
||||
EnvelopeProcessingError::BlockProcessingError(e) => {
|
||||
EnvelopeError::BlockProcessingError(e)
|
||||
}
|
||||
e => EnvelopeError::EnvelopeProcessingError(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// This snapshot is to be used for verifying a envelope of the block.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct EnvelopeProcessingSnapshot<E: EthSpec> {
|
||||
/// This state is equivalent to the `self.beacon_block.state_root()` before applying the envelope.
|
||||
pub pre_state: BeaconState<E>,
|
||||
pub state_root: Hash256,
|
||||
pub beacon_block_root: Hash256,
|
||||
}
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
#[instrument(skip_all, level = "debug", fields(beacon_block_root = %envelope.beacon_block_root()))]
|
||||
fn load_snapshot<T: BeaconChainTypes>(
|
||||
envelope: &SignedExecutionPayloadEnvelope<T::EthSpec>,
|
||||
chain: &BeaconChain<T>,
|
||||
) -> Result<EnvelopeProcessingSnapshot<T::EthSpec>, EnvelopeError> {
|
||||
// Reject any block if its block is not known to fork choice.
|
||||
//
|
||||
// A block that is not in fork choice is either:
|
||||
//
|
||||
// - Not yet imported: we should reject this block because we should only import a child
|
||||
// envelope after its parent has been fully imported.
|
||||
// - Pre-finalized: if the block is _prior_ to finalization, we should ignore the envelope
|
||||
// because it will revert finalization. Note that the finalized block is stored in fork
|
||||
// choice, so we will not reject any child of the finalized block (this is relevant during
|
||||
// genesis).
|
||||
|
||||
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,
|
||||
});
|
||||
};
|
||||
drop(fork_choice_read_lock);
|
||||
|
||||
// TODO(EIP-7732): add metrics here
|
||||
|
||||
let block_state_root = proto_beacon_block.state_root;
|
||||
// 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 = chain
|
||||
.store
|
||||
.get_hot_state(&block_state_root, cache_state)
|
||||
.map_err(|e| EnvelopeError::BeaconChainError(Arc::new(e.into())))?
|
||||
.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,
|
||||
})
|
||||
}
|
||||
|
||||
/// A wrapper around a `SignedExecutionPayloadEnvelope` that indicates it has been approved for re-gossiping on
|
||||
/// the p2p network.
|
||||
#[derive(Educe)]
|
||||
@@ -313,20 +152,6 @@ impl<T: BeaconChainTypes> GossipVerifiedEnvelope<T> {
|
||||
}
|
||||
}
|
||||
|
||||
pub trait IntoExecutionPendingEnvelope<T: BeaconChainTypes>: Sized {
|
||||
fn into_execution_pending_envelope(
|
||||
self,
|
||||
chain: &Arc<BeaconChain<T>>,
|
||||
notify_execution_layer: NotifyExecutionLayer,
|
||||
) -> Result<ExecutionPendingEnvelope<T>, EnvelopeError>;
|
||||
}
|
||||
|
||||
pub struct ExecutionPendingEnvelope<T: BeaconChainTypes> {
|
||||
pub signed_envelope: MaybeAvailableEnvelope<T::EthSpec>,
|
||||
pub import_data: EnvelopeImportData<T::EthSpec>,
|
||||
pub payload_verification_handle: PayloadVerificationHandle,
|
||||
}
|
||||
|
||||
impl<T: BeaconChainTypes> IntoExecutionPendingEnvelope<T> for GossipVerifiedEnvelope<T> {
|
||||
fn into_execution_pending_envelope(
|
||||
self,
|
||||
@@ -336,10 +161,13 @@ impl<T: BeaconChainTypes> IntoExecutionPendingEnvelope<T> for GossipVerifiedEnve
|
||||
let signed_envelope = self.signed_envelope;
|
||||
let envelope = &signed_envelope.message;
|
||||
let payload = &envelope.payload;
|
||||
|
||||
// TODO(gloas) unwrap
|
||||
let bid = chain.get_full_block(&envelope.beacon_block_root).unwrap().unwrap().message().body().signed_execution_payload_bid().unwrap().message;
|
||||
|
||||
// Verify the execution payload is valid
|
||||
let payload_notifier =
|
||||
PayloadNotifier::from_envelope(chain.clone(), envelope, notify_execution_layer)?;
|
||||
PayloadNotifier::new(chain.clone(), envelope, notify_execution_layer)?;
|
||||
let block_root = envelope.beacon_block_root;
|
||||
let slot = self.block.slot();
|
||||
|
||||
@@ -403,17 +231,3 @@ impl<T: BeaconChainTypes> IntoExecutionPendingEnvelope<T> for GossipVerifiedEnve
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: BeaconChainTypes> IntoExecutionPendingEnvelope<T>
|
||||
for Arc<SignedExecutionPayloadEnvelope<T::EthSpec>>
|
||||
{
|
||||
fn into_execution_pending_envelope(
|
||||
self,
|
||||
chain: &Arc<BeaconChain<T>>,
|
||||
notify_execution_layer: NotifyExecutionLayer,
|
||||
) -> Result<ExecutionPendingEnvelope<T>, EnvelopeError> {
|
||||
// TODO(EIP-7732): figure out how this should be refactored..
|
||||
GossipVerifiedEnvelope::new(self, chain)?
|
||||
.into_execution_pending_envelope(chain, notify_execution_layer)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,229 @@
|
||||
//! The incremental processing steps (e.g., signatures verified but not the state transition) is
|
||||
//! represented as a sequence of wrapper-types around the block. There is a linear progression of
|
||||
//! types, starting at a `SignedBeaconBlock` and finishing with a `Fully VerifiedBlock` (see
|
||||
//! diagram below).
|
||||
//!
|
||||
//! ```ignore
|
||||
//! START
|
||||
//! |
|
||||
//! ▼
|
||||
//! SignedExecutionPayloadEnvelope
|
||||
//! |
|
||||
//! |---------------
|
||||
//! | |
|
||||
//! | ▼
|
||||
//! | GossipVerifiedEnvelope
|
||||
//! | |
|
||||
//! |---------------
|
||||
//! |
|
||||
//! ▼
|
||||
//! ExecutionPendingEnvelope
|
||||
//! |
|
||||
//! await
|
||||
//! |
|
||||
//! ▼
|
||||
//! END
|
||||
//!
|
||||
//! ```
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use state_processing::{BlockProcessingError, ConsensusContext, envelope_processing::EnvelopeProcessingError};
|
||||
use tracing::instrument;
|
||||
use types::{BeaconState, BeaconStateError, ChainSpec, DataColumnSidecarList, EthSpec, ExecutionBlockHash, Hash256, SignedBeaconBlock, SignedExecutionPayloadEnvelope, Slot};
|
||||
|
||||
use crate::{BeaconChain, BeaconChainError, BeaconChainTypes, NotifyExecutionLayer, block_verification::PayloadVerificationHandle, payload_envelope_verification::gossip_verified_envelope::GossipVerifiedEnvelope};
|
||||
|
||||
pub mod execution_pending_envelope;
|
||||
pub mod gossip_verified_envelope;
|
||||
mod payload_notifier;
|
||||
|
||||
pub trait IntoExecutionPendingEnvelope<T: BeaconChainTypes>: Sized {
|
||||
fn into_execution_pending_envelope(
|
||||
self,
|
||||
chain: &Arc<BeaconChain<T>>,
|
||||
notify_execution_layer: NotifyExecutionLayer,
|
||||
) -> Result<ExecutionPendingEnvelope<T>, EnvelopeError>;
|
||||
}
|
||||
|
||||
pub struct ExecutionPendingEnvelope<T: BeaconChainTypes> {
|
||||
pub signed_envelope: MaybeAvailableEnvelope<T::EthSpec>,
|
||||
pub import_data: EnvelopeImportData<T::EthSpec>,
|
||||
pub payload_verification_handle: PayloadVerificationHandle,
|
||||
}
|
||||
|
||||
#[derive(PartialEq)]
|
||||
pub struct EnvelopeImportData<E: EthSpec> {
|
||||
pub block_root: Hash256,
|
||||
pub block: Arc<SignedBeaconBlock<E>>,
|
||||
pub post_state: Box<BeaconState<E>>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[allow(dead_code)]
|
||||
pub struct AvailableEnvelope<E: EthSpec> {
|
||||
// TODO(EIP-7732): rename to execution_block_hash
|
||||
block_hash: ExecutionBlockHash,
|
||||
envelope: Arc<SignedExecutionPayloadEnvelope<E>>,
|
||||
columns: DataColumnSidecarList<E>,
|
||||
/// Timestamp at which this block first became available (UNIX timestamp, time since 1970).
|
||||
columns_available_timestamp: Option<std::time::Duration>,
|
||||
pub spec: Arc<ChainSpec>,
|
||||
}
|
||||
|
||||
pub enum MaybeAvailableEnvelope<E: EthSpec> {
|
||||
Available(AvailableEnvelope<E>),
|
||||
AvailabilityPending {
|
||||
block_hash: ExecutionBlockHash,
|
||||
envelope: Arc<SignedExecutionPayloadEnvelope<E>>,
|
||||
},
|
||||
}
|
||||
|
||||
/// This snapshot is to be used for verifying a envelope of the block.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct EnvelopeProcessingSnapshot<E: EthSpec> {
|
||||
/// This state is equivalent to the `self.beacon_block.state_root()` before applying the envelope.
|
||||
pub pre_state: BeaconState<E>,
|
||||
pub state_root: Hash256,
|
||||
pub beacon_block_root: Hash256,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
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 {
|
||||
builder_index: u64,
|
||||
},
|
||||
// The block hash doesn't match the committed bid
|
||||
BlockHashMismatch {
|
||||
committed_bid: ExecutionBlockHash,
|
||||
envelope: ExecutionBlockHash,
|
||||
},
|
||||
// Some Beacon Chain Error
|
||||
BeaconChainError(Arc<BeaconChainError>),
|
||||
// Some Beacon State error
|
||||
BeaconStateError(BeaconStateError),
|
||||
// Some BlockProcessingError (for electra operations)
|
||||
BlockProcessingError(BlockProcessingError),
|
||||
// Some EnvelopeProcessingError
|
||||
EnvelopeProcessingError(EnvelopeProcessingError),
|
||||
}
|
||||
|
||||
impl From<BeaconChainError> for EnvelopeError {
|
||||
fn from(e: BeaconChainError) -> Self {
|
||||
EnvelopeError::BeaconChainError(Arc::new(e))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<BeaconStateError> for EnvelopeError {
|
||||
fn from(e: BeaconStateError) -> Self {
|
||||
EnvelopeError::BeaconStateError(e)
|
||||
}
|
||||
}
|
||||
|
||||
/// Pull errors up from EnvelopeProcessingError to EnvelopeError
|
||||
impl From<EnvelopeProcessingError> 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,
|
||||
},
|
||||
EnvelopeProcessingError::BlockProcessingError(e) => {
|
||||
EnvelopeError::BlockProcessingError(e)
|
||||
}
|
||||
e => EnvelopeError::EnvelopeProcessingError(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
#[instrument(skip_all, level = "debug", fields(beacon_block_root = %envelope.beacon_block_root()))]
|
||||
pub(crate) fn load_snapshot<T: BeaconChainTypes>(
|
||||
envelope: &SignedExecutionPayloadEnvelope<T::EthSpec>,
|
||||
chain: &BeaconChain<T>,
|
||||
) -> Result<EnvelopeProcessingSnapshot<T::EthSpec>, EnvelopeError> {
|
||||
// Reject any block if its block is not known to fork choice.
|
||||
//
|
||||
// A block that is not in fork choice is either:
|
||||
//
|
||||
// - Not yet imported: we should reject this block because we should only import a child
|
||||
// envelope after its parent has been fully imported.
|
||||
// - Pre-finalized: if the block is _prior_ to finalization, we should ignore the envelope
|
||||
// because it will revert finalization. Note that the finalized block is stored in fork
|
||||
// choice, so we will not reject any child of the finalized block (this is relevant during
|
||||
// genesis).
|
||||
|
||||
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,
|
||||
});
|
||||
};
|
||||
drop(fork_choice_read_lock);
|
||||
|
||||
// TODO(EIP-7732): add metrics here
|
||||
|
||||
let block_state_root = proto_beacon_block.state_root;
|
||||
// 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 = chain
|
||||
.store
|
||||
.get_hot_state(&block_state_root, cache_state)
|
||||
.map_err(|e| EnvelopeError::BeaconChainError(Arc::new(e.into())))?
|
||||
.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,
|
||||
})
|
||||
}
|
||||
|
||||
impl<T: BeaconChainTypes> IntoExecutionPendingEnvelope<T>
|
||||
for Arc<SignedExecutionPayloadEnvelope<T::EthSpec>>
|
||||
{
|
||||
fn into_execution_pending_envelope(
|
||||
self,
|
||||
chain: &Arc<BeaconChain<T>>,
|
||||
notify_execution_layer: NotifyExecutionLayer,
|
||||
) -> Result<ExecutionPendingEnvelope<T>, EnvelopeError> {
|
||||
// TODO(EIP-7732): figure out how this should be refactored..
|
||||
GossipVerifiedEnvelope::new(self, chain)?
|
||||
.into_execution_pending_envelope(chain, notify_execution_layer)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct PayloadEnvelopeImportData<E: EthSpec> {
|
||||
pub block_root: Hash256,
|
||||
pub state: BeaconState<E>,
|
||||
pub consensus_context: ConsensusContext<E>,
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use execution_layer::NewPayloadRequest;
|
||||
use fork_choice::PayloadVerificationStatus;
|
||||
use state_processing::{envelope_processing::partially_verify_payload_envelope, per_block_processing::is_execution_enabled};
|
||||
use tracing::warn;
|
||||
use types::{BeaconState, ExecutionPayloadBid, Hash256, SignedBeaconBlock, SignedExecutionPayloadEnvelope};
|
||||
|
||||
use crate::{BeaconChain, BeaconChainTypes, BlockError, ExecutionPayloadError, NotifyExecutionLayer, execution_payload::notify_new_payload};
|
||||
|
||||
|
||||
/// Used to await the result of executing payload with a remote EE.
|
||||
pub struct PayloadNotifier<T: BeaconChainTypes> {
|
||||
pub chain: Arc<BeaconChain<T>>,
|
||||
pub envelope: Arc<SignedExecutionPayloadEnvelope<T::EthSpec>>,
|
||||
payload_verification_status: Option<PayloadVerificationStatus>,
|
||||
}
|
||||
|
||||
impl<T: BeaconChainTypes> PayloadNotifier<T> {
|
||||
pub fn new(
|
||||
chain: Arc<BeaconChain<T>>,
|
||||
bid: &ExecutionPayloadBid<T::EthSpec>,
|
||||
envelope: Arc<SignedExecutionPayloadEnvelope<T::EthSpec>>,
|
||||
state: &BeaconState<T::EthSpec>,
|
||||
notify_execution_layer: NotifyExecutionLayer,
|
||||
) -> Result<Self, ExecutionPayloadError> {
|
||||
let payload_verification_status = {
|
||||
// Perform the initial stages of payload verification.
|
||||
//
|
||||
// We will duplicate these checks again during `per_block_processing`, however these
|
||||
// checks are cheap and doing them here ensures we have verified them before marking
|
||||
// the block as optimistically imported. This is particularly relevant in the case
|
||||
// where we do not send the block to the EL at all.
|
||||
let payload_message = &envelope.message;
|
||||
partially_verify_payload_envelope(
|
||||
state,
|
||||
&envelope,
|
||||
&chain.spec,
|
||||
).unwrap(); // TODO(gloas) unwrap
|
||||
|
||||
match notify_execution_layer {
|
||||
NotifyExecutionLayer::No if chain.config.optimistic_finalized_sync => {
|
||||
// Create a NewPayloadRequest (no clones required) and check optimistic sync verifications
|
||||
let new_payload_request: NewPayloadRequest<T::EthSpec> =
|
||||
payload_message.try_into()?;
|
||||
if let Err(e) = new_payload_request.perform_optimistic_sync_verifications() {
|
||||
warn!(
|
||||
block_number = ?payload_message.payload.block_number,
|
||||
info = "you can silence this warning with --disable-optimistic-finalized-sync",
|
||||
error = ?e,
|
||||
"Falling back to slow block hash verification"
|
||||
);
|
||||
None
|
||||
} else {
|
||||
Some(PayloadVerificationStatus::Optimistic)
|
||||
}
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
chain,
|
||||
envelope,
|
||||
payload_verification_status,
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn notify_new_payload(self) -> Result<PayloadVerificationStatus, BlockError> {
|
||||
if let Some(precomputed_status) = self.payload_verification_status {
|
||||
Ok(precomputed_status)
|
||||
} else {
|
||||
// tODO(gloas) fix zero
|
||||
notify_new_payload(&self.chain, Hash256::ZERO, self.envelope.message.try_into()?).await
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
use std::sync::Arc;
|
||||
use types::{
|
||||
BeaconState, ChainSpec, DataColumnSidecarList, EthSpec, ExecutionBlockHash, Hash256,
|
||||
SignedBeaconBlock, SignedExecutionPayloadEnvelope,
|
||||
};
|
||||
|
||||
#[derive(PartialEq)]
|
||||
pub struct EnvelopeImportData<E: EthSpec> {
|
||||
pub block_root: Hash256,
|
||||
pub block: Arc<SignedBeaconBlock<E>>,
|
||||
pub post_state: Box<BeaconState<E>>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[allow(dead_code)]
|
||||
pub struct AvailableEnvelope<E: EthSpec> {
|
||||
// TODO(EIP-7732): rename to execution_block_hash
|
||||
block_hash: ExecutionBlockHash,
|
||||
envelope: Arc<SignedExecutionPayloadEnvelope<E>>,
|
||||
columns: DataColumnSidecarList<E>,
|
||||
/// Timestamp at which this block first became available (UNIX timestamp, time since 1970).
|
||||
columns_available_timestamp: Option<std::time::Duration>,
|
||||
pub spec: Arc<ChainSpec>,
|
||||
}
|
||||
pub enum MaybeAvailableEnvelope<E: EthSpec> {
|
||||
Available(AvailableEnvelope<E>),
|
||||
AvailabilityPending {
|
||||
block_hash: ExecutionBlockHash,
|
||||
envelope: Arc<SignedExecutionPayloadEnvelope<E>>,
|
||||
},
|
||||
}
|
||||
@@ -4,8 +4,7 @@ use crate::versioned_hashes::verify_versioned_hashes;
|
||||
use state_processing::per_block_processing::deneb::kzg_commitment_to_versioned_hash;
|
||||
use superstruct::superstruct;
|
||||
use types::{
|
||||
BeaconBlockRef, BeaconStateError, EthSpec, ExecutionBlockHash, ExecutionPayload,
|
||||
ExecutionPayloadRef, Hash256, VersionedHash,
|
||||
BeaconBlockRef, BeaconStateError, EthSpec, ExecutionBlockHash, ExecutionPayload, ExecutionPayloadEnvelope, ExecutionPayloadRef, Hash256, VersionedHash
|
||||
};
|
||||
use types::{
|
||||
ExecutionPayloadBellatrix, ExecutionPayloadCapella, ExecutionPayloadDeneb,
|
||||
|
||||
@@ -276,3 +276,104 @@ pub fn process_execution_payload_envelope<E: EthSpec>(
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
/// Performs *partial* verification of the `payload envelope`.
|
||||
pub fn partially_verify_payload_envelope<E: EthSpec>(
|
||||
state: &BeaconState<E>,
|
||||
signed_envelope: &SignedExecutionPayloadEnvelope<E>,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<(), EnvelopeProcessingError> {
|
||||
let envelope = &signed_envelope.message;
|
||||
let payload = &signed_envelope.message.payload;
|
||||
|
||||
// Verify consistency with the beacon block
|
||||
let latest_block_header_root = state.latest_block_header().tree_hash_root();
|
||||
envelope_verify!(
|
||||
envelope.beacon_block_root == latest_block_header_root,
|
||||
EnvelopeProcessingError::LatestBlockHeaderMismatch {
|
||||
envelope_root: envelope.beacon_block_root,
|
||||
block_header_root: latest_block_header_root,
|
||||
}
|
||||
);
|
||||
envelope_verify!(
|
||||
envelope.slot == state.slot(),
|
||||
EnvelopeProcessingError::SlotMismatch {
|
||||
envelope_slot: envelope.slot,
|
||||
parent_state_slot: state.slot(),
|
||||
}
|
||||
);
|
||||
|
||||
// Verify consistency with the committed bid
|
||||
let committed_bid = state.latest_execution_payload_bid()?;
|
||||
envelope_verify!(
|
||||
envelope.builder_index == committed_bid.builder_index,
|
||||
EnvelopeProcessingError::BuilderIndexMismatch {
|
||||
committed_bid: committed_bid.builder_index,
|
||||
envelope: envelope.builder_index,
|
||||
}
|
||||
);
|
||||
envelope_verify!(
|
||||
committed_bid.prev_randao == payload.prev_randao,
|
||||
EnvelopeProcessingError::PrevRandaoMismatch {
|
||||
committed_bid: committed_bid.prev_randao,
|
||||
envelope: payload.prev_randao,
|
||||
}
|
||||
);
|
||||
|
||||
// Verify consistency with expected withdrawals
|
||||
// NOTE: we don't bother hashing here except in case of error, because we can just compare for
|
||||
// equality directly. This equality check could be more straight-forward if the types were
|
||||
// changed to match (currently we are comparing VariableList to List). This could happen
|
||||
// coincidentally when we adopt ProgressiveList.
|
||||
envelope_verify!(
|
||||
payload.withdrawals.len() == state.payload_expected_withdrawals()?.len()
|
||||
&& payload
|
||||
.withdrawals
|
||||
.iter()
|
||||
.eq(state.payload_expected_withdrawals()?.iter()),
|
||||
EnvelopeProcessingError::WithdrawalsRootMismatch {
|
||||
state: state.payload_expected_withdrawals()?.tree_hash_root(),
|
||||
payload: payload.withdrawals.tree_hash_root(),
|
||||
}
|
||||
);
|
||||
|
||||
// Verify the gas limit
|
||||
envelope_verify!(
|
||||
committed_bid.gas_limit == payload.gas_limit,
|
||||
EnvelopeProcessingError::GasLimitMismatch {
|
||||
committed_bid: committed_bid.gas_limit,
|
||||
envelope: payload.gas_limit,
|
||||
}
|
||||
);
|
||||
|
||||
// Verify the block hash
|
||||
envelope_verify!(
|
||||
committed_bid.block_hash == payload.block_hash,
|
||||
EnvelopeProcessingError::BlockHashMismatch {
|
||||
committed_bid: committed_bid.block_hash,
|
||||
envelope: payload.block_hash,
|
||||
}
|
||||
);
|
||||
|
||||
// Verify consistency of the parent hash with respect to the previous execution payload
|
||||
envelope_verify!(
|
||||
payload.parent_hash == *state.latest_block_hash()?,
|
||||
EnvelopeProcessingError::ParentHashMismatch {
|
||||
state: *state.latest_block_hash()?,
|
||||
envelope: payload.parent_hash,
|
||||
}
|
||||
);
|
||||
|
||||
// Verify timestamp
|
||||
let state_timestamp = compute_timestamp_at_slot(state, state.slot(), spec)?;
|
||||
envelope_verify!(
|
||||
payload.timestamp == state_timestamp,
|
||||
EnvelopeProcessingError::TimestampMismatch {
|
||||
state: state_timestamp,
|
||||
envelope: payload.timestamp,
|
||||
}
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user