Many fixes

This commit is contained in:
Eitan Seri-Levi
2026-05-09 18:58:08 +03:00
parent 2356bdd256
commit 1b2cf3ba01
45 changed files with 667 additions and 1328 deletions

View File

@@ -13,8 +13,7 @@ use crate::{
execution::{
ExecutionPayloadHeaderBellatrix, ExecutionPayloadHeaderCapella,
ExecutionPayloadHeaderDeneb, ExecutionPayloadHeaderElectra, ExecutionPayloadHeaderFulu,
ExecutionPayloadHeaderHeze, ExecutionPayloadHeaderRef, ExecutionPayloadHeaderRefMut,
ExecutionRequests,
ExecutionPayloadHeaderRef, ExecutionPayloadHeaderRefMut, ExecutionRequests,
},
fork::{ForkName, ForkVersionDecode},
kzg_ext::KzgCommitments,
@@ -22,7 +21,7 @@ use crate::{
};
#[superstruct(
variants(Bellatrix, Capella, Deneb, Electra, Heze, Fulu),
variants(Bellatrix, Capella, Deneb, Electra, Fulu),
variant_attributes(
derive(
PartialEq,
@@ -53,13 +52,11 @@ pub struct BuilderBid<E: EthSpec> {
pub header: ExecutionPayloadHeaderDeneb<E>,
#[superstruct(only(Electra), partial_getter(rename = "header_electra"))]
pub header: ExecutionPayloadHeaderElectra<E>,
#[superstruct(only(Heze), partial_getter(rename = "header_heze"))]
pub header: ExecutionPayloadHeaderHeze<E>,
#[superstruct(only(Fulu), partial_getter(rename = "header_fulu"))]
pub header: ExecutionPayloadHeaderFulu<E>,
#[superstruct(only(Deneb, Electra, Heze, Fulu))]
#[superstruct(only(Deneb, Electra, Fulu))]
pub blob_kzg_commitments: KzgCommitments<E>,
#[superstruct(only(Electra, Heze, Fulu))]
#[superstruct(only(Electra, Fulu))]
pub execution_requests: ExecutionRequests<E>,
#[serde(with = "serde_utils::quoted_u256")]
pub value: Uint256,
@@ -92,7 +89,7 @@ impl<E: EthSpec> ForkVersionDecode for BuilderBid<E> {
/// SSZ decode with explicit fork variant.
fn from_ssz_bytes_by_fork(bytes: &[u8], fork_name: ForkName) -> Result<Self, ssz::DecodeError> {
let builder_bid = match fork_name {
ForkName::Altair | ForkName::Base | ForkName::Gloas => {
ForkName::Altair | ForkName::Base | ForkName::Gloas | ForkName::Heze => {
return Err(ssz::DecodeError::BytesInvalid(format!(
"unsupported fork for ExecutionPayloadHeader: {fork_name}",
)));
@@ -103,7 +100,6 @@ impl<E: EthSpec> ForkVersionDecode for BuilderBid<E> {
ForkName::Capella => BuilderBid::Capella(BuilderBidCapella::from_ssz_bytes(bytes)?),
ForkName::Deneb => BuilderBid::Deneb(BuilderBidDeneb::from_ssz_bytes(bytes)?),
ForkName::Electra => BuilderBid::Electra(BuilderBidElectra::from_ssz_bytes(bytes)?),
ForkName::Heze => BuilderBid::Heze(BuilderBidHeze::from_ssz_bytes(bytes)?),
ForkName::Fulu => BuilderBid::Fulu(BuilderBidFulu::from_ssz_bytes(bytes)?),
};
Ok(builder_bid)
@@ -160,10 +156,7 @@ impl<'de, E: EthSpec> ContextDeserialize<'de, ForkName> for BuilderBid<E> {
ForkName::Fulu => {
Self::Fulu(Deserialize::deserialize(deserializer).map_err(convert_err)?)
}
ForkName::Heze => {
Self::Heze(Deserialize::deserialize(deserializer).map_err(convert_err)?)
}
ForkName::Base | ForkName::Altair | ForkName::Gloas => {
ForkName::Base | ForkName::Altair | ForkName::Gloas | ForkName::Heze => {
return Err(serde::de::Error::custom(format!(
"BuilderBid failed to deserialize: unsupported fork '{}'",
context

View File

@@ -7,7 +7,7 @@ mod proposer_preferences;
pub use builder::{Builder, BuilderIndex};
pub use builder_bid::{
BuilderBid, BuilderBidBellatrix, BuilderBidCapella, BuilderBidDeneb, BuilderBidElectra,
BuilderBidFulu, BuilderBidHeze, SignedBuilderBid,
BuilderBidFulu, SignedBuilderBid,
};
pub use builder_pending_payment::BuilderPendingPayment;
pub use builder_pending_withdrawal::BuilderPendingWithdrawal;

View File

@@ -29,10 +29,8 @@ macro_rules! map_execution_payload_into_full_payload {
let f: fn(ExecutionPayloadFulu<_>, fn(_) -> _) -> _ = $f;
f(inner, FullPayload::Fulu)
}
ExecutionPayload::Gloas(_) => panic!("FullPayload::Gloas does not exist!"),
ExecutionPayload::Heze(inner) => {
let f: fn(ExecutionPayloadHeze<_>, fn(_) -> _) -> _ = $f;
f(inner, FullPayload::Heze)
ExecutionPayload::Gloas(_) | ExecutionPayload::Heze(_) => {
panic!("FullPayload::Gloas does not exist!")
}
}
};
@@ -62,10 +60,8 @@ macro_rules! map_execution_payload_into_blinded_payload {
let f: fn(ExecutionPayloadFulu<_>, fn(_) -> _) -> _ = $f;
f(inner, BlindedPayload::Fulu)
}
ExecutionPayload::Gloas(_) => panic!("BlindedPayload::Gloas does not exist!"),
ExecutionPayload::Heze(inner) => {
let f: fn(ExecutionPayloadHeze<_>, fn(_) -> _) -> _ = $f;
f(inner, BlindedPayload::Heze)
ExecutionPayload::Gloas(_) | ExecutionPayload::Heze(_) => {
panic!("BlindedPayload::Gloas does not exist!")
}
}
};
@@ -110,14 +106,7 @@ macro_rules! map_execution_payload_ref_into_execution_payload_header {
) -> _ = $f;
f(inner, ExecutionPayloadHeader::Fulu)
}
ExecutionPayloadRef::Gloas(_) => panic!("ExecutionPayloadHeader::Gloas does not exist!"),
ExecutionPayloadRef::Heze(inner) => {
let f: fn(
&$lifetime ExecutionPayloadHeze<_>,
fn(_) -> _,
) -> _ = $f;
f(inner, ExecutionPayloadHeader::Heze)
}
ExecutionPayloadRef::Gloas(_) | ExecutionPayloadRef::Heze(_) => panic!("ExecutionPayloadHeader::Gloas does not exist!"),
}
}
}

View File

@@ -1,56 +1,135 @@
use crate::execution::{ExecutionPayloadGloas, ExecutionRequests};
use crate::execution::{
ExecutionPayloadGloas, ExecutionPayloadHeze, ExecutionPayloadRef, ExecutionRequests,
};
use crate::fork::ForkVersionDecode;
use crate::state::BeaconStateError;
use crate::test_utils::TestRandom;
use crate::{EthSpec, ForkName, Hash256, SignedRoot, Slot};
use context_deserialize::context_deserialize;
use context_deserialize::{ContextDeserialize, context_deserialize};
use educe::Educe;
use fixed_bytes::FixedBytesExtended;
use serde::{Deserialize, Serialize};
use serde::{Deserialize, Deserializer, Serialize};
use ssz::{BYTES_PER_LENGTH_OFFSET, Encode as SszEncode};
use ssz_derive::{Decode, Encode};
use superstruct::superstruct;
use test_random_derive::TestRandom;
use tree_hash_derive::TreeHash;
#[derive(Debug, Clone, Serialize, Encode, Decode, Deserialize, TestRandom, TreeHash, Educe)]
#[superstruct(
variants(Gloas, Heze),
variant_attributes(
derive(
Default,
Debug,
Clone,
Serialize,
Deserialize,
Encode,
Decode,
TreeHash,
TestRandom,
Educe,
),
educe(PartialEq, Hash(bound(E: EthSpec))),
serde(bound = "E: EthSpec", deny_unknown_fields),
cfg_attr(
feature = "arbitrary",
derive(arbitrary::Arbitrary),
arbitrary(bound = "E: EthSpec"),
),
context_deserialize(ForkName),
),
ref_attributes(
derive(PartialEq, TreeHash, Debug),
tree_hash(enum_behaviour = "transparent")
),
cast_error(
ty = "BeaconStateError",
expr = "BeaconStateError::IncorrectStateVariant"
),
partial_getter_error(
ty = "BeaconStateError",
expr = "BeaconStateError::IncorrectStateVariant"
),
)]
#[cfg_attr(
feature = "arbitrary",
derive(arbitrary::Arbitrary),
arbitrary(bound = "E: EthSpec")
)]
#[derive(Debug, Clone, Serialize, Deserialize, Encode, TreeHash, Educe)]
#[educe(PartialEq, Hash(bound(E: EthSpec)))]
#[context_deserialize(ForkName)]
#[serde(bound = "E: EthSpec")]
#[serde(bound = "E: EthSpec", untagged)]
#[tree_hash(enum_behaviour = "transparent")]
#[ssz(enum_behaviour = "transparent")]
pub struct ExecutionPayloadEnvelope<E: EthSpec> {
#[superstruct(only(Gloas), partial_getter(rename = "payload_gloas"))]
pub payload: ExecutionPayloadGloas<E>,
#[superstruct(only(Heze), partial_getter(rename = "payload_heze"))]
pub payload: ExecutionPayloadHeze<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,
#[superstruct(getter(copy))]
pub parent_beacon_block_root: Hash256,
}
impl<E: EthSpec> ExecutionPayloadEnvelope<E> {
/// Returns an empty envelope with all fields zeroed. Used for SSZ size calculations.
pub fn slot(&self) -> Slot {
match self {
Self::Gloas(env) => env.payload.slot_number,
Self::Heze(env) => env.payload.slot_number,
}
}
pub fn payload(&self) -> ExecutionPayloadRef<'_, E> {
match self {
Self::Gloas(env) => ExecutionPayloadRef::Gloas(&env.payload),
Self::Heze(env) => ExecutionPayloadRef::Heze(&env.payload),
}
}
}
impl<'a, E: EthSpec> ExecutionPayloadEnvelopeRef<'a, E> {
pub fn slot(&self) -> Slot {
match self {
Self::Gloas(env) => env.payload.slot_number,
Self::Heze(env) => env.payload.slot_number,
}
}
pub fn payload(&self) -> ExecutionPayloadRef<'_, E> {
match self {
Self::Gloas(env) => ExecutionPayloadRef::Gloas(&env.payload),
Self::Heze(env) => ExecutionPayloadRef::Heze(&env.payload),
}
}
}
impl<E: EthSpec> ExecutionPayloadEnvelopeGloas<E> {
pub fn empty() -> Self {
Self {
payload: ExecutionPayloadGloas::default(),
execution_requests: ExecutionRequests::default(),
builder_index: 0,
beacon_block_root: Hash256::zero(),
parent_beacon_block_root: Hash256::zero(),
beacon_block_root: Hash256::ZERO,
parent_beacon_block_root: Hash256::ZERO,
}
}
/// Returns the minimum SSZ-encoded size (all variable-length fields empty).
pub fn min_size() -> usize {
Self::empty().as_ssz_bytes().len()
}
/// Returns the maximum SSZ-encoded size.
#[allow(clippy::arithmetic_side_effects)]
pub fn max_size() -> usize {
Self::min_size()
// ExecutionPayloadGloas variable-length fields:
+ (E::max_extra_data_bytes() * <u8 as SszEncode>::ssz_fixed_len())
+ (E::max_transactions_per_payload()
* (BYTES_PER_LENGTH_OFFSET + E::max_bytes_per_transaction()))
+ (E::max_withdrawals_per_payload()
* <crate::Withdrawal as SszEncode>::ssz_fixed_len())
// ExecutionRequests variable-length fields:
+ (E::max_withdrawals_per_payload() * <crate::Withdrawal as SszEncode>::ssz_fixed_len())
+ (E::max_deposit_requests_per_payload()
* <crate::DepositRequest as SszEncode>::ssz_fixed_len())
+ (E::max_withdrawal_requests_per_payload()
@@ -58,18 +137,99 @@ impl<E: EthSpec> ExecutionPayloadEnvelope<E> {
+ (E::max_consolidation_requests_per_payload()
* <crate::ConsolidationRequest as SszEncode>::ssz_fixed_len())
}
}
pub fn slot(&self) -> Slot {
self.payload.slot_number
impl<E: EthSpec> ExecutionPayloadEnvelopeHeze<E> {
pub fn empty() -> Self {
Self {
payload: ExecutionPayloadHeze::default(),
execution_requests: ExecutionRequests::default(),
builder_index: 0,
beacon_block_root: Hash256::ZERO,
parent_beacon_block_root: Hash256::ZERO,
}
}
pub fn min_size() -> usize {
Self::empty().as_ssz_bytes().len()
}
#[allow(clippy::arithmetic_side_effects)]
pub fn max_size() -> usize {
Self::min_size()
+ (E::max_extra_data_bytes() * <u8 as SszEncode>::ssz_fixed_len())
+ (E::max_transactions_per_payload()
* (BYTES_PER_LENGTH_OFFSET + E::max_bytes_per_transaction()))
+ (E::max_withdrawals_per_payload() * <crate::Withdrawal as SszEncode>::ssz_fixed_len())
+ (E::max_deposit_requests_per_payload()
* <crate::DepositRequest as SszEncode>::ssz_fixed_len())
+ (E::max_withdrawal_requests_per_payload()
* <crate::WithdrawalRequest as SszEncode>::ssz_fixed_len())
+ (E::max_consolidation_requests_per_payload()
* <crate::ConsolidationRequest as SszEncode>::ssz_fixed_len())
}
}
impl<E: EthSpec> ForkVersionDecode for ExecutionPayloadEnvelope<E> {
fn from_ssz_bytes_by_fork(bytes: &[u8], fork_name: ForkName) -> Result<Self, ssz::DecodeError> {
match fork_name {
ForkName::Gloas => {
<ExecutionPayloadEnvelopeGloas<E> as ssz::Decode>::from_ssz_bytes(bytes)
.map(Self::Gloas)
}
ForkName::Heze => {
<ExecutionPayloadEnvelopeHeze<E> as ssz::Decode>::from_ssz_bytes(bytes)
.map(Self::Heze)
}
_ => Err(ssz::DecodeError::BytesInvalid(format!(
"unsupported fork for ExecutionPayloadEnvelope: {fork_name}",
))),
}
}
}
impl<E: EthSpec> SignedRoot for ExecutionPayloadEnvelope<E> {}
impl<E: EthSpec> SignedRoot for ExecutionPayloadEnvelopeGloas<E> {}
impl<E: EthSpec> SignedRoot for ExecutionPayloadEnvelopeHeze<E> {}
impl<'a, E: EthSpec> SignedRoot for ExecutionPayloadEnvelopeRef<'a, E> {}
impl<'de, E: EthSpec> ContextDeserialize<'de, ForkName> for ExecutionPayloadEnvelope<E> {
fn context_deserialize<D>(deserializer: D, context: ForkName) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let convert_err = |e| {
serde::de::Error::custom(format!(
"ExecutionPayloadEnvelope failed to deserialize: {:?}",
e
))
};
match context {
ForkName::Heze => Ok(Self::Heze(
Deserialize::deserialize(deserializer).map_err(convert_err)?,
)),
ForkName::Gloas => Ok(Self::Gloas(
Deserialize::deserialize(deserializer).map_err(convert_err)?,
)),
_ => Err(serde::de::Error::custom(format!(
"ExecutionPayloadEnvelope failed to deserialize: unsupported fork '{}'",
context
))),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::MainnetEthSpec;
ssz_and_tree_hash_tests!(ExecutionPayloadEnvelope<MainnetEthSpec>);
mod gloas {
use super::*;
ssz_and_tree_hash_tests!(ExecutionPayloadEnvelopeGloas<MainnetEthSpec>);
}
mod heze {
use super::*;
ssz_and_tree_hash_tests!(ExecutionPayloadEnvelopeHeze<MainnetEthSpec>);
}
}

View File

@@ -14,8 +14,7 @@ use crate::{
core::{Address, EthSpec, ExecutionBlockHash, Hash256, Uint256},
execution::{
ExecutionPayloadBellatrix, ExecutionPayloadCapella, ExecutionPayloadDeneb,
ExecutionPayloadElectra, ExecutionPayloadFulu, ExecutionPayloadHeze, ExecutionPayloadRef,
Transactions,
ExecutionPayloadElectra, ExecutionPayloadFulu, ExecutionPayloadRef, Transactions,
},
fork::ForkName,
map_execution_payload_ref_into_execution_payload_header,
@@ -24,7 +23,7 @@ use crate::{
};
#[superstruct(
variants(Bellatrix, Capella, Deneb, Electra, Heze, Fulu),
variants(Bellatrix, Capella, Deneb, Electra, Fulu),
variant_attributes(
derive(
Default,
@@ -106,12 +105,12 @@ pub struct ExecutionPayloadHeader<E: EthSpec> {
pub block_hash: ExecutionBlockHash,
#[superstruct(getter(copy))]
pub transactions_root: Hash256,
#[superstruct(only(Capella, Deneb, Electra, Heze, Fulu), partial_getter(copy))]
#[superstruct(only(Capella, Deneb, Electra, Fulu), partial_getter(copy))]
pub withdrawals_root: Hash256,
#[superstruct(only(Deneb, Electra, Heze, Fulu), partial_getter(copy))]
#[superstruct(only(Deneb, Electra, Fulu), partial_getter(copy))]
#[serde(with = "serde_utils::quoted_u64")]
pub blob_gas_used: u64,
#[superstruct(only(Deneb, Electra, Heze, Fulu), partial_getter(copy))]
#[superstruct(only(Deneb, Electra, Fulu), partial_getter(copy))]
#[serde(with = "serde_utils::quoted_u64")]
pub excess_blob_gas: u64,
}
@@ -166,7 +165,6 @@ impl<E: EthSpec> ExecutionPayloadHeader<E> {
ExecutionPayloadHeader::Capella(_) => ForkName::Capella,
ExecutionPayloadHeader::Deneb(_) => ForkName::Deneb,
ExecutionPayloadHeader::Electra(_) => ForkName::Electra,
ExecutionPayloadHeader::Heze(_) => ForkName::Heze,
ExecutionPayloadHeader::Fulu(_) => ForkName::Fulu,
}
}
@@ -251,30 +249,6 @@ impl<E: EthSpec> ExecutionPayloadHeaderDeneb<E> {
}
}
impl<E: EthSpec> ExecutionPayloadHeaderFulu<E> {
pub fn upgrade_to_heze(&self) -> ExecutionPayloadHeaderHeze<E> {
ExecutionPayloadHeaderHeze {
parent_hash: self.parent_hash,
fee_recipient: self.fee_recipient,
state_root: self.state_root,
receipts_root: self.receipts_root,
logs_bloom: self.logs_bloom.clone(),
prev_randao: self.prev_randao,
block_number: self.block_number,
gas_limit: self.gas_limit,
gas_used: self.gas_used,
timestamp: self.timestamp,
extra_data: self.extra_data.clone(),
base_fee_per_gas: self.base_fee_per_gas,
block_hash: self.block_hash,
transactions_root: self.transactions_root,
withdrawals_root: self.withdrawals_root,
blob_gas_used: self.blob_gas_used,
excess_blob_gas: self.excess_blob_gas,
}
}
}
impl<E: EthSpec> ExecutionPayloadHeaderElectra<E> {
pub fn upgrade_to_fulu(&self) -> ExecutionPayloadHeaderFulu<E> {
ExecutionPayloadHeaderFulu {
@@ -390,30 +364,6 @@ impl<'a, E: EthSpec> From<&'a ExecutionPayloadElectra<E>> for ExecutionPayloadHe
}
}
impl<'a, E: EthSpec> From<&'a ExecutionPayloadHeze<E>> for ExecutionPayloadHeaderHeze<E> {
fn from(payload: &'a ExecutionPayloadHeze<E>) -> Self {
Self {
parent_hash: payload.parent_hash,
fee_recipient: payload.fee_recipient,
state_root: payload.state_root,
receipts_root: payload.receipts_root,
logs_bloom: payload.logs_bloom.clone(),
prev_randao: payload.prev_randao,
block_number: payload.block_number,
gas_limit: payload.gas_limit,
gas_used: payload.gas_used,
timestamp: payload.timestamp,
extra_data: payload.extra_data.clone(),
base_fee_per_gas: payload.base_fee_per_gas,
block_hash: payload.block_hash,
transactions_root: payload.transactions.tree_hash_root(),
withdrawals_root: payload.withdrawals.tree_hash_root(),
blob_gas_used: payload.blob_gas_used,
excess_blob_gas: payload.excess_blob_gas,
}
}
}
impl<'a, E: EthSpec> From<&'a ExecutionPayloadFulu<E>> for ExecutionPayloadHeaderFulu<E> {
fn from(payload: &'a ExecutionPayloadFulu<E>) -> Self {
Self {
@@ -464,12 +414,6 @@ impl<'a, E: EthSpec> From<&'a Self> for ExecutionPayloadHeaderElectra<E> {
}
}
impl<'a, E: EthSpec> From<&'a Self> for ExecutionPayloadHeaderHeze<E> {
fn from(payload: &'a Self) -> Self {
payload.clone()
}
}
impl<'a, E: EthSpec> From<&'a Self> for ExecutionPayloadHeaderFulu<E> {
fn from(payload: &'a Self) -> Self {
payload.clone()
@@ -534,9 +478,6 @@ impl<E: EthSpec> ExecutionPayloadHeaderRefMut<'_, E> {
ExecutionPayloadHeaderRefMut::Electra(mut_ref) => {
*mut_ref = header.try_into()?;
}
ExecutionPayloadHeaderRefMut::Heze(mut_ref) => {
*mut_ref = header.try_into()?;
}
ExecutionPayloadHeaderRefMut::Fulu(mut_ref) => {
*mut_ref = header.try_into()?;
}
@@ -557,16 +498,6 @@ impl<E: EthSpec> TryFrom<ExecutionPayloadHeader<E>> for ExecutionPayloadHeaderEl
}
}
impl<E: EthSpec> TryFrom<ExecutionPayloadHeader<E>> for ExecutionPayloadHeaderHeze<E> {
type Error = BeaconStateError;
fn try_from(header: ExecutionPayloadHeader<E>) -> Result<Self, Self::Error> {
match header {
ExecutionPayloadHeader::Heze(execution_payload_header) => Ok(execution_payload_header),
_ => Err(BeaconStateError::IncorrectStateVariant),
}
}
}
impl<E: EthSpec> TryFrom<ExecutionPayloadHeader<E>> for ExecutionPayloadHeaderFulu<E> {
type Error = BeaconStateError;
fn try_from(header: ExecutionPayloadHeader<E>) -> Result<Self, Self::Error> {

View File

@@ -25,11 +25,14 @@ pub use execution_payload::{
pub use execution_payload_bid::{
ExecutionPayloadBid, ExecutionPayloadBidGloas, ExecutionPayloadBidHeze, ExecutionPayloadBidRef,
};
pub use execution_payload_envelope::ExecutionPayloadEnvelope;
pub use execution_payload_envelope::{
ExecutionPayloadEnvelope, ExecutionPayloadEnvelopeGloas, ExecutionPayloadEnvelopeHeze,
ExecutionPayloadEnvelopeRef,
};
pub use execution_payload_header::{
ExecutionPayloadHeader, ExecutionPayloadHeaderBellatrix, ExecutionPayloadHeaderCapella,
ExecutionPayloadHeaderDeneb, ExecutionPayloadHeaderElectra, ExecutionPayloadHeaderFulu,
ExecutionPayloadHeaderHeze, ExecutionPayloadHeaderRef, ExecutionPayloadHeaderRefMut,
ExecutionPayloadHeaderRef, ExecutionPayloadHeaderRefMut,
};
pub use execution_requests::{
ConsolidationRequests, DepositRequests, ExecutionRequests, RequestType, WithdrawalRequests,
@@ -39,14 +42,17 @@ pub use inclusion_list::{
};
pub use payload::{
AbstractExecPayload, BlindedPayload, BlindedPayloadBellatrix, BlindedPayloadCapella,
BlindedPayloadDeneb, BlindedPayloadElectra, BlindedPayloadFulu, BlindedPayloadHeze,
BlindedPayloadRef, BlockProductionVersion, BlockType, ExecPayload, FullPayload,
FullPayloadBellatrix, FullPayloadCapella, FullPayloadDeneb, FullPayloadElectra,
FullPayloadFulu, FullPayloadHeze, FullPayloadRef, OwnedExecPayload,
BlindedPayloadDeneb, BlindedPayloadElectra, BlindedPayloadFulu, BlindedPayloadRef,
BlockProductionVersion, BlockType, ExecPayload, FullPayload, FullPayloadBellatrix,
FullPayloadCapella, FullPayloadDeneb, FullPayloadElectra, FullPayloadFulu, FullPayloadRef,
OwnedExecPayload,
};
pub use signed_bls_to_execution_change::SignedBlsToExecutionChange;
pub use signed_execution_payload_bid::{
SignedExecutionPayloadBid, SignedExecutionPayloadBidGloas, SignedExecutionPayloadBidHeze,
SignedExecutionPayloadBidRef,
};
pub use signed_execution_payload_envelope::SignedExecutionPayloadEnvelope;
pub use signed_execution_payload_envelope::{
SignedExecutionPayloadEnvelope, SignedExecutionPayloadEnvelopeGloas,
SignedExecutionPayloadEnvelopeHeze, SignedExecutionPayloadEnvelopeRef,
};

View File

@@ -17,7 +17,7 @@ use crate::{
ExecutionPayloadDeneb, ExecutionPayloadElectra, ExecutionPayloadFulu,
ExecutionPayloadHeader, ExecutionPayloadHeaderBellatrix, ExecutionPayloadHeaderCapella,
ExecutionPayloadHeaderDeneb, ExecutionPayloadHeaderElectra, ExecutionPayloadHeaderFulu,
ExecutionPayloadHeaderHeze, ExecutionPayloadHeze, ExecutionPayloadRef, Transactions,
ExecutionPayloadRef, Transactions,
},
fork::ForkName,
map_execution_payload_into_blinded_payload, map_execution_payload_into_full_payload,
@@ -119,7 +119,6 @@ pub trait AbstractExecPayload<E: EthSpec>:
+ TryInto<Self::Deneb>
+ TryInto<Self::Electra>
+ TryInto<Self::Fulu>
+ TryInto<Self::Heze>
+ Sync
{
type Ref<'a>: ExecPayload<E>
@@ -128,8 +127,7 @@ pub trait AbstractExecPayload<E: EthSpec>:
+ From<&'a Self::Capella>
+ From<&'a Self::Deneb>
+ From<&'a Self::Electra>
+ From<&'a Self::Fulu>
+ From<&'a Self::Heze>;
+ From<&'a Self::Fulu>;
type Bellatrix: OwnedExecPayload<E>
+ Into<Self>
@@ -156,15 +154,10 @@ pub trait AbstractExecPayload<E: EthSpec>:
+ for<'a> From<Cow<'a, ExecutionPayloadFulu<E>>>
+ TryFrom<ExecutionPayloadHeaderFulu<E>>
+ Sync;
type Heze: OwnedExecPayload<E>
+ Into<Self>
+ for<'a> From<Cow<'a, ExecutionPayloadHeze<E>>>
+ TryFrom<ExecutionPayloadHeaderHeze<E>>
+ Sync;
}
#[superstruct(
variants(Bellatrix, Capella, Deneb, Electra, Heze, Fulu),
variants(Bellatrix, Capella, Deneb, Electra, Fulu),
variant_attributes(
derive(
Debug,
@@ -223,8 +216,6 @@ pub struct FullPayload<E: EthSpec> {
pub execution_payload: ExecutionPayloadDeneb<E>,
#[superstruct(only(Electra), partial_getter(rename = "execution_payload_electra"))]
pub execution_payload: ExecutionPayloadElectra<E>,
#[superstruct(only(Heze), partial_getter(rename = "execution_payload_heze"))]
pub execution_payload: ExecutionPayloadHeze<E>,
#[superstruct(only(Fulu), partial_getter(rename = "execution_payload_fulu"))]
pub execution_payload: ExecutionPayloadFulu<E>,
}
@@ -338,7 +329,6 @@ impl<E: EthSpec> ExecPayload<E> for FullPayload<E> {
FullPayload::Deneb(inner) => Ok(inner.execution_payload.withdrawals.tree_hash_root()),
FullPayload::Electra(inner) => Ok(inner.execution_payload.withdrawals.tree_hash_root()),
FullPayload::Fulu(inner) => Ok(inner.execution_payload.withdrawals.tree_hash_root()),
FullPayload::Heze(inner) => Ok(inner.execution_payload.withdrawals.tree_hash_root()),
}
}
@@ -350,7 +340,6 @@ impl<E: EthSpec> ExecPayload<E> for FullPayload<E> {
FullPayload::Deneb(inner) => Ok(inner.execution_payload.blob_gas_used),
FullPayload::Electra(inner) => Ok(inner.execution_payload.blob_gas_used),
FullPayload::Fulu(inner) => Ok(inner.execution_payload.blob_gas_used),
FullPayload::Heze(inner) => Ok(inner.execution_payload.blob_gas_used),
}
}
@@ -482,7 +471,6 @@ impl<E: EthSpec> ExecPayload<E> for FullPayloadRef<'_, E> {
FullPayloadRef::Electra(inner) => {
Ok(inner.execution_payload.withdrawals.tree_hash_root())
}
FullPayloadRef::Heze(inner) => Ok(inner.execution_payload.withdrawals.tree_hash_root()),
FullPayloadRef::Fulu(inner) => Ok(inner.execution_payload.withdrawals.tree_hash_root()),
}
}
@@ -494,7 +482,6 @@ impl<E: EthSpec> ExecPayload<E> for FullPayloadRef<'_, E> {
}
FullPayloadRef::Deneb(inner) => Ok(inner.execution_payload.blob_gas_used),
FullPayloadRef::Electra(inner) => Ok(inner.execution_payload.blob_gas_used),
FullPayloadRef::Heze(inner) => Ok(inner.execution_payload.blob_gas_used),
FullPayloadRef::Fulu(inner) => Ok(inner.execution_payload.blob_gas_used),
}
}
@@ -518,7 +505,6 @@ impl<E: EthSpec> AbstractExecPayload<E> for FullPayload<E> {
type Capella = FullPayloadCapella<E>;
type Deneb = FullPayloadDeneb<E>;
type Electra = FullPayloadElectra<E>;
type Heze = FullPayloadHeze<E>;
type Fulu = FullPayloadFulu<E>;
}
@@ -538,7 +524,7 @@ impl<E: EthSpec> TryFrom<ExecutionPayloadHeader<E>> for FullPayload<E> {
}
#[superstruct(
variants(Bellatrix, Capella, Deneb, Electra, Heze, Fulu),
variants(Bellatrix, Capella, Deneb, Electra, Fulu),
variant_attributes(
derive(
Debug,
@@ -596,8 +582,6 @@ pub struct BlindedPayload<E: EthSpec> {
pub execution_payload_header: ExecutionPayloadHeaderDeneb<E>,
#[superstruct(only(Electra), partial_getter(rename = "execution_payload_electra"))]
pub execution_payload_header: ExecutionPayloadHeaderElectra<E>,
#[superstruct(only(Heze), partial_getter(rename = "execution_payload_heze"))]
pub execution_payload_header: ExecutionPayloadHeaderHeze<E>,
#[superstruct(only(Fulu), partial_getter(rename = "execution_payload_fulu"))]
pub execution_payload_header: ExecutionPayloadHeaderFulu<E>,
}
@@ -689,7 +673,6 @@ impl<E: EthSpec> ExecPayload<E> for BlindedPayload<E> {
BlindedPayload::Deneb(inner) => Ok(inner.execution_payload_header.withdrawals_root),
BlindedPayload::Electra(inner) => Ok(inner.execution_payload_header.withdrawals_root),
BlindedPayload::Fulu(inner) => Ok(inner.execution_payload_header.withdrawals_root),
BlindedPayload::Heze(inner) => Ok(inner.execution_payload_header.withdrawals_root),
}
}
@@ -701,7 +684,6 @@ impl<E: EthSpec> ExecPayload<E> for BlindedPayload<E> {
BlindedPayload::Deneb(inner) => Ok(inner.execution_payload_header.blob_gas_used),
BlindedPayload::Electra(inner) => Ok(inner.execution_payload_header.blob_gas_used),
BlindedPayload::Fulu(inner) => Ok(inner.execution_payload_header.blob_gas_used),
BlindedPayload::Heze(inner) => Ok(inner.execution_payload_header.blob_gas_used),
}
}
@@ -800,7 +782,6 @@ impl<'b, E: EthSpec> ExecPayload<E> for BlindedPayloadRef<'b, E> {
BlindedPayloadRef::Electra(inner) => {
Ok(inner.execution_payload_header.withdrawals_root)
}
BlindedPayloadRef::Heze(inner) => Ok(inner.execution_payload_header.withdrawals_root),
BlindedPayloadRef::Fulu(inner) => Ok(inner.execution_payload_header.withdrawals_root),
}
}
@@ -812,7 +793,6 @@ impl<'b, E: EthSpec> ExecPayload<E> for BlindedPayloadRef<'b, E> {
}
BlindedPayloadRef::Deneb(inner) => Ok(inner.execution_payload_header.blob_gas_used),
BlindedPayloadRef::Electra(inner) => Ok(inner.execution_payload_header.blob_gas_used),
BlindedPayloadRef::Heze(inner) => Ok(inner.execution_payload_header.blob_gas_used),
BlindedPayloadRef::Fulu(inner) => Ok(inner.execution_payload_header.blob_gas_used),
}
}
@@ -1118,14 +1098,6 @@ impl_exec_payload_for_fork!(
ExecutionPayloadElectra,
Electra
);
impl_exec_payload_for_fork!(
BlindedPayloadHeze,
FullPayloadHeze,
ExecutionPayloadHeaderHeze,
ExecutionPayloadHeze,
Heze
);
impl_exec_payload_for_fork!(
BlindedPayloadFulu,
FullPayloadFulu,
@@ -1140,7 +1112,6 @@ impl<E: EthSpec> AbstractExecPayload<E> for BlindedPayload<E> {
type Capella = BlindedPayloadCapella<E>;
type Deneb = BlindedPayloadDeneb<E>;
type Electra = BlindedPayloadElectra<E>;
type Heze = BlindedPayloadHeze<E>;
type Fulu = BlindedPayloadFulu<E>;
}
@@ -1178,11 +1149,6 @@ impl<E: EthSpec> From<ExecutionPayloadHeader<E>> for BlindedPayload<E> {
execution_payload_header,
})
}
ExecutionPayloadHeader::Heze(execution_payload_header) => {
Self::Heze(BlindedPayloadHeze {
execution_payload_header,
})
}
ExecutionPayloadHeader::Fulu(execution_payload_header) => {
Self::Fulu(BlindedPayloadFulu {
execution_payload_header,
@@ -1207,9 +1173,6 @@ impl<E: EthSpec> From<BlindedPayload<E>> for ExecutionPayloadHeader<E> {
BlindedPayload::Electra(blinded_payload) => {
ExecutionPayloadHeader::Electra(blinded_payload.execution_payload_header)
}
BlindedPayload::Heze(blinded_payload) => {
ExecutionPayloadHeader::Heze(blinded_payload.execution_payload_header)
}
BlindedPayload::Fulu(blinded_payload) => {
ExecutionPayloadHeader::Fulu(blinded_payload.execution_payload_header)
}

View File

@@ -1,48 +1,97 @@
use crate::execution::{
ExecutionPayloadEnvelopeGloas, ExecutionPayloadEnvelopeHeze, ExecutionPayloadEnvelopeRef,
};
use crate::fork::ForkVersionDecode;
use crate::state::BeaconStateError;
use crate::test_utils::TestRandom;
use crate::{
BeaconState, BeaconStateError, ChainSpec, Domain, Epoch, EthSpec, ExecutionBlockHash,
ExecutionPayloadEnvelope, Fork, ForkName, Hash256, SignedRoot, Slot,
consts::gloas::BUILDER_INDEX_SELF_BUILD,
BeaconState, ChainSpec, Domain, Epoch, EthSpec, ExecutionBlockHash, Fork, ForkName, Hash256,
SignedRoot, Slot, consts::gloas::BUILDER_INDEX_SELF_BUILD,
};
use bls::{PublicKey, Signature};
use context_deserialize::context_deserialize;
use context_deserialize::{ContextDeserialize, context_deserialize};
use educe::Educe;
use serde::{Deserialize, Serialize};
use serde::{Deserialize, Deserializer, Serialize};
use ssz::Encode;
use ssz_derive::{Decode, Encode};
use superstruct::superstruct;
use test_random_derive::TestRandom;
use tree_hash_derive::TreeHash;
#[derive(Debug, Clone, Serialize, Encode, Decode, Deserialize, TestRandom, TreeHash, Educe)]
#[educe(PartialEq, Hash(bound(E: EthSpec)))]
#[serde(bound = "E: EthSpec")]
#[context_deserialize(ForkName)]
#[superstruct(
variants(Gloas, Heze),
variant_attributes(
derive(
Debug,
Clone,
Serialize,
Deserialize,
Encode,
Decode,
TestRandom,
TreeHash,
Educe,
),
educe(PartialEq, Hash(bound(E: EthSpec))),
context_deserialize(ForkName),
serde(bound = "E: EthSpec"),
cfg_attr(
feature = "arbitrary",
derive(arbitrary::Arbitrary),
arbitrary(bound = "E: EthSpec"),
),
),
cast_error(
ty = "BeaconStateError",
expr = "BeaconStateError::IncorrectStateVariant"
),
partial_getter_error(
ty = "BeaconStateError",
expr = "BeaconStateError::IncorrectStateVariant"
),
map_ref_into(ExecutionPayloadEnvelopeRef)
)]
#[cfg_attr(
feature = "arbitrary",
derive(arbitrary::Arbitrary),
arbitrary(bound = "E: EthSpec")
)]
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Encode, TreeHash)]
#[serde(untagged)]
#[tree_hash(enum_behaviour = "transparent")]
#[ssz(enum_behaviour = "transparent")]
#[serde(bound = "E: EthSpec", deny_unknown_fields)]
pub struct SignedExecutionPayloadEnvelope<E: EthSpec> {
#[superstruct(flatten)]
pub message: ExecutionPayloadEnvelope<E>,
pub signature: Signature,
}
impl<E: EthSpec> SignedExecutionPayloadEnvelope<E> {
/// Returns the minimum SSZ-encoded size (all variable-length fields empty).
pub fn min_size() -> usize {
Self {
message: ExecutionPayloadEnvelope::empty(),
signature: Signature::empty(),
pub fn message(&self) -> ExecutionPayloadEnvelopeRef<'_, E> {
match self {
Self::Gloas(inner) => ExecutionPayloadEnvelopeRef::Gloas(&inner.message),
Self::Heze(inner) => ExecutionPayloadEnvelopeRef::Heze(&inner.message),
}
.as_ssz_bytes()
.len()
}
/// Returns the maximum SSZ-encoded size.
pub fn min_size() -> usize {
SignedExecutionPayloadEnvelopeGloas::<E>::empty()
.as_ssz_bytes()
.len()
}
#[allow(clippy::arithmetic_side_effects)]
pub fn max_size() -> usize {
// Signature is fixed-size, so the variable-length delta is entirely from the envelope.
Self::min_size() + ExecutionPayloadEnvelope::<E>::max_size()
- ExecutionPayloadEnvelope::<E>::min_size()
Self::min_size() + ExecutionPayloadEnvelopeGloas::<E>::max_size()
- ExecutionPayloadEnvelopeGloas::<E>::min_size()
}
pub fn slot(&self) -> Slot {
self.message.slot()
match self {
Self::Gloas(inner) => inner.message.payload.slot_number,
Self::Heze(inner) => inner.message.payload.slot_number,
}
}
pub fn epoch(&self) -> Epoch {
@@ -50,11 +99,24 @@ impl<E: EthSpec> SignedExecutionPayloadEnvelope<E> {
}
pub fn beacon_block_root(&self) -> Hash256 {
self.message.beacon_block_root
match self {
Self::Gloas(inner) => inner.message.beacon_block_root,
Self::Heze(inner) => inner.message.beacon_block_root,
}
}
pub fn block_hash(&self) -> ExecutionBlockHash {
self.message.payload.block_hash
match self {
Self::Gloas(inner) => inner.message.payload.block_hash,
Self::Heze(inner) => inner.message.payload.block_hash,
}
}
pub fn builder_index(&self) -> u64 {
match self {
Self::Gloas(inner) => inner.message.builder_index,
Self::Heze(inner) => inner.message.builder_index,
}
}
/// Verify `self.signature`.
@@ -65,8 +127,6 @@ impl<E: EthSpec> SignedExecutionPayloadEnvelope<E> {
genesis_validators_root: Hash256,
spec: &ChainSpec,
) -> bool {
// Signed envelopes using the new BeaconBuilder domain per the spec:
// https://github.com/ethereum/consensus-specs/blob/v1.7.0-alpha.1/specs/gloas/beacon-chain.md#new-verify_execution_payload_envelope_signature
let domain = spec.get_domain(
self.epoch(),
Domain::BeaconBuilder,
@@ -74,9 +134,8 @@ impl<E: EthSpec> SignedExecutionPayloadEnvelope<E> {
genesis_validators_root,
);
let message = self.message.signing_root(domain);
self.signature.verify(pubkey, message)
let message = self.message().signing_root(domain);
self.signature().verify(pubkey, message)
}
/// Verify `self.signature` using keys drawn from the beacon state.
@@ -85,7 +144,7 @@ impl<E: EthSpec> SignedExecutionPayloadEnvelope<E> {
state: &BeaconState<E>,
spec: &ChainSpec,
) -> Result<bool, BeaconStateError> {
let builder_index = self.message.builder_index;
let builder_index = self.builder_index();
let pubkey_bytes = if builder_index == BUILDER_INDEX_SELF_BUILD {
let validator_index = state.latest_block_header().proposer_index;
@@ -94,12 +153,8 @@ impl<E: EthSpec> SignedExecutionPayloadEnvelope<E> {
state.get_builder(builder_index)?.pubkey
};
// TODO(gloas): Could use pubkey cache on state here, but it probably isn't worth
// it because this function is rarely used. Almost always the envelope should be signature
// verified prior to consensus code running.
let pubkey = pubkey_bytes.decompress()?;
// Ensure the state's epoch matches the message's epoch before determining the Fork.
if self.epoch() != state.current_epoch() {
return Err(BeaconStateError::SignedEnvelopeIncorrectEpoch {
state_epoch: state.current_epoch(),
@@ -116,10 +171,79 @@ impl<E: EthSpec> SignedExecutionPayloadEnvelope<E> {
}
}
impl<E: EthSpec> SignedExecutionPayloadEnvelopeGloas<E> {
pub fn empty() -> Self {
Self {
message: ExecutionPayloadEnvelopeGloas::empty(),
signature: Signature::empty(),
}
}
}
impl<E: EthSpec> SignedExecutionPayloadEnvelopeHeze<E> {
pub fn empty() -> Self {
Self {
message: ExecutionPayloadEnvelopeHeze::empty(),
signature: Signature::empty(),
}
}
}
impl<E: EthSpec> ForkVersionDecode for SignedExecutionPayloadEnvelope<E> {
fn from_ssz_bytes_by_fork(bytes: &[u8], fork_name: ForkName) -> Result<Self, ssz::DecodeError> {
match fork_name {
ForkName::Gloas => {
<SignedExecutionPayloadEnvelopeGloas<E> as ssz::Decode>::from_ssz_bytes(bytes)
.map(Self::Gloas)
}
ForkName::Heze => {
<SignedExecutionPayloadEnvelopeHeze<E> as ssz::Decode>::from_ssz_bytes(bytes)
.map(Self::Heze)
}
_ => Err(ssz::DecodeError::BytesInvalid(format!(
"unsupported fork for SignedExecutionPayloadEnvelope: {fork_name}",
))),
}
}
}
impl<'de, E: EthSpec> ContextDeserialize<'de, ForkName> for SignedExecutionPayloadEnvelope<E> {
fn context_deserialize<D>(deserializer: D, context: ForkName) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let convert_err = |e| {
serde::de::Error::custom(format!(
"SignedExecutionPayloadEnvelope failed to deserialize: {:?}",
e
))
};
match context {
ForkName::Heze => Ok(Self::Heze(
Deserialize::deserialize(deserializer).map_err(convert_err)?,
)),
ForkName::Gloas => Ok(Self::Gloas(
Deserialize::deserialize(deserializer).map_err(convert_err)?,
)),
_ => Err(serde::de::Error::custom(format!(
"SignedExecutionPayloadEnvelope failed to deserialize: unsupported fork '{}'",
context
))),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::MainnetEthSpec;
ssz_and_tree_hash_tests!(SignedExecutionPayloadEnvelope<MainnetEthSpec>);
mod gloas {
use super::*;
ssz_and_tree_hash_tests!(SignedExecutionPayloadEnvelopeGloas<MainnetEthSpec>);
}
mod heze {
use super::*;
ssz_and_tree_hash_tests!(SignedExecutionPayloadEnvelopeHeze<MainnetEthSpec>);
}
}