mirror of
https://github.com/sigp/lighthouse.git
synced 2026-03-02 16:21:42 +00:00
Whole Bunch of Changes Sorry
This commit is contained in:
@@ -139,6 +139,9 @@ pub struct BeaconForkChoiceStore<E: EthSpec, Hot: ItemStore<E>, Cold: ItemStore<
|
||||
unrealized_justified_checkpoint: Checkpoint,
|
||||
unrealized_finalized_checkpoint: Checkpoint,
|
||||
proposer_boost_root: Hash256,
|
||||
payload_withhold_boost_root: Hash256,
|
||||
payload_withhold_boost_full: bool,
|
||||
payload_reveal_boost_root: Hash256,
|
||||
equivocating_indices: BTreeSet<u64>,
|
||||
_phantom: PhantomData<E>,
|
||||
}
|
||||
@@ -188,6 +191,9 @@ where
|
||||
unrealized_justified_checkpoint: justified_checkpoint,
|
||||
unrealized_finalized_checkpoint: finalized_checkpoint,
|
||||
proposer_boost_root: Hash256::zero(),
|
||||
payload_withhold_boost_root: Hash256::zero(),
|
||||
payload_withhold_boost_full: false,
|
||||
payload_reveal_boost_root: Hash256::zero(),
|
||||
equivocating_indices: BTreeSet::new(),
|
||||
_phantom: PhantomData,
|
||||
})
|
||||
@@ -205,6 +211,9 @@ where
|
||||
unrealized_justified_checkpoint: self.unrealized_justified_checkpoint,
|
||||
unrealized_finalized_checkpoint: self.unrealized_finalized_checkpoint,
|
||||
proposer_boost_root: self.proposer_boost_root,
|
||||
payload_withhold_boost_root: self.payload_withhold_boost_root,
|
||||
payload_withhold_boost_full: self.payload_withhold_boost_full,
|
||||
payload_reveal_boost_root: self.payload_reveal_boost_root,
|
||||
equivocating_indices: self.equivocating_indices.clone(),
|
||||
}
|
||||
}
|
||||
@@ -226,6 +235,9 @@ where
|
||||
unrealized_justified_checkpoint: persisted.unrealized_justified_checkpoint,
|
||||
unrealized_finalized_checkpoint: persisted.unrealized_finalized_checkpoint,
|
||||
proposer_boost_root: persisted.proposer_boost_root,
|
||||
payload_withhold_boost_root: persisted.payload_withhold_boost_root,
|
||||
payload_withhold_boost_full: persisted.payload_withhold_boost_full,
|
||||
payload_reveal_boost_root: persisted.payload_reveal_boost_root,
|
||||
equivocating_indices: persisted.equivocating_indices,
|
||||
_phantom: PhantomData,
|
||||
})
|
||||
@@ -281,6 +293,18 @@ where
|
||||
self.proposer_boost_root
|
||||
}
|
||||
|
||||
fn payload_withhold_boost_root(&self) -> Hash256 {
|
||||
self.payload_withhold_boost_root
|
||||
}
|
||||
|
||||
fn payload_withhold_boost_full(&self) -> bool {
|
||||
self.payload_withhold_boost_full
|
||||
}
|
||||
|
||||
fn payload_reveal_boost_root(&self) -> Hash256 {
|
||||
self.payload_reveal_boost_root
|
||||
}
|
||||
|
||||
fn set_finalized_checkpoint(&mut self, checkpoint: Checkpoint) {
|
||||
self.finalized_checkpoint = checkpoint
|
||||
}
|
||||
@@ -337,6 +361,18 @@ where
|
||||
self.proposer_boost_root = proposer_boost_root;
|
||||
}
|
||||
|
||||
fn set_payload_withhold_boost_root(&mut self, payload_withhold_boost_root: Hash256) {
|
||||
self.payload_withhold_boost_root = payload_withhold_boost_root;
|
||||
}
|
||||
|
||||
fn set_payload_withhold_boost_full(&mut self, payload_withhold_boost_full: bool) {
|
||||
self.payload_withhold_boost_full = payload_withhold_boost_full;
|
||||
}
|
||||
|
||||
fn set_payload_reveal_boost_root(&mut self, payload_reveal_boost_root: Hash256) {
|
||||
self.payload_reveal_boost_root = payload_reveal_boost_root;
|
||||
}
|
||||
|
||||
fn equivocating_indices(&self) -> &BTreeSet<u64> {
|
||||
&self.equivocating_indices
|
||||
}
|
||||
@@ -359,5 +395,9 @@ pub struct PersistedForkChoiceStore {
|
||||
pub unrealized_justified_checkpoint: Checkpoint,
|
||||
pub unrealized_finalized_checkpoint: Checkpoint,
|
||||
pub proposer_boost_root: Hash256,
|
||||
// TODO(EIP7732): implement db migration
|
||||
pub payload_withhold_boost_root: Hash256,
|
||||
pub payload_withhold_boost_full: bool,
|
||||
pub payload_reveal_boost_root: Hash256,
|
||||
pub equivocating_indices: BTreeSet<u64>,
|
||||
}
|
||||
|
||||
@@ -684,7 +684,8 @@ pub struct SignatureVerifiedBlock<T: BeaconChainTypes> {
|
||||
}
|
||||
|
||||
/// Used to await the result of executing payload with an EE.
|
||||
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:
|
||||
@@ -1344,9 +1345,10 @@ impl<T: BeaconChainTypes> ExecutionPendingBlock<T> {
|
||||
//
|
||||
// We do this as early as possible so that later parts of this function can run in parallel
|
||||
// with the payload verification.
|
||||
let block_copy = block.block_cloned();
|
||||
let payload_notifier = PayloadNotifier::new(
|
||||
chain.clone(),
|
||||
block.block_cloned(),
|
||||
&block_copy,
|
||||
&parent.pre_state,
|
||||
notify_execution_layer,
|
||||
)?;
|
||||
@@ -1354,7 +1356,7 @@ impl<T: BeaconChainTypes> ExecutionPendingBlock<T> {
|
||||
is_merge_transition_block(&parent.pre_state, block.message().body());
|
||||
let payload_verification_future = async move {
|
||||
let chain = payload_notifier.chain.clone();
|
||||
let block = payload_notifier.block.clone();
|
||||
let block = block_copy;
|
||||
|
||||
// If this block triggers the merge, check to ensure that it references valid execution
|
||||
// blocks.
|
||||
|
||||
406
beacon_node/beacon_chain/src/envelope_verification.rs
Normal file
406
beacon_node/beacon_chain/src/envelope_verification.rs
Normal file
@@ -0,0 +1,406 @@
|
||||
//! 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
|
||||
//! |
|
||||
//! ▼
|
||||
//! SignedExecutionEnvelope
|
||||
//! |
|
||||
//! |---------------
|
||||
//! | |
|
||||
//! | ▼
|
||||
//! | GossipVerifiedEnvelope
|
||||
//! | |
|
||||
//! |---------------
|
||||
//! |
|
||||
//! ▼
|
||||
//! ExecutionPendingEnvelope
|
||||
//! |
|
||||
//! await
|
||||
//! |
|
||||
//! ▼
|
||||
//! END
|
||||
//!
|
||||
//! ```
|
||||
|
||||
use crate::block_verification::{PayloadVerificationHandle, PayloadVerificationOutcome};
|
||||
use crate::execution_payload::PayloadNotifier;
|
||||
use crate::NotifyExecutionLayer;
|
||||
use crate::{BeaconChain, BeaconChainError, BeaconChainTypes};
|
||||
use derivative::Derivative;
|
||||
use safe_arith::ArithError;
|
||||
use slot_clock::SlotClock;
|
||||
use state_processing::per_block_processing::compute_timestamp_at_slot;
|
||||
use std::sync::Arc;
|
||||
use tree_hash::TreeHash;
|
||||
use types::{
|
||||
BeaconState, BeaconStateError, EthSpec, ExecutionBlockHash, Hash256, SignedBlindedBeaconBlock,
|
||||
SignedExecutionEnvelope,
|
||||
};
|
||||
|
||||
// TODO(EIP7732): don't use this redefinition..
|
||||
macro_rules! block_verify {
|
||||
($condition: expr, $result: expr) => {
|
||||
if !$condition {
|
||||
return Err($result);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// TODO: finish this properly
|
||||
pub type MaybeAvailableEnvelope<E> = Arc<SignedExecutionEnvelope<E>>;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum EnvelopeError {
|
||||
/// The envelope's block root is unknown.
|
||||
BlockRootUnknown {
|
||||
block_root: Hash256,
|
||||
},
|
||||
/// The signature is invalid.
|
||||
BadSignature,
|
||||
/// Envelope doesn't match latest beacon block header
|
||||
LatestBlockHeaderMismatch {
|
||||
envelope_root: Hash256,
|
||||
block_header_root: Hash256,
|
||||
},
|
||||
/// The builder index doesn't match the committed bid
|
||||
BuilderIndexMismatch {
|
||||
committed_bid: u64,
|
||||
envelope: u64,
|
||||
},
|
||||
/// The blob KZG commitments root doesn't match the committed bid
|
||||
BlobKzgCommitmentsRootMismatch {
|
||||
committed_bid: Hash256,
|
||||
envelope: Hash256,
|
||||
},
|
||||
/// The withdrawals root doesn't match the state's latest withdrawals root
|
||||
WithdrawalsRootMismatch {
|
||||
state: Hash256,
|
||||
envelope: Hash256,
|
||||
},
|
||||
// The gas limit doesn't match the committed bid
|
||||
GasLimitMismatch {
|
||||
committed_bid: u64,
|
||||
envelope: u64,
|
||||
},
|
||||
// The block hash doesn't match the committed bid
|
||||
BlockHashMismatch {
|
||||
committed_bid: ExecutionBlockHash,
|
||||
envelope: ExecutionBlockHash,
|
||||
},
|
||||
// The parent hash doesn't match the previous execution payload
|
||||
ParentHashMismatch {
|
||||
state: ExecutionBlockHash,
|
||||
envelope: ExecutionBlockHash,
|
||||
},
|
||||
// The previous randao didn't match the payload
|
||||
PrevRandaoMismatch {
|
||||
state: Hash256,
|
||||
envelope: Hash256,
|
||||
},
|
||||
// The timestamp didn't match the payload
|
||||
TimestampMismatch {
|
||||
state: u64,
|
||||
envelope: u64,
|
||||
},
|
||||
// Blob committments exceeded the maximum
|
||||
BlobLimitExceeded {
|
||||
max: usize,
|
||||
envelope: usize,
|
||||
},
|
||||
// The payload was withheld but the block hash
|
||||
// matched the committed bid
|
||||
PayloadWithheldBlockHashMismatch,
|
||||
// Some Beacon Chain Error
|
||||
BeaconChainError(BeaconChainError),
|
||||
// Some Beacon State error
|
||||
BeaconStateError(BeaconStateError),
|
||||
// Some ArithError
|
||||
ArithError(ArithError),
|
||||
}
|
||||
|
||||
impl From<BeaconChainError> for EnvelopeError {
|
||||
fn from(e: BeaconChainError) -> Self {
|
||||
EnvelopeError::BeaconChainError(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<BeaconStateError> for EnvelopeError {
|
||||
fn from(e: BeaconStateError) -> Self {
|
||||
EnvelopeError::BeaconStateError(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ArithError> for EnvelopeError {
|
||||
fn from(e: ArithError) -> Self {
|
||||
EnvelopeError::ArithError(e)
|
||||
}
|
||||
}
|
||||
|
||||
/// A wrapper around a `SignedBeaconBlock` that indicates it has been approved for re-gossiping on
|
||||
/// the p2p network.
|
||||
#[derive(Derivative)]
|
||||
#[derivative(Debug(bound = "T: BeaconChainTypes"))]
|
||||
pub struct GossipVerifiedEnvelope<T: BeaconChainTypes> {
|
||||
pub signed_envelope: Arc<SignedExecutionEnvelope<T::EthSpec>>,
|
||||
pub signed_block: Arc<SignedBlindedBeaconBlock<T::EthSpec>>,
|
||||
pub pre_state: Box<BeaconState<T::EthSpec>>,
|
||||
/*
|
||||
parent: Option<PreProcessingSnapshot<T::EthSpec>>,
|
||||
consensus_context: ConsensusContext<T::EthSpec>,
|
||||
*/
|
||||
}
|
||||
|
||||
impl<T: BeaconChainTypes> GossipVerifiedEnvelope<T> {
|
||||
pub fn new(
|
||||
signed_envelope: Arc<SignedExecutionEnvelope<T::EthSpec>>,
|
||||
chain: &BeaconChain<T>,
|
||||
) -> Result<Self, EnvelopeError> {
|
||||
let envelope = signed_envelope.message();
|
||||
let payload = envelope.payload();
|
||||
let block_root = envelope.beacon_block_root();
|
||||
|
||||
// TODO(EIP7732): this check would fail if the block didn't pass validation right?
|
||||
|
||||
// check that we've seen the parent block of this envelope
|
||||
let fork_choice_read_lock = chain.canonical_head.fork_choice_read_lock();
|
||||
if !fork_choice_read_lock.contains_block(&block_root) {
|
||||
return Err(EnvelopeError::BlockRootUnknown { block_root });
|
||||
}
|
||||
drop(fork_choice_read_lock);
|
||||
|
||||
let signed_block = chain
|
||||
.get_blinded_block(&block_root)?
|
||||
.ok_or_else(|| EnvelopeError::from(BeaconChainError::MissingBeaconBlock(block_root)))
|
||||
.map(Arc::new)?;
|
||||
let execution_bid = &signed_block
|
||||
.message()
|
||||
.body()
|
||||
.signed_execution_bid()?
|
||||
.message;
|
||||
|
||||
// TODO(EIP7732): check we're within the bounds of the slot (probably)
|
||||
|
||||
// TODO(EIP7732): check that we haven't seen another valid `SignedExecutionPayloadEnvelope`
|
||||
// for this block root from this builder
|
||||
|
||||
// builder index matches committed bid
|
||||
if envelope.builder_index() != execution_bid.builder_index {
|
||||
return Err(EnvelopeError::BuilderIndexMismatch {
|
||||
committed_bid: execution_bid.builder_index,
|
||||
envelope: envelope.builder_index(),
|
||||
});
|
||||
}
|
||||
|
||||
// if payload is withheld, the block hash should not match the committed bid
|
||||
if !envelope.payload_withheld() && payload.block_hash() == execution_bid.block_hash {
|
||||
return Err(EnvelopeError::PayloadWithheldBlockHashMismatch);
|
||||
}
|
||||
|
||||
let parent_state = chain
|
||||
.get_state(
|
||||
&signed_block.message().state_root(),
|
||||
Some(signed_block.slot()),
|
||||
)?
|
||||
.ok_or_else(|| {
|
||||
EnvelopeError::from(BeaconChainError::MissingBeaconState(
|
||||
signed_block.message().state_root(),
|
||||
))
|
||||
})?;
|
||||
|
||||
// verify the signature
|
||||
if signed_envelope.verify_signature(
|
||||
&parent_state,
|
||||
chain.genesis_validators_root,
|
||||
&chain.spec,
|
||||
)? {
|
||||
return Err(EnvelopeError::BadSignature);
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
signed_envelope,
|
||||
signed_block,
|
||||
pre_state: Box::new(parent_state),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn envelope_cloned(&self) -> Arc<SignedExecutionEnvelope<T::EthSpec>> {
|
||||
self.signed_envelope.clone()
|
||||
}
|
||||
}
|
||||
|
||||
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 signed_block: Arc<SignedBlindedBeaconBlock<T::EthSpec>>,
|
||||
pub pre_state: Box<BeaconState<T::EthSpec>>,
|
||||
pub payload_verification_handle: PayloadVerificationHandle,
|
||||
}
|
||||
|
||||
impl<T: BeaconChainTypes> IntoExecutionPendingEnvelope<T> for GossipVerifiedEnvelope<T> {
|
||||
fn into_execution_pending_envelope(
|
||||
self,
|
||||
chain: &Arc<BeaconChain<T>>,
|
||||
notify_execution_layer: NotifyExecutionLayer,
|
||||
) -> Result<ExecutionPendingEnvelope<T>, EnvelopeError> {
|
||||
let signed_envelope = self.signed_envelope;
|
||||
let envelope = signed_envelope.message();
|
||||
let payload = &envelope.payload();
|
||||
|
||||
// verify signature done
|
||||
|
||||
let mut state = *self.pre_state;
|
||||
let previous_state_root = state.canonical_root()?;
|
||||
if state.latest_block_header().state_root == Hash256::default() {
|
||||
state.latest_block_header_mut().state_root = previous_state_root;
|
||||
}
|
||||
|
||||
// Verify consistency with the beacon block
|
||||
if !envelope.tree_hash_root() == state.latest_block_header().tree_hash_root() {
|
||||
return Err(EnvelopeError::LatestBlockHeaderMismatch {
|
||||
envelope_root: envelope.tree_hash_root(),
|
||||
block_header_root: state.latest_block_header().tree_hash_root(),
|
||||
});
|
||||
};
|
||||
|
||||
// Verify consistency with the committed bid
|
||||
let committed_bid = state.latest_execution_bid()?;
|
||||
// builder index match already verified
|
||||
if committed_bid.blob_kzg_commitments_root
|
||||
!= envelope.blob_kzg_commitments().tree_hash_root()
|
||||
{
|
||||
return Err(EnvelopeError::BlobKzgCommitmentsRootMismatch {
|
||||
committed_bid: committed_bid.blob_kzg_commitments_root,
|
||||
envelope: envelope.blob_kzg_commitments().tree_hash_root(),
|
||||
});
|
||||
};
|
||||
|
||||
if !envelope.payload_withheld() {
|
||||
// Verify the withdrawals root
|
||||
block_verify!(
|
||||
payload.withdrawals()?.tree_hash_root() == state.latest_withdrawals_root()?,
|
||||
EnvelopeError::WithdrawalsRootMismatch {
|
||||
state: state.latest_withdrawals_root()?,
|
||||
envelope: payload.withdrawals()?.tree_hash_root(),
|
||||
}
|
||||
.into()
|
||||
);
|
||||
|
||||
// Verify the gas limit
|
||||
block_verify!(
|
||||
payload.gas_limit() == committed_bid.gas_limit,
|
||||
EnvelopeError::GasLimitMismatch {
|
||||
committed_bid: committed_bid.gas_limit,
|
||||
envelope: payload.gas_limit(),
|
||||
}
|
||||
.into()
|
||||
);
|
||||
// Verify the block hash
|
||||
block_verify!(
|
||||
committed_bid.block_hash == payload.block_hash(),
|
||||
EnvelopeError::BlockHashMismatch {
|
||||
committed_bid: committed_bid.block_hash,
|
||||
envelope: payload.block_hash(),
|
||||
}
|
||||
.into()
|
||||
);
|
||||
|
||||
// Verify consistency of the parent hash with respect to the previous execution payload
|
||||
block_verify!(
|
||||
payload.parent_hash() == state.latest_block_hash()?,
|
||||
EnvelopeError::ParentHashMismatch {
|
||||
state: state.latest_block_hash()?,
|
||||
envelope: payload.parent_hash(),
|
||||
}
|
||||
.into()
|
||||
);
|
||||
|
||||
// Verify prev_randao
|
||||
block_verify!(
|
||||
payload.prev_randao() == *state.get_randao_mix(state.current_epoch())?,
|
||||
EnvelopeError::PrevRandaoMismatch {
|
||||
state: *state.get_randao_mix(state.current_epoch())?,
|
||||
envelope: payload.prev_randao(),
|
||||
}
|
||||
.into()
|
||||
);
|
||||
|
||||
// Verify the timestamp
|
||||
let state_timestamp =
|
||||
compute_timestamp_at_slot(&state, state.slot(), chain.spec.as_ref())?;
|
||||
block_verify!(
|
||||
payload.timestamp() == state_timestamp,
|
||||
EnvelopeError::TimestampMismatch {
|
||||
state: state_timestamp,
|
||||
envelope: payload.timestamp(),
|
||||
}
|
||||
.into()
|
||||
);
|
||||
|
||||
// Verify the commitments are under limit
|
||||
block_verify!(
|
||||
envelope.blob_kzg_commitments().len()
|
||||
<= T::EthSpec::max_blob_commitments_per_block(),
|
||||
EnvelopeError::BlobLimitExceeded {
|
||||
max: T::EthSpec::max_blob_commitments_per_block(),
|
||||
envelope: envelope.blob_kzg_commitments().len(),
|
||||
}
|
||||
.into()
|
||||
);
|
||||
}
|
||||
|
||||
// Verify the execution payload is valid
|
||||
let payload_notifier =
|
||||
PayloadNotifier::from_envelope(chain.clone(), envelope, notify_execution_layer)?;
|
||||
let block_root = envelope.beacon_block_root();
|
||||
let slot = self.signed_block.slot();
|
||||
|
||||
let payload_verification_future = async move {
|
||||
let chain = payload_notifier.chain.clone();
|
||||
// TODO:(EIP7732): timing
|
||||
if let Some(started_execution) = chain.slot_clock.now_duration() {
|
||||
chain.block_times_cache.write().set_time_started_execution(
|
||||
block_root,
|
||||
slot,
|
||||
started_execution,
|
||||
);
|
||||
}
|
||||
|
||||
let payload_verification_status = payload_notifier.notify_new_payload().await?;
|
||||
Ok(PayloadVerificationOutcome {
|
||||
payload_verification_status,
|
||||
// This fork is after the merge so it'll never be the merge transition block
|
||||
is_valid_merge_transition_block: false,
|
||||
})
|
||||
};
|
||||
// Spawn the payload verification future as a new task, but don't wait for it to complete.
|
||||
// The `payload_verification_future` will be awaited later to ensure verification completed
|
||||
// successfully.
|
||||
let payload_verification_handle = chain
|
||||
.task_executor
|
||||
.spawn_handle(
|
||||
payload_verification_future,
|
||||
"execution_payload_verification",
|
||||
)
|
||||
.ok_or(BeaconChainError::RuntimeShutdown)?;
|
||||
|
||||
// TODO(EIP7732): process electra operations
|
||||
|
||||
Ok(ExecutionPendingEnvelope {
|
||||
signed_envelope,
|
||||
pre_state: Box::new(state),
|
||||
signed_block: self.signed_block,
|
||||
payload_verification_handle,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,7 @@
|
||||
use crate::otb_verification_service::OptimisticTransitionBlock;
|
||||
use crate::{
|
||||
BeaconChain, BeaconChainError, BeaconChainTypes, BlockError, BlockProductionError,
|
||||
ExecutionPayloadError,
|
||||
EnvelopeError, ExecutionPayloadError,
|
||||
};
|
||||
use execution_layer::{
|
||||
BlockProposalContents, BlockProposalContentsType, BuilderParams, NewPayloadRequest,
|
||||
@@ -26,7 +26,6 @@ use state_processing::per_block_processing::{
|
||||
};
|
||||
use std::sync::Arc;
|
||||
use tokio::task::JoinHandle;
|
||||
use tree_hash::TreeHash;
|
||||
use types::payload::BlockProductionVersion;
|
||||
use types::*;
|
||||
|
||||
@@ -52,18 +51,24 @@ pub enum NotifyExecutionLayer {
|
||||
/// Used to await the result of executing payload with a remote EE.
|
||||
pub struct PayloadNotifier<T: BeaconChainTypes> {
|
||||
pub chain: Arc<BeaconChain<T>>,
|
||||
pub block: Arc<SignedBeaconBlock<T::EthSpec>>,
|
||||
payload_verification_status: Option<PayloadVerificationStatus>,
|
||||
pub parent_root: Hash256,
|
||||
pub payload_verification_state: PayloadVerificationState<T::EthSpec>,
|
||||
}
|
||||
|
||||
impl<T: BeaconChainTypes> PayloadNotifier<T> {
|
||||
pub enum PayloadVerificationState<E: EthSpec> {
|
||||
PreComputed(PayloadVerificationStatus),
|
||||
Request(NewPayloadRequest<E>),
|
||||
}
|
||||
|
||||
impl<'block, T: BeaconChainTypes> PayloadNotifier<T> {
|
||||
pub fn new(
|
||||
chain: Arc<BeaconChain<T>>,
|
||||
block: Arc<SignedBeaconBlock<T::EthSpec>>,
|
||||
block: &SignedBeaconBlock<T::EthSpec>,
|
||||
state: &BeaconState<T::EthSpec>,
|
||||
notify_execution_layer: NotifyExecutionLayer,
|
||||
) -> Result<Self, BlockError> {
|
||||
let payload_verification_status = if is_execution_enabled(state, block.message().body()) {
|
||||
let parent_root = block.parent_root();
|
||||
let payload_verification_state = if is_execution_enabled(state, block.message().body()) {
|
||||
// Perform the initial stages of payload verification.
|
||||
//
|
||||
// We will duplicate these checks again during `per_block_processing`, however these
|
||||
@@ -78,12 +83,11 @@ impl<T: BeaconChainTypes> PayloadNotifier<T> {
|
||||
&chain.spec,
|
||||
)
|
||||
.map_err(BlockError::PerBlockProcessingError)?;
|
||||
let new_payload_request: NewPayloadRequest<T::EthSpec> = block_message.try_into()?;
|
||||
|
||||
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> =
|
||||
block_message.try_into()?;
|
||||
// check optimistic sync verifications
|
||||
if let Err(e) = new_payload_request.perform_optimistic_sync_verifications() {
|
||||
warn!(
|
||||
chain.log,
|
||||
@@ -92,29 +96,68 @@ impl<T: BeaconChainTypes> PayloadNotifier<T> {
|
||||
"info" => "you can silence this warning with --disable-optimistic-finalized-sync",
|
||||
"error" => ?e,
|
||||
);
|
||||
None
|
||||
PayloadVerificationState::Request(new_payload_request)
|
||||
} else {
|
||||
Some(PayloadVerificationStatus::Optimistic)
|
||||
PayloadVerificationState::PreComputed(PayloadVerificationStatus::Optimistic)
|
||||
}
|
||||
}
|
||||
_ => None,
|
||||
_ => PayloadVerificationState::Request(new_payload_request),
|
||||
}
|
||||
} else {
|
||||
Some(PayloadVerificationStatus::Irrelevant)
|
||||
PayloadVerificationState::PreComputed(PayloadVerificationStatus::Irrelevant)
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
chain,
|
||||
block,
|
||||
payload_verification_status,
|
||||
parent_root,
|
||||
payload_verification_state,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn from_envelope(
|
||||
chain: Arc<BeaconChain<T>>,
|
||||
envelope: ExecutionEnvelopeRef<T::EthSpec>,
|
||||
notify_execution_layer: NotifyExecutionLayer,
|
||||
) -> Result<Self, EnvelopeError> {
|
||||
let parent_root = envelope.beacon_block_root();
|
||||
let new_payload_request: NewPayloadRequest<T::EthSpec> = envelope.try_into()?;
|
||||
|
||||
let payload_verification_state = if !envelope.payload_withheld() {
|
||||
match notify_execution_layer {
|
||||
NotifyExecutionLayer::No if chain.config.optimistic_finalized_sync => {
|
||||
// check optimistic sync verifications
|
||||
if let Err(e) = new_payload_request.perform_optimistic_sync_verifications() {
|
||||
warn!(
|
||||
chain.log,
|
||||
"Falling back to slow block hash verification";
|
||||
"block_number" => envelope.payload().block_number(),
|
||||
"info" => "you can silence this warning with --disable-optimistic-finalized-sync",
|
||||
"error" => ?e,
|
||||
);
|
||||
PayloadVerificationState::Request(new_payload_request)
|
||||
} else {
|
||||
PayloadVerificationState::PreComputed(PayloadVerificationStatus::Optimistic)
|
||||
}
|
||||
}
|
||||
_ => PayloadVerificationState::Request(new_payload_request),
|
||||
}
|
||||
} else {
|
||||
PayloadVerificationState::PreComputed(PayloadVerificationStatus::Irrelevant)
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
chain,
|
||||
parent_root,
|
||||
payload_verification_state,
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn notify_new_payload(self) -> Result<PayloadVerificationStatus, BlockError> {
|
||||
if let Some(precomputed_status) = self.payload_verification_status {
|
||||
Ok(precomputed_status)
|
||||
} else {
|
||||
notify_new_payload(&self.chain, self.block.message()).await
|
||||
match self.payload_verification_state {
|
||||
PayloadVerificationState::Request(request) => {
|
||||
notify_new_payload(&self.chain, request, self.parent_root).await
|
||||
}
|
||||
PayloadVerificationState::PreComputed(status) => Ok(status),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -128,17 +171,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<'a, T: BeaconChainTypes>(
|
||||
async fn notify_new_payload<T: BeaconChainTypes>(
|
||||
chain: &Arc<BeaconChain<T>>,
|
||||
block: BeaconBlockRef<'a, T::EthSpec>,
|
||||
request: NewPayloadRequest<T::EthSpec>,
|
||||
parent_root: Hash256,
|
||||
) -> 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 = request.block_hash();
|
||||
let new_payload_response = execution_layer.notify_new_payload(request).await;
|
||||
|
||||
match new_payload_response {
|
||||
Ok(status) => match status {
|
||||
@@ -156,10 +200,13 @@ async fn notify_new_payload<'a, T: BeaconChainTypes>(
|
||||
"validation_error" => ?validation_error,
|
||||
"latest_valid_hash" => ?latest_valid_hash,
|
||||
"execution_block_hash" => ?execution_block_hash,
|
||||
/*
|
||||
// EIP-7732 - none of this stuff is available in the envelope.. is it worth it?
|
||||
"root" => ?block.tree_hash_root(),
|
||||
"graffiti" => block.body().graffiti().as_utf8_lossy(),
|
||||
"proposer_index" => block.proposer_index(),
|
||||
"slot" => block.slot(),
|
||||
*/
|
||||
"method" => "new_payload",
|
||||
);
|
||||
|
||||
@@ -181,8 +228,7 @@ async fn notify_new_payload<'a, 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 = parent_root;
|
||||
chain
|
||||
.process_invalid_execution_payload(&InvalidationOperation::InvalidateMany {
|
||||
head_block_root: latest_root,
|
||||
@@ -202,10 +248,13 @@ async fn notify_new_payload<'a, T: BeaconChainTypes>(
|
||||
"Invalid execution payload block hash";
|
||||
"validation_error" => ?validation_error,
|
||||
"execution_block_hash" => ?execution_block_hash,
|
||||
/*
|
||||
// Again this stuff isn't available in the envelope
|
||||
"root" => ?block.tree_hash_root(),
|
||||
"graffiti" => block.body().graffiti().as_utf8_lossy(),
|
||||
"proposer_index" => block.proposer_index(),
|
||||
"slot" => block.slot(),
|
||||
*/
|
||||
"method" => "new_payload",
|
||||
);
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ pub mod data_column_verification;
|
||||
pub mod deneb_readiness;
|
||||
mod early_attester_cache;
|
||||
pub mod electra_readiness;
|
||||
pub mod envelope_verification;
|
||||
mod errors;
|
||||
pub mod eth1_chain;
|
||||
mod eth1_finalization_cache;
|
||||
@@ -83,6 +84,7 @@ pub use block_verification::{
|
||||
pub use block_verification_types::AvailabilityPendingExecutedBlock;
|
||||
pub use block_verification_types::ExecutedBlock;
|
||||
pub use canonical_head::{CachedHead, CanonicalHead, CanonicalHeadRwLock};
|
||||
pub use envelope_verification::{EnvelopeError, ExecutionPendingEnvelope, GossipVerifiedEnvelope};
|
||||
pub use eth1_chain::{Eth1Chain, Eth1ChainBackend};
|
||||
pub use events::ServerSentEventHandler;
|
||||
pub use execution_layer::EngineState;
|
||||
|
||||
@@ -784,7 +784,7 @@ impl HttpJsonRpc {
|
||||
|
||||
pub async fn new_payload_v3_deneb<E: EthSpec>(
|
||||
&self,
|
||||
new_payload_request_deneb: NewPayloadRequestDeneb<'_, E>,
|
||||
new_payload_request_deneb: NewPayloadRequestDeneb<E>,
|
||||
) -> Result<PayloadStatusV1, Error> {
|
||||
let params = json!([
|
||||
JsonExecutionPayload::V3(new_payload_request_deneb.execution_payload.clone().into()),
|
||||
@@ -805,7 +805,7 @@ impl HttpJsonRpc {
|
||||
|
||||
pub async fn new_payload_v4_electra<E: EthSpec>(
|
||||
&self,
|
||||
new_payload_request_electra: NewPayloadRequestElectra<'_, E>,
|
||||
new_payload_request_electra: NewPayloadRequestElectra<E>,
|
||||
) -> Result<PayloadStatusV1, Error> {
|
||||
let params = json!([
|
||||
JsonExecutionPayload::V4(new_payload_request_electra.execution_payload.clone().into()),
|
||||
@@ -1191,7 +1191,7 @@ impl HttpJsonRpc {
|
||||
// new_payload that the execution engine supports
|
||||
pub async fn new_payload<E: EthSpec>(
|
||||
&self,
|
||||
new_payload_request: NewPayloadRequest<'_, E>,
|
||||
new_payload_request: NewPayloadRequest<E>,
|
||||
) -> Result<PayloadStatusV1, Error> {
|
||||
let engine_capabilities = self.get_engine_capabilities(None).await?;
|
||||
match new_payload_request {
|
||||
@@ -1221,6 +1221,7 @@ impl HttpJsonRpc {
|
||||
Err(Error::RequiredMethodUnsupported("engine_newPayloadV4"))
|
||||
}
|
||||
}
|
||||
NewPayloadRequest::EIP7732(_) => todo!("EIP-7732 Engine API new_payload"),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,8 +4,8 @@ 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, ExecutionEnvelopeRef,
|
||||
ExecutionPayload, ExecutionPayloadEIP7732, ExecutionPayloadRef, Hash256, VersionedHash,
|
||||
};
|
||||
use types::{
|
||||
ExecutionPayloadBellatrix, ExecutionPayloadCapella, ExecutionPayloadDeneb,
|
||||
@@ -13,7 +13,7 @@ use types::{
|
||||
};
|
||||
|
||||
#[superstruct(
|
||||
variants(Bellatrix, Capella, Deneb, Electra),
|
||||
variants(Bellatrix, Capella, Deneb, Electra, EIP7732),
|
||||
variant_attributes(derive(Clone, Debug, PartialEq),),
|
||||
map_into(ExecutionPayload),
|
||||
map_ref_into(ExecutionPayloadRef),
|
||||
@@ -27,33 +27,36 @@ use types::{
|
||||
)
|
||||
)]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct NewPayloadRequest<'block, E: EthSpec> {
|
||||
pub struct NewPayloadRequest<E: EthSpec> {
|
||||
#[superstruct(
|
||||
only(Bellatrix),
|
||||
partial_getter(rename = "execution_payload_bellatrix")
|
||||
)]
|
||||
pub execution_payload: &'block ExecutionPayloadBellatrix<E>,
|
||||
pub execution_payload: ExecutionPayloadBellatrix<E>,
|
||||
#[superstruct(only(Capella), partial_getter(rename = "execution_payload_capella"))]
|
||||
pub execution_payload: &'block ExecutionPayloadCapella<E>,
|
||||
pub execution_payload: ExecutionPayloadCapella<E>,
|
||||
#[superstruct(only(Deneb), partial_getter(rename = "execution_payload_deneb"))]
|
||||
pub execution_payload: &'block ExecutionPayloadDeneb<E>,
|
||||
pub execution_payload: ExecutionPayloadDeneb<E>,
|
||||
#[superstruct(only(Electra), partial_getter(rename = "execution_payload_electra"))]
|
||||
pub execution_payload: &'block ExecutionPayloadElectra<E>,
|
||||
#[superstruct(only(Deneb, Electra))]
|
||||
pub execution_payload: ExecutionPayloadElectra<E>,
|
||||
#[superstruct(only(EIP7732), partial_getter(rename = "execution_payload_eip7732"))]
|
||||
pub execution_payload: ExecutionPayloadEIP7732<E>,
|
||||
#[superstruct(only(Deneb, Electra, EIP7732))]
|
||||
pub versioned_hashes: Vec<VersionedHash>,
|
||||
#[superstruct(only(Deneb, Electra))]
|
||||
#[superstruct(only(Deneb, Electra, EIP7732))]
|
||||
pub parent_beacon_block_root: Hash256,
|
||||
#[superstruct(only(Electra))]
|
||||
pub execution_requests_list: &'block ExecutionRequests<E>,
|
||||
#[superstruct(only(Electra, EIP7732))]
|
||||
pub execution_requests_list: ExecutionRequests<E>,
|
||||
}
|
||||
|
||||
impl<'block, E: EthSpec> NewPayloadRequest<'block, E> {
|
||||
impl<E: EthSpec> NewPayloadRequest<E> {
|
||||
pub fn parent_hash(&self) -> ExecutionBlockHash {
|
||||
match self {
|
||||
Self::Bellatrix(payload) => payload.execution_payload.parent_hash,
|
||||
Self::Capella(payload) => payload.execution_payload.parent_hash,
|
||||
Self::Deneb(payload) => payload.execution_payload.parent_hash,
|
||||
Self::Electra(payload) => payload.execution_payload.parent_hash,
|
||||
Self::EIP7732(payload) => payload.execution_payload.parent_hash,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,6 +66,7 @@ impl<'block, E: EthSpec> NewPayloadRequest<'block, E> {
|
||||
Self::Capella(payload) => payload.execution_payload.block_hash,
|
||||
Self::Deneb(payload) => payload.execution_payload.block_hash,
|
||||
Self::Electra(payload) => payload.execution_payload.block_hash,
|
||||
Self::EIP7732(payload) => payload.execution_payload.block_hash,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,15 +76,17 @@ impl<'block, E: EthSpec> NewPayloadRequest<'block, E> {
|
||||
Self::Capella(payload) => payload.execution_payload.block_number,
|
||||
Self::Deneb(payload) => payload.execution_payload.block_number,
|
||||
Self::Electra(payload) => payload.execution_payload.block_number,
|
||||
Self::EIP7732(payload) => payload.execution_payload.block_number,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn execution_payload_ref(&self) -> ExecutionPayloadRef<'block, E> {
|
||||
pub fn execution_payload_ref(&self) -> ExecutionPayloadRef<E> {
|
||||
match self {
|
||||
Self::Bellatrix(request) => ExecutionPayloadRef::Bellatrix(request.execution_payload),
|
||||
Self::Capella(request) => ExecutionPayloadRef::Capella(request.execution_payload),
|
||||
Self::Deneb(request) => ExecutionPayloadRef::Deneb(request.execution_payload),
|
||||
Self::Electra(request) => ExecutionPayloadRef::Electra(request.execution_payload),
|
||||
Self::Bellatrix(request) => ExecutionPayloadRef::Bellatrix(&request.execution_payload),
|
||||
Self::Capella(request) => ExecutionPayloadRef::Capella(&request.execution_payload),
|
||||
Self::Deneb(request) => ExecutionPayloadRef::Deneb(&request.execution_payload),
|
||||
Self::Electra(request) => ExecutionPayloadRef::Electra(&request.execution_payload),
|
||||
Self::EIP7732(request) => ExecutionPayloadRef::EIP7732(&request.execution_payload),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,9 +95,10 @@ impl<'block, E: EthSpec> NewPayloadRequest<'block, E> {
|
||||
Self::Bellatrix(request) => {
|
||||
ExecutionPayload::Bellatrix(request.execution_payload.clone())
|
||||
}
|
||||
Self::Capella(request) => ExecutionPayload::Capella(request.execution_payload.clone()),
|
||||
Self::Deneb(request) => ExecutionPayload::Deneb(request.execution_payload.clone()),
|
||||
Self::Electra(request) => ExecutionPayload::Electra(request.execution_payload.clone()),
|
||||
Self::Capella(request) => ExecutionPayload::Capella(request.execution_payload),
|
||||
Self::Deneb(request) => ExecutionPayload::Deneb(request.execution_payload),
|
||||
Self::Electra(request) => ExecutionPayload::Electra(request.execution_payload),
|
||||
Self::EIP7732(request) => ExecutionPayload::EIP7732(request.execution_payload),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,7 +157,8 @@ impl<'block, E: EthSpec> NewPayloadRequest<'block, E> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, E: EthSpec> TryFrom<BeaconBlockRef<'a, E>> for NewPayloadRequest<'a, E> {
|
||||
//TODO(EIP7732): Consider implmenting these as methods on the NewPayloadRequest struct
|
||||
impl<'a, E: EthSpec> TryFrom<BeaconBlockRef<'a, E>> for NewPayloadRequest<E> {
|
||||
type Error = BeaconStateError;
|
||||
|
||||
fn try_from(block: BeaconBlockRef<'a, E>) -> Result<Self, Self::Error> {
|
||||
@@ -160,14 +168,14 @@ impl<'a, E: EthSpec> TryFrom<BeaconBlockRef<'a, E>> for NewPayloadRequest<'a, E>
|
||||
}
|
||||
BeaconBlockRef::Bellatrix(block_ref) => {
|
||||
Ok(Self::Bellatrix(NewPayloadRequestBellatrix {
|
||||
execution_payload: &block_ref.body.execution_payload.execution_payload,
|
||||
execution_payload: block_ref.body.execution_payload.execution_payload.clone(),
|
||||
}))
|
||||
}
|
||||
BeaconBlockRef::Capella(block_ref) => Ok(Self::Capella(NewPayloadRequestCapella {
|
||||
execution_payload: &block_ref.body.execution_payload.execution_payload,
|
||||
execution_payload: block_ref.body.execution_payload.execution_payload.clone(),
|
||||
})),
|
||||
BeaconBlockRef::Deneb(block_ref) => Ok(Self::Deneb(NewPayloadRequestDeneb {
|
||||
execution_payload: &block_ref.body.execution_payload.execution_payload,
|
||||
execution_payload: block_ref.body.execution_payload.execution_payload.clone(),
|
||||
versioned_hashes: block_ref
|
||||
.body
|
||||
.blob_kzg_commitments
|
||||
@@ -177,7 +185,7 @@ impl<'a, E: EthSpec> TryFrom<BeaconBlockRef<'a, E>> for NewPayloadRequest<'a, E>
|
||||
parent_beacon_block_root: block_ref.parent_root,
|
||||
})),
|
||||
BeaconBlockRef::Electra(block_ref) => Ok(Self::Electra(NewPayloadRequestElectra {
|
||||
execution_payload: &block_ref.body.execution_payload.execution_payload,
|
||||
execution_payload: block_ref.body.execution_payload.execution_payload.clone(),
|
||||
versioned_hashes: block_ref
|
||||
.body
|
||||
.blob_kzg_commitments
|
||||
@@ -185,26 +193,47 @@ impl<'a, E: EthSpec> TryFrom<BeaconBlockRef<'a, E>> for NewPayloadRequest<'a, E>
|
||||
.map(kzg_commitment_to_versioned_hash)
|
||||
.collect(),
|
||||
parent_beacon_block_root: block_ref.parent_root,
|
||||
execution_requests_list: &block_ref.body.execution_requests,
|
||||
execution_requests_list: block_ref.body.execution_requests.clone(),
|
||||
})),
|
||||
//TODO(EIP7732): Need new method of constructing NewPayloadRequest
|
||||
BeaconBlockRef::EIP7732(_) => Err(Self::Error::IncorrectStateVariant),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, E: EthSpec> TryFrom<ExecutionPayloadRef<'a, E>> for NewPayloadRequest<'a, E> {
|
||||
impl<'a, E: EthSpec> TryFrom<ExecutionEnvelopeRef<'a, E>> for NewPayloadRequest<E> {
|
||||
type Error = BeaconStateError;
|
||||
|
||||
fn try_from(envelope: ExecutionEnvelopeRef<'a, E>) -> Result<Self, Self::Error> {
|
||||
match envelope {
|
||||
ExecutionEnvelopeRef::EIP7732(envelope) => {
|
||||
Ok(Self::EIP7732(NewPayloadRequestEIP7732 {
|
||||
execution_payload: envelope.payload.clone(),
|
||||
versioned_hashes: envelope
|
||||
.blob_kzg_commitments
|
||||
.iter()
|
||||
.map(kzg_commitment_to_versioned_hash)
|
||||
.collect(),
|
||||
parent_beacon_block_root: envelope.beacon_block_root,
|
||||
execution_requests_list: envelope.execution_requests.clone(),
|
||||
}))
|
||||
}
|
||||
ExecutionEnvelopeRef::NextFork(_) => Err(Self::Error::IncorrectStateVariant),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, E: EthSpec> TryFrom<ExecutionPayloadRef<'a, E>> for NewPayloadRequest<E> {
|
||||
type Error = BeaconStateError;
|
||||
|
||||
fn try_from(payload: ExecutionPayloadRef<'a, E>) -> Result<Self, Self::Error> {
|
||||
match payload {
|
||||
ExecutionPayloadRef::Bellatrix(payload) => {
|
||||
Ok(Self::Bellatrix(NewPayloadRequestBellatrix {
|
||||
execution_payload: payload,
|
||||
execution_payload: payload.clone(),
|
||||
}))
|
||||
}
|
||||
ExecutionPayloadRef::Capella(payload) => Ok(Self::Capella(NewPayloadRequestCapella {
|
||||
execution_payload: payload,
|
||||
execution_payload: payload.clone(),
|
||||
})),
|
||||
ExecutionPayloadRef::Deneb(_) => Err(Self::Error::IncorrectStateVariant),
|
||||
ExecutionPayloadRef::Electra(_) => Err(Self::Error::IncorrectStateVariant),
|
||||
|
||||
@@ -1358,7 +1358,7 @@ impl<E: EthSpec> ExecutionLayer<E> {
|
||||
/// Maps to the `engine_newPayload` JSON-RPC call.
|
||||
pub async fn notify_new_payload(
|
||||
&self,
|
||||
new_payload_request: NewPayloadRequest<'_, E>,
|
||||
new_payload_request: NewPayloadRequest<E>,
|
||||
) -> Result<PayloadStatus, Error> {
|
||||
let _timer = metrics::start_timer_vec(
|
||||
&metrics::EXECUTION_LAYER_REQUEST_TIMES,
|
||||
|
||||
@@ -59,6 +59,15 @@ pub trait ForkChoiceStore<E: EthSpec>: Sized {
|
||||
/// Returns the `proposer_boost_root`.
|
||||
fn proposer_boost_root(&self) -> Hash256;
|
||||
|
||||
/// Returns the `payload_withhold_boost_root`.
|
||||
fn payload_withhold_boost_root(&self) -> Hash256;
|
||||
|
||||
/// Returns the `payload_withhold_boost_full`.
|
||||
fn payload_withhold_boost_full(&self) -> bool;
|
||||
|
||||
/// Returns the `payload_reveal_boost_root`.
|
||||
fn payload_reveal_boost_root(&self) -> Hash256;
|
||||
|
||||
/// Sets `finalized_checkpoint`.
|
||||
fn set_finalized_checkpoint(&mut self, checkpoint: Checkpoint);
|
||||
|
||||
@@ -74,6 +83,15 @@ pub trait ForkChoiceStore<E: EthSpec>: Sized {
|
||||
/// Sets the proposer boost root.
|
||||
fn set_proposer_boost_root(&mut self, proposer_boost_root: Hash256);
|
||||
|
||||
/// Sets the payload withhold boost root.
|
||||
fn set_payload_withhold_boost_root(&mut self, payload_withhold_boost_root: Hash256);
|
||||
|
||||
/// Sets the payload withhold boost full.
|
||||
fn set_payload_withhold_boost_full(&mut self, payload_withhold_boost_full: bool);
|
||||
|
||||
/// Sets the payload reveal boost root.
|
||||
fn set_payload_reveal_boost_root(&mut self, payload_reveal_boost_root: Hash256);
|
||||
|
||||
/// Gets the equivocating indices.
|
||||
fn equivocating_indices(&self) -> &BTreeSet<u64>;
|
||||
|
||||
|
||||
@@ -1,136 +0,0 @@
|
||||
use super::signature_sets::{execution_envelope_signature_set, get_pubkey_from_state};
|
||||
use crate::per_block_processing::compute_timestamp_at_slot;
|
||||
use crate::per_block_processing::errors::{BlockProcessingError, ExecutionEnvelopeError};
|
||||
use crate::VerifySignatures;
|
||||
use tree_hash::TreeHash;
|
||||
use types::{BeaconState, ChainSpec, EthSpec, Hash256, SignedExecutionEnvelope};
|
||||
|
||||
pub fn process_execution_envelope<E: EthSpec>(
|
||||
state: &mut BeaconState<E>,
|
||||
signed_envelope: SignedExecutionEnvelope<E>,
|
||||
spec: &ChainSpec,
|
||||
verify_signatures: VerifySignatures,
|
||||
) -> Result<(), BlockProcessingError> {
|
||||
if verify_signatures.is_true() {
|
||||
block_verify!(
|
||||
execution_envelope_signature_set(
|
||||
state,
|
||||
|i| get_pubkey_from_state(state, i),
|
||||
&signed_envelope,
|
||||
spec
|
||||
)?
|
||||
.verify(),
|
||||
ExecutionEnvelopeError::BadSignature.into()
|
||||
)
|
||||
}
|
||||
|
||||
let envelope = signed_envelope.message();
|
||||
let payload = &envelope.payload;
|
||||
let previous_state_root = state.canonical_root()?;
|
||||
if state.latest_block_header().state_root == Hash256::default() {
|
||||
*state.latest_block_header_mut().state_root = *previous_state_root;
|
||||
}
|
||||
|
||||
// Verify consistency with the beacon block
|
||||
block_verify!(
|
||||
envelope.tree_hash_root() == state.latest_block_header().tree_hash_root(),
|
||||
ExecutionEnvelopeError::LatestBlockHeaderMismatch {
|
||||
envelope_root: envelope.tree_hash_root(),
|
||||
block_header_root: state.latest_block_header().tree_hash_root(),
|
||||
}
|
||||
.into()
|
||||
);
|
||||
|
||||
// Verify consistency with the committed bid
|
||||
let committed_bid = state.latest_execution_bid()?;
|
||||
block_verify!(
|
||||
envelope.builder_index == committed_bid.builder_index,
|
||||
ExecutionEnvelopeError::BuilderIndexMismatch {
|
||||
committed_bid: committed_bid.builder_index,
|
||||
envelope: envelope.builder_index,
|
||||
}
|
||||
.into()
|
||||
);
|
||||
block_verify!(
|
||||
committed_bid.blob_kzg_commitments_root == envelope.blob_kzg_commitments.tree_hash_root(),
|
||||
ExecutionEnvelopeError::BlobKzgCommitmentsRootMismatch {
|
||||
committed_bid: committed_bid.blob_kzg_commitments_root,
|
||||
envelope: envelope.blob_kzg_commitments.tree_hash_root(),
|
||||
}
|
||||
.into()
|
||||
);
|
||||
|
||||
if !envelope.payment_withheld {
|
||||
// Verify the withdrawals root
|
||||
block_verify!(
|
||||
payload.withdrawals.tree_hash_root() == state.latest_withdrawals_root()?,
|
||||
ExecutionEnvelopeError::WithdrawalsRootMismatch {
|
||||
state: state.latest_withdrawals_root()?,
|
||||
envelope: payload.withdrawals.tree_hash_root(),
|
||||
}
|
||||
.into()
|
||||
);
|
||||
|
||||
// Verify the gas limit
|
||||
block_verify!(
|
||||
payload.gas_limit == committed_bid.gas_limit,
|
||||
ExecutionEnvelopeError::GasLimitMismatch {
|
||||
committed_bid: committed_bid.gas_limit,
|
||||
envelope: payload.gas_limit,
|
||||
}
|
||||
.into()
|
||||
);
|
||||
|
||||
block_verify!(
|
||||
committed_bid.block_hash == payload.block_hash,
|
||||
ExecutionEnvelopeError::BlockHashMismatch {
|
||||
committed_bid: committed_bid.block_hash,
|
||||
envelope: payload.block_hash,
|
||||
}
|
||||
.into()
|
||||
);
|
||||
|
||||
// Verify consistency of the parent hash with respect to the previous execution payload
|
||||
block_verify!(
|
||||
payload.parent_hash == state.latest_block_hash()?,
|
||||
ExecutionEnvelopeError::ParentHashMismatch {
|
||||
state: state.latest_block_hash()?,
|
||||
envelope: payload.parent_hash,
|
||||
}
|
||||
.into()
|
||||
);
|
||||
|
||||
// Verify prev_randao
|
||||
block_verify!(
|
||||
payload.prev_randao == *state.get_randao_mix(state.current_epoch())?,
|
||||
ExecutionEnvelopeError::PrevRandaoMismatch {
|
||||
state: *state.get_randao_mix(state.current_epoch())?,
|
||||
envelope: payload.prev_randao,
|
||||
}
|
||||
.into()
|
||||
);
|
||||
|
||||
// Verify the timestamp
|
||||
let state_timestamp = compute_timestamp_at_slot(state, state.slot(), spec)?;
|
||||
block_verify!(
|
||||
payload.timestamp == state_timestamp,
|
||||
ExecutionEnvelopeError::TimestampMismatch {
|
||||
state: state_timestamp,
|
||||
envelope: payload.timestamp,
|
||||
}
|
||||
.into()
|
||||
);
|
||||
|
||||
// Verify the commitments are under limit
|
||||
block_verify!(
|
||||
envelope.blob_kzg_commitments.len() <= E::max_blob_commitments_per_block(),
|
||||
ExecutionEnvelopeError::BlobLimitExceeded {
|
||||
max: E::max_blob_commitments_per_block(),
|
||||
envelope: envelope.blob_kzg_commitments.len(),
|
||||
}
|
||||
.into()
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -21,7 +21,6 @@ pub mod block_replayer;
|
||||
pub mod common;
|
||||
pub mod consensus_context;
|
||||
pub mod epoch_cache;
|
||||
pub mod execution_processing;
|
||||
pub mod genesis;
|
||||
pub mod per_block_processing;
|
||||
pub mod per_epoch_processing;
|
||||
@@ -33,7 +32,6 @@ pub mod verify_operation;
|
||||
pub use all_caches::AllCaches;
|
||||
pub use block_replayer::{BlockReplayError, BlockReplayer};
|
||||
pub use consensus_context::{ConsensusContext, ContextError};
|
||||
pub use execution_processing::process_execution_envelope;
|
||||
pub use genesis::{
|
||||
eth2_genesis_time, initialize_beacon_state_from_eth1, is_valid_genesis_state,
|
||||
process_activations,
|
||||
|
||||
@@ -63,7 +63,6 @@ pub enum BlockProcessingError {
|
||||
ExecutionBidInvalid {
|
||||
reason: ExecutionBidInvalid,
|
||||
},
|
||||
ExecutionEnvelopeError(ExecutionEnvelopeError),
|
||||
BeaconStateError(BeaconStateError),
|
||||
SignatureSetError(SignatureSetError),
|
||||
SszTypesError(ssz_types::Error),
|
||||
@@ -160,12 +159,6 @@ impl From<ExecutionBidInvalid> for BlockProcessingError {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ExecutionEnvelopeError> for BlockProcessingError {
|
||||
fn from(e: ExecutionEnvelopeError) -> Self {
|
||||
BlockProcessingError::ExecutionEnvelopeError(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<BlockOperationError<HeaderInvalid>> for BlockProcessingError {
|
||||
fn from(e: BlockOperationError<HeaderInvalid>) -> BlockProcessingError {
|
||||
match e {
|
||||
@@ -548,59 +541,3 @@ pub enum ExecutionBidInvalid {
|
||||
bid_parent_root: Hash256,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub enum ExecutionEnvelopeError {
|
||||
/// The signature is invalid.
|
||||
BadSignature,
|
||||
/// Envelope doesn't match latest beacon block header
|
||||
LatestBlockHeaderMismatch {
|
||||
envelope_root: Hash256,
|
||||
block_header_root: Hash256,
|
||||
},
|
||||
/// The builder index doesn't match the committed bid
|
||||
BuilderIndexMismatch {
|
||||
committed_bid: u64,
|
||||
envelope: u64,
|
||||
},
|
||||
/// The blob KZG commitments root doesn't match the committed bid
|
||||
BlobKzgCommitmentsRootMismatch {
|
||||
committed_bid: Hash256,
|
||||
envelope: Hash256,
|
||||
},
|
||||
/// The withdrawals root doesn't match the state's latest withdrawals root
|
||||
WithdrawalsRootMismatch {
|
||||
state: Hash256,
|
||||
envelope: Hash256,
|
||||
},
|
||||
// The gas limit doesn't match the committed bid
|
||||
GasLimitMismatch {
|
||||
committed_bid: u64,
|
||||
envelope: u64,
|
||||
},
|
||||
// The block hash doesn't match the committed bid
|
||||
BlockHashMismatch {
|
||||
committed_bid: ExecutionBlockHash,
|
||||
envelope: ExecutionBlockHash,
|
||||
},
|
||||
// The parent hash doesn't match the previous execution payload
|
||||
ParentHashMismatch {
|
||||
state: ExecutionBlockHash,
|
||||
envelope: ExecutionBlockHash,
|
||||
},
|
||||
// The previous randao didn't match the payload
|
||||
PrevRandaoMismatch {
|
||||
state: Hash256,
|
||||
envelope: Hash256,
|
||||
},
|
||||
// The timestamp didn't match the payload
|
||||
TimestampMismatch {
|
||||
state: u64,
|
||||
envelope: u64,
|
||||
},
|
||||
// Blob committments exceeded the maximum
|
||||
BlobLimitExceeded {
|
||||
max: usize,
|
||||
envelope: usize,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -406,8 +406,8 @@ where
|
||||
state.genesis_validators_root(),
|
||||
);
|
||||
let message = signed_envelope.message().signing_root(domain);
|
||||
let pubkey = get_pubkey(signed_envelope.message().builder_index as usize).ok_or(
|
||||
Error::ValidatorUnknown(signed_envelope.message().builder_index),
|
||||
let pubkey = get_pubkey(signed_envelope.message().builder_index() as usize).ok_or(
|
||||
Error::ValidatorUnknown(signed_envelope.message().builder_index()),
|
||||
)?;
|
||||
|
||||
Ok(SignatureSet::single_pubkey(
|
||||
|
||||
@@ -8,8 +8,9 @@ use superstruct::superstruct;
|
||||
use test_random_derive::TestRandom;
|
||||
use tree_hash_derive::TreeHash;
|
||||
|
||||
// in all likelihood, this will be superstructed so might as well start early eh?
|
||||
#[superstruct(
|
||||
variants(EIP7732),
|
||||
variants(EIP7732, NextFork),
|
||||
variant_attributes(
|
||||
derive(
|
||||
Debug,
|
||||
@@ -27,6 +28,10 @@ use tree_hash_derive::TreeHash;
|
||||
serde(bound = "E: EthSpec", deny_unknown_fields),
|
||||
arbitrary(bound = "E: EthSpec")
|
||||
),
|
||||
ref_attributes(
|
||||
derive(Debug, PartialEq, TreeHash),
|
||||
tree_hash(enum_behaviour = "transparent")
|
||||
),
|
||||
cast_error(ty = "Error", expr = "BeaconStateError::IncorrectStateVariant"),
|
||||
partial_getter_error(ty = "Error", expr = "BeaconStateError::IncorrectStateVariant")
|
||||
)]
|
||||
@@ -41,12 +46,32 @@ use tree_hash_derive::TreeHash;
|
||||
pub struct ExecutionEnvelope<E: EthSpec> {
|
||||
#[superstruct(only(EIP7732), partial_getter(rename = "payload_eip7732"))]
|
||||
pub payload: ExecutionPayloadEIP7732<E>,
|
||||
#[superstruct(only(NextFork), partial_getter(rename = "payload_next_fork"))]
|
||||
pub payload: ExecutionPayloadEIP7732<E>,
|
||||
pub execution_requests: ExecutionRequests<E>,
|
||||
#[serde(with = "serde_utils::quoted_u64")]
|
||||
#[superstruct(getter(copy))]
|
||||
pub builder_index: u64,
|
||||
#[superstruct(getter(copy))]
|
||||
pub beacon_block_root: Hash256,
|
||||
pub blob_kzg_commitments: KzgCommitments<E>,
|
||||
pub payment_withheld: bool,
|
||||
#[superstruct(getter(copy))]
|
||||
pub payload_withheld: bool,
|
||||
#[superstruct(getter(copy))]
|
||||
pub state_root: Hash256,
|
||||
}
|
||||
|
||||
impl<E: EthSpec> SignedRoot for ExecutionEnvelopeEIP7732<E> {}
|
||||
impl<'a, E: EthSpec> SignedRoot for ExecutionEnvelopeRef<'a, E> {}
|
||||
|
||||
impl<'a, E: EthSpec> ExecutionEnvelopeRef<'a, E> {
|
||||
pub fn payload(&self) -> ExecutionPayloadRef<'a, E> {
|
||||
match self {
|
||||
ExecutionEnvelopeRef::EIP7732(envelope) => {
|
||||
ExecutionPayloadRef::EIP7732(&envelope.payload)
|
||||
}
|
||||
ExecutionEnvelopeRef::NextFork(envelope) => {
|
||||
ExecutionPayloadRef::EIP7732(&envelope.payload)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -172,7 +172,9 @@ pub use crate::eth_spec::EthSpecId;
|
||||
pub use crate::execution_bid::ExecutionBid;
|
||||
pub use crate::execution_block_hash::ExecutionBlockHash;
|
||||
pub use crate::execution_block_header::{EncodableExecutionBlockHeader, ExecutionBlockHeader};
|
||||
pub use crate::execution_envelope::{ExecutionEnvelope, ExecutionEnvelopeEIP7732};
|
||||
pub use crate::execution_envelope::{
|
||||
ExecutionEnvelope, ExecutionEnvelopeEIP7732, ExecutionEnvelopeRef,
|
||||
};
|
||||
pub use crate::execution_payload::{
|
||||
ExecutionPayload, ExecutionPayloadBellatrix, ExecutionPayloadCapella, ExecutionPayloadDeneb,
|
||||
ExecutionPayloadEIP7732, ExecutionPayloadElectra, ExecutionPayloadRef, Transaction,
|
||||
|
||||
@@ -7,8 +7,9 @@ use superstruct::superstruct;
|
||||
use test_random_derive::TestRandom;
|
||||
use tree_hash_derive::TreeHash;
|
||||
|
||||
// in all likelihood, this will be superstructed so might as well start early eh?
|
||||
#[superstruct(
|
||||
variants(EIP7732),
|
||||
variants(EIP7732, NextFork),
|
||||
variant_attributes(
|
||||
derive(
|
||||
Debug,
|
||||
@@ -40,5 +41,51 @@ use tree_hash_derive::TreeHash;
|
||||
pub struct SignedExecutionEnvelope<E: EthSpec> {
|
||||
#[superstruct(only(EIP7732), partial_getter(rename = "message_eip7732"))]
|
||||
pub message: ExecutionEnvelopeEIP7732<E>,
|
||||
#[superstruct(only(NextFork), partial_getter(rename = "message_next_fork"))]
|
||||
pub message: crate::execution_envelope::ExecutionEnvelopeNextFork<E>,
|
||||
pub signature: Signature,
|
||||
}
|
||||
|
||||
impl<E: EthSpec> SignedExecutionEnvelope<E> {
|
||||
pub fn message(&self) -> ExecutionEnvelopeRef<E> {
|
||||
match self {
|
||||
SignedExecutionEnvelope::EIP7732(ref signed) => {
|
||||
ExecutionEnvelopeRef::EIP7732(&signed.message)
|
||||
}
|
||||
SignedExecutionEnvelope::NextFork(ref signed) => {
|
||||
ExecutionEnvelopeRef::NextFork(&signed.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Verify `self.signature`.
|
||||
///
|
||||
/// The `parent_state` is the post-state of the beacon block with
|
||||
/// block_root = self.message.beacon_block_root
|
||||
pub fn verify_signature(
|
||||
&self,
|
||||
parent_state: &BeaconState<E>,
|
||||
genesis_validators_root: Hash256,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<bool, BeaconStateError> {
|
||||
let domain = spec.get_domain(
|
||||
parent_state.current_epoch(),
|
||||
Domain::BeaconBuilder,
|
||||
&parent_state.fork(),
|
||||
genesis_validators_root,
|
||||
);
|
||||
let pubkey = parent_state
|
||||
.validators()
|
||||
.get(self.message().builder_index() as usize)
|
||||
.and_then(|v| {
|
||||
let pk: Option<PublicKey> = v.pubkey.decompress().ok();
|
||||
pk
|
||||
})
|
||||
.ok_or_else(|| {
|
||||
BeaconStateError::UnknownValidator(self.message().builder_index() as usize)
|
||||
})?;
|
||||
let message = self.message().signing_root(domain);
|
||||
|
||||
Ok(self.signature().verify(&pubkey, message))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user