mirror of
https://github.com/sigp/lighthouse.git
synced 2026-06-30 19:34:37 +00:00
process_execution_envelope
This commit is contained in:
@@ -1,10 +1,136 @@
|
|||||||
use crate::BlockProcessingError;
|
use super::signature_sets::{execution_envelope_signature_set, get_pubkey_from_state};
|
||||||
use types::{BeaconState, ChainSpec, EthSpec, SignedExecutionEnvelope};
|
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_payload_envelope<E: EthSpec>(
|
pub fn process_execution_envelope<E: EthSpec>(
|
||||||
state: &mut BeaconState<E>,
|
state: &mut BeaconState<E>,
|
||||||
signed_envelope: SignedExecutionEnvelope<E>,
|
signed_envelope: SignedExecutionEnvelope<E>,
|
||||||
spec: &ChainSpec,
|
spec: &ChainSpec,
|
||||||
|
verify_signatures: VerifySignatures,
|
||||||
) -> Result<(), BlockProcessingError> {
|
) -> 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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ pub mod verify_operation;
|
|||||||
pub use all_caches::AllCaches;
|
pub use all_caches::AllCaches;
|
||||||
pub use block_replayer::{BlockReplayError, BlockReplayer};
|
pub use block_replayer::{BlockReplayError, BlockReplayer};
|
||||||
pub use consensus_context::{ConsensusContext, ContextError};
|
pub use consensus_context::{ConsensusContext, ContextError};
|
||||||
pub use execution_processing::process_execution_payload_envelope;
|
pub use execution_processing::process_execution_envelope;
|
||||||
pub use genesis::{
|
pub use genesis::{
|
||||||
eth2_genesis_time, initialize_beacon_state_from_eth1, is_valid_genesis_state,
|
eth2_genesis_time, initialize_beacon_state_from_eth1, is_valid_genesis_state,
|
||||||
process_activations,
|
process_activations,
|
||||||
|
|||||||
@@ -699,7 +699,7 @@ pub fn process_execution_bid<E: EthSpec, Payload: AbstractExecPayload<E>>(
|
|||||||
decrease_balance(state, builder_index as usize, bid.value)?;
|
decrease_balance(state, builder_index as usize, bid.value)?;
|
||||||
increase_balance(state, block.proposer_index() as usize, bid.value)?;
|
increase_balance(state, block.proposer_index() as usize, bid.value)?;
|
||||||
|
|
||||||
*state.latest_execution_bid_eip7732_mut()? = bid.clone();
|
*state.latest_execution_bid_mut()? = bid.clone();
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ pub enum BlockProcessingError {
|
|||||||
ExecutionBidInvalid {
|
ExecutionBidInvalid {
|
||||||
reason: ExecutionBidInvalid,
|
reason: ExecutionBidInvalid,
|
||||||
},
|
},
|
||||||
|
ExecutionEnvelopeError(ExecutionEnvelopeError),
|
||||||
BeaconStateError(BeaconStateError),
|
BeaconStateError(BeaconStateError),
|
||||||
SignatureSetError(SignatureSetError),
|
SignatureSetError(SignatureSetError),
|
||||||
SszTypesError(ssz_types::Error),
|
SszTypesError(ssz_types::Error),
|
||||||
@@ -159,6 +160,12 @@ impl From<ExecutionBidInvalid> for BlockProcessingError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<ExecutionEnvelopeError> for BlockProcessingError {
|
||||||
|
fn from(e: ExecutionEnvelopeError) -> Self {
|
||||||
|
BlockProcessingError::ExecutionEnvelopeError(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<BlockOperationError<HeaderInvalid>> for BlockProcessingError {
|
impl From<BlockOperationError<HeaderInvalid>> for BlockProcessingError {
|
||||||
fn from(e: BlockOperationError<HeaderInvalid>) -> BlockProcessingError {
|
fn from(e: BlockOperationError<HeaderInvalid>) -> BlockProcessingError {
|
||||||
match e {
|
match e {
|
||||||
@@ -541,3 +548,59 @@ pub enum ExecutionBidInvalid {
|
|||||||
bid_parent_root: Hash256,
|
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,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|||||||
@@ -97,6 +97,10 @@ pub mod eip7732 {
|
|||||||
state: &mut BeaconState<E>,
|
state: &mut BeaconState<E>,
|
||||||
spec: &ChainSpec,
|
spec: &ChainSpec,
|
||||||
) -> Result<(), BlockProcessingError> {
|
) -> Result<(), BlockProcessingError> {
|
||||||
|
if !state.is_parent_block_full() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
let (expected_withdrawals, partial_withdrawals_count) =
|
let (expected_withdrawals, partial_withdrawals_count) =
|
||||||
get_expected_withdrawals(state, spec)?;
|
get_expected_withdrawals(state, spec)?;
|
||||||
process_withdrawals_common(state, expected_withdrawals, partial_withdrawals_count, spec)
|
process_withdrawals_common(state, expected_withdrawals, partial_withdrawals_count, spec)
|
||||||
|
|||||||
@@ -12,8 +12,8 @@ use types::{
|
|||||||
InconsistentFork, IndexedAttestation, IndexedAttestationRef, IndexedPayloadAttestation,
|
InconsistentFork, IndexedAttestation, IndexedAttestationRef, IndexedPayloadAttestation,
|
||||||
ProposerSlashing, PublicKey, PublicKeyBytes, Signature, SignedAggregateAndProof,
|
ProposerSlashing, PublicKey, PublicKeyBytes, Signature, SignedAggregateAndProof,
|
||||||
SignedBeaconBlock, SignedBeaconBlockHeader, SignedBlsToExecutionChange,
|
SignedBeaconBlock, SignedBeaconBlockHeader, SignedBlsToExecutionChange,
|
||||||
SignedContributionAndProof, SignedExecutionBid, SignedRoot, SignedVoluntaryExit, SigningData,
|
SignedContributionAndProof, SignedExecutionBid, SignedExecutionEnvelope, SignedRoot,
|
||||||
Slot, SyncAggregate, SyncAggregatorSelectionData, Unsigned,
|
SignedVoluntaryExit, SigningData, Slot, SyncAggregate, SyncAggregatorSelectionData, Unsigned,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub type Result<T> = std::result::Result<T, Error>;
|
pub type Result<T> = std::result::Result<T, Error>;
|
||||||
@@ -389,6 +389,34 @@ where
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn execution_envelope_signature_set<'a, E, F>(
|
||||||
|
state: &'a BeaconState<E>,
|
||||||
|
get_pubkey: F,
|
||||||
|
signed_envelope: &'a SignedExecutionEnvelope<E>,
|
||||||
|
spec: &'a ChainSpec,
|
||||||
|
) -> Result<SignatureSet<'a>>
|
||||||
|
where
|
||||||
|
E: EthSpec,
|
||||||
|
F: Fn(usize) -> Option<Cow<'a, PublicKey>>,
|
||||||
|
{
|
||||||
|
let domain = spec.get_domain(
|
||||||
|
state.current_epoch(),
|
||||||
|
Domain::BeaconBuilder,
|
||||||
|
&state.fork(),
|
||||||
|
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),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok(SignatureSet::single_pubkey(
|
||||||
|
signed_envelope.signature(),
|
||||||
|
pubkey,
|
||||||
|
message,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the signature set for the given `attester_slashing` and corresponding `pubkeys`.
|
/// Returns the signature set for the given `attester_slashing` and corresponding `pubkeys`.
|
||||||
pub fn attester_slashing_signature_sets<'a, E, F>(
|
pub fn attester_slashing_signature_sets<'a, E, F>(
|
||||||
state: &'a BeaconState<E>,
|
state: &'a BeaconState<E>,
|
||||||
|
|||||||
@@ -482,7 +482,7 @@ where
|
|||||||
)]
|
)]
|
||||||
#[metastruct(exclude_from(tree_lists))]
|
#[metastruct(exclude_from(tree_lists))]
|
||||||
pub latest_execution_payload_header: ExecutionPayloadHeaderElectra<E>,
|
pub latest_execution_payload_header: ExecutionPayloadHeaderElectra<E>,
|
||||||
#[superstruct(only(EIP7732), partial_getter(rename = "latest_execution_bid_eip7732"))]
|
#[superstruct(only(EIP7732))]
|
||||||
#[metastruct(exclude_from(tree_lists))]
|
#[metastruct(exclude_from(tree_lists))]
|
||||||
pub latest_execution_bid: ExecutionBid,
|
pub latest_execution_bid: ExecutionBid,
|
||||||
|
|
||||||
@@ -1982,6 +1982,19 @@ impl<E: EthSpec> BeaconState<E> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_parent_block_full(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
BeaconState::Base(_) | BeaconState::Altair(_) => false,
|
||||||
|
BeaconState::Bellatrix(_)
|
||||||
|
| BeaconState::Capella(_)
|
||||||
|
| BeaconState::Deneb(_)
|
||||||
|
| BeaconState::Electra(_) => true,
|
||||||
|
BeaconState::EIP7732(state) => {
|
||||||
|
state.latest_execution_bid.block_hash == state.latest_block_hash
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Get the committee cache for some `slot`.
|
/// Get the committee cache for some `slot`.
|
||||||
///
|
///
|
||||||
/// Return an error if the cache for the slot's epoch is not initialized.
|
/// Return an error if the cache for the slot's epoch is not initialized.
|
||||||
|
|||||||
@@ -48,3 +48,5 @@ pub struct ExecutionEnvelope<E: EthSpec> {
|
|||||||
pub payment_withheld: bool,
|
pub payment_withheld: bool,
|
||||||
pub state_root: Hash256,
|
pub state_root: Hash256,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<E: EthSpec> SignedRoot for ExecutionEnvelopeEIP7732<E> {}
|
||||||
|
|||||||
Reference in New Issue
Block a user