Whole Bunch of Changes Sorry

This commit is contained in:
Mark Mackey
2024-11-30 16:09:35 -06:00
parent cd7b3cfa2d
commit dd571b5b4d
16 changed files with 692 additions and 272 deletions

View File

@@ -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>,
}

View File

@@ -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.

View 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,
})
}
}

View File

@@ -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",
);

View File

@@ -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;

View File

@@ -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"),
}
}

View File

@@ -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),

View File

@@ -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,

View File

@@ -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>;

View File

@@ -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(())
}

View File

@@ -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,

View File

@@ -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,
},
}

View File

@@ -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(

View File

@@ -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)
}
}
}
}

View File

@@ -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,

View File

@@ -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))
}
}