From 7dac892c7163f1b4d58a362c3da24421865ec082 Mon Sep 17 00:00:00 2001 From: shane-moore Date: Tue, 29 Jul 2025 20:14:48 -0700 Subject: [PATCH] eip-7732 containers --- .../types/src/builder_pending_payment.rs | 34 ++++++ .../types/src/builder_pending_withdrawal.rs | 38 +++++++ consensus/types/src/chain_spec.rs | 42 +++++++ consensus/types/src/eth_spec.rs | 31 +++++- consensus/types/src/execution_bid.rs | 47 ++++++++ .../types/src/execution_payload_envelope.rs | 103 ++++++++++++++++++ .../types/src/indexed_payload_attestation.rs | 41 +++++++ consensus/types/src/lib.rs | 27 ++++- consensus/types/src/payload_attestation.rs | 36 ++++++ .../types/src/payload_attestation_data.rs | 27 +++++ .../types/src/payload_attestation_message.rs | 33 ++++++ consensus/types/src/signed_execution_bid.rs | 42 +++++++ .../src/signed_execution_payload_envelope.rs | 91 ++++++++++++++++ 13 files changed, 589 insertions(+), 3 deletions(-) create mode 100644 consensus/types/src/builder_pending_payment.rs create mode 100644 consensus/types/src/builder_pending_withdrawal.rs create mode 100644 consensus/types/src/execution_bid.rs create mode 100644 consensus/types/src/execution_payload_envelope.rs create mode 100644 consensus/types/src/indexed_payload_attestation.rs create mode 100644 consensus/types/src/payload_attestation.rs create mode 100644 consensus/types/src/payload_attestation_data.rs create mode 100644 consensus/types/src/payload_attestation_message.rs create mode 100644 consensus/types/src/signed_execution_bid.rs create mode 100644 consensus/types/src/signed_execution_payload_envelope.rs diff --git a/consensus/types/src/builder_pending_payment.rs b/consensus/types/src/builder_pending_payment.rs new file mode 100644 index 0000000000..8fa062ddea --- /dev/null +++ b/consensus/types/src/builder_pending_payment.rs @@ -0,0 +1,34 @@ +use crate::test_utils::TestRandom; +use crate::*; +use serde::{Deserialize, Serialize}; +use ssz_derive::{Decode, Encode}; +use test_random_derive::TestRandom; +use tree_hash_derive::TreeHash; + +#[derive( + Debug, + PartialEq, + Eq, + Hash, + Clone, + Serialize, + Deserialize, + Encode, + Decode, + TreeHash, + TestRandom, +)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +#[context_deserialize(ForkName)] +pub struct BuilderPendingPayment { + #[serde(with = "serde_utils::quoted_u64")] + pub weight: u64, + pub withdrawal: BuilderPendingWithdrawal, +} + +#[cfg(test)] +mod tests { + use super::*; + + ssz_and_tree_hash_tests!(BuilderPendingPayment); +} diff --git a/consensus/types/src/builder_pending_withdrawal.rs b/consensus/types/src/builder_pending_withdrawal.rs new file mode 100644 index 0000000000..707f8fcdcb --- /dev/null +++ b/consensus/types/src/builder_pending_withdrawal.rs @@ -0,0 +1,38 @@ +use crate::test_utils::TestRandom; +use crate::*; +use serde::{Deserialize, Serialize}; +use ssz_derive::{Decode, Encode}; +use test_random_derive::TestRandom; +use tree_hash_derive::TreeHash; + +#[derive( + Debug, + PartialEq, + Eq, + Hash, + Clone, + Serialize, + Deserialize, + Encode, + Decode, + TreeHash, + TestRandom, +)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +#[context_deserialize(ForkName)] +pub struct BuilderPendingWithdrawal { + #[serde(with = "serde_utils::address_hex")] + pub fee_recipient: Address, + #[serde(with = "serde_utils::quoted_u64")] + pub amount: u64, + #[serde(with = "serde_utils::quoted_u64")] + pub builder_index: u64, + pub withdrawable_epoch: Epoch, +} + +#[cfg(test)] +mod tests { + use super::*; + + ssz_and_tree_hash_tests!(BuilderPendingWithdrawal); +} diff --git a/consensus/types/src/chain_spec.rs b/consensus/types/src/chain_spec.rs index 709b4f28fe..b2a93c24d8 100644 --- a/consensus/types/src/chain_spec.rs +++ b/consensus/types/src/chain_spec.rs @@ -28,6 +28,8 @@ pub enum Domain { SyncCommittee, ContributionAndProof, SyncCommitteeSelectionProof, + BeaconBuilder, + PTCAttester, ApplicationMask(ApplicationDomain), } @@ -81,6 +83,7 @@ pub struct ChainSpec { pub bls_withdrawal_prefix_byte: u8, pub eth1_address_withdrawal_prefix_byte: u8, pub compounding_withdrawal_prefix_byte: u8, + pub builder_withdrawal_prefix_byte: u8, /* * Time parameters @@ -113,6 +116,8 @@ pub struct ChainSpec { pub(crate) domain_voluntary_exit: u32, pub(crate) domain_selection_proof: u32, pub(crate) domain_aggregate_and_proof: u32, + pub(crate) domain_beacon_builder: u32, + pub(crate) domain_ptc_attester: u32, /* * Fork choice @@ -214,6 +219,8 @@ pub struct ChainSpec { pub gloas_fork_version: [u8; 4], /// The Gloas fork epoch is optional, with `None` representing "Gloas never happens". pub gloas_fork_epoch: Option, + pub builder_payment_threshold_numerator: u64, + pub builder_payment_threshold_denominator: u64, /* * Networking @@ -513,6 +520,8 @@ impl ChainSpec { Domain::VoluntaryExit => self.domain_voluntary_exit, Domain::SelectionProof => self.domain_selection_proof, Domain::AggregateAndProof => self.domain_aggregate_and_proof, + Domain::BeaconBuilder => self.domain_beacon_builder, + Domain::PTCAttester => self.domain_ptc_attester, Domain::SyncCommittee => self.domain_sync_committee, Domain::ContributionAndProof => self.domain_contribution_and_proof, Domain::SyncCommitteeSelectionProof => self.domain_sync_committee_selection_proof, @@ -922,6 +931,7 @@ impl ChainSpec { bls_withdrawal_prefix_byte: 0x00, eth1_address_withdrawal_prefix_byte: 0x01, compounding_withdrawal_prefix_byte: 0x02, + builder_withdrawal_prefix_byte: 0x03, /* * Time parameters @@ -955,6 +965,8 @@ impl ChainSpec { domain_voluntary_exit: 4, domain_selection_proof: 5, domain_aggregate_and_proof: 6, + domain_beacon_builder: 0x1B, + domain_ptc_attester: 0x0C, /* * Fork choice @@ -1075,6 +1087,8 @@ impl ChainSpec { */ gloas_fork_version: [0x07, 0x00, 0x00, 0x00], gloas_fork_epoch: None, + builder_payment_threshold_numerator: 6, + builder_payment_threshold_denominator: 10, /* * Network specific @@ -1268,6 +1282,7 @@ impl ChainSpec { bls_withdrawal_prefix_byte: 0x00, eth1_address_withdrawal_prefix_byte: 0x01, compounding_withdrawal_prefix_byte: 0x02, + builder_withdrawal_prefix_byte: 0x03, /* * Time parameters @@ -1301,6 +1316,8 @@ impl ChainSpec { domain_voluntary_exit: 4, domain_selection_proof: 5, domain_aggregate_and_proof: 6, + domain_beacon_builder: 0x1B, + domain_ptc_attester: 0x0C, /* * Fork choice @@ -1421,6 +1438,8 @@ impl ChainSpec { */ gloas_fork_version: [0x07, 0x00, 0x00, 0x64], gloas_fork_epoch: None, + builder_payment_threshold_numerator: 6, + builder_payment_threshold_denominator: 10, /* * Network specific @@ -1690,6 +1709,12 @@ pub struct Config { #[serde(serialize_with = "serialize_fork_epoch")] #[serde(deserialize_with = "deserialize_fork_epoch")] pub gloas_fork_epoch: Option>, + #[serde(default = "default_builder_payment_threshold_numerator")] + #[serde(with = "serde_utils::quoted_u64")] + pub builder_payment_threshold_numerator: u64, + #[serde(default = "default_builder_payment_threshold_denominator")] + #[serde(with = "serde_utils::quoted_u64")] + pub builder_payment_threshold_denominator: u64, #[serde(with = "serde_utils::quoted_u64")] seconds_per_slot: u64, @@ -1853,6 +1878,14 @@ fn default_gloas_fork_version() -> [u8; 4] { [0xff, 0xff, 0xff, 0xff] } +fn default_builder_payment_threshold_numerator() -> u64 { + 6 +} + +fn default_builder_payment_threshold_denominator() -> u64 { + 10 +} + /// Placeholder value: 2^256-2^10 (115792089237316195423570985008687907853269984665640564039457584007913129638912). /// /// Taken from https://github.com/ethereum/consensus-specs/blob/d5e4828aecafaf1c57ef67a5f23c4ae7b08c5137/configs/mainnet.yaml#L15-L16 @@ -2154,6 +2187,9 @@ impl Config { .gloas_fork_epoch .map(|epoch| MaybeQuoted { value: epoch }), + builder_payment_threshold_numerator: spec.builder_payment_threshold_numerator, + builder_payment_threshold_denominator: spec.builder_payment_threshold_denominator, + seconds_per_slot: spec.seconds_per_slot, seconds_per_eth1_block: spec.seconds_per_eth1_block, min_validator_withdrawability_delay: spec.min_validator_withdrawability_delay, @@ -2245,6 +2281,8 @@ impl Config { fulu_fork_version, gloas_fork_version, gloas_fork_epoch, + builder_payment_threshold_numerator, + builder_payment_threshold_denominator, seconds_per_slot, seconds_per_eth1_block, min_validator_withdrawability_delay, @@ -2318,6 +2356,8 @@ impl Config { fulu_fork_version, gloas_fork_version, gloas_fork_epoch: gloas_fork_epoch.map(|q| q.value), + builder_payment_threshold_numerator, + builder_payment_threshold_denominator, seconds_per_slot, seconds_per_eth1_block, min_validator_withdrawability_delay, @@ -2443,6 +2483,8 @@ mod tests { spec.domain_aggregate_and_proof, &spec, ); + test_domain(Domain::BeaconBuilder, spec.domain_beacon_builder, &spec); + test_domain(Domain::PTCAttester, spec.domain_ptc_attester, &spec); test_domain(Domain::SyncCommittee, spec.domain_sync_committee, &spec); // The builder domain index is zero diff --git a/consensus/types/src/eth_spec.rs b/consensus/types/src/eth_spec.rs index e9a291f6c7..0f1e03a73d 100644 --- a/consensus/types/src/eth_spec.rs +++ b/consensus/types/src/eth_spec.rs @@ -75,6 +75,7 @@ pub trait EthSpec: 'static + Default + Sync + Send + Clone + Debug + PartialEq + type EpochsPerSlashingsVector: Unsigned + Clone + Sync + Send + Debug + PartialEq; type HistoricalRootsLimit: Unsigned + Clone + Sync + Send + Debug + PartialEq; type ValidatorRegistryLimit: Unsigned + Clone + Sync + Send + Debug + PartialEq; + type BuilderPendingWithdrawalsLimit: Unsigned + Clone + Sync + Send + Debug + PartialEq; /* * Max operations per block */ @@ -165,6 +166,12 @@ pub trait EthSpec: 'static + Default + Sync + Send + Clone + Debug + PartialEq + type MaxWithdrawalRequestsPerPayload: Unsigned + Clone + Sync + Send + Debug + PartialEq; type MaxPendingDepositsPerEpoch: Unsigned + Clone + Sync + Send + Debug + PartialEq; + /* + * New in Gloas + */ + type PTCSize: Unsigned + Clone + Sync + Send + Debug + PartialEq; + type MaxPayloadAttestations: Unsigned + Clone + Sync + Send + Debug + PartialEq; + fn default_spec() -> ChainSpec; fn spec_name() -> EthSpecId; @@ -346,6 +353,11 @@ pub trait EthSpec: 'static + Default + Sync + Send + Clone + Debug + PartialEq + Self::PendingConsolidationsLimit::to_usize() } + /// Returns the `BUILDER_PENDING_WITHDRAWALS_LIMIT` constant for this specification. + fn builder_pending_withdrawals_limit() -> usize { + Self::BuilderPendingWithdrawalsLimit::to_usize() + } + /// Returns the `MAX_CONSOLIDATION_REQUESTS_PER_PAYLOAD` constant for this specification. fn max_consolidation_requests_per_payload() -> usize { Self::MaxConsolidationRequestsPerPayload::to_usize() @@ -391,6 +403,14 @@ pub trait EthSpec: 'static + Default + Sync + Send + Clone + Debug + PartialEq + fn proposer_lookahead_slots() -> usize { Self::ProposerLookaheadSlots::to_usize() } + + fn ptc_size() -> usize { + Self::PTCSize::to_usize() + } + + fn max_payload_attestations() -> usize { + Self::MaxPayloadAttestations::to_usize() + } } /// Macro to inherit some type values from another EthSpec. @@ -420,6 +440,7 @@ impl EthSpec for MainnetEthSpec { type EpochsPerSlashingsVector = U8192; type HistoricalRootsLimit = U16777216; type ValidatorRegistryLimit = U1099511627776; + type BuilderPendingWithdrawalsLimit = U1048576; type MaxProposerSlashings = U16; type MaxAttesterSlashings = U2; type MaxAttestations = U128; @@ -460,6 +481,8 @@ impl EthSpec for MainnetEthSpec { type MaxAttestationsElectra = U8; type MaxWithdrawalRequestsPerPayload = U16; type MaxPendingDepositsPerEpoch = U16; + type PTCSize = U64; // todo: verify if needs to be U512 for some reason like in Mark's OG implementation + type MaxPayloadAttestations = U2; fn default_spec() -> ChainSpec { ChainSpec::mainnet() @@ -511,6 +534,7 @@ impl EthSpec for MinimalEthSpec { GenesisEpoch, HistoricalRootsLimit, ValidatorRegistryLimit, + BuilderPendingWithdrawalsLimit, MaxProposerSlashings, MaxAttesterSlashings, MaxAttestations, @@ -530,7 +554,9 @@ impl EthSpec for MinimalEthSpec { MaxAttesterSlashingsElectra, MaxAttestationsElectra, MaxDepositRequestsPerPayload, - MaxWithdrawalRequestsPerPayload + MaxWithdrawalRequestsPerPayload, + PTCSize, + MaxPayloadAttestations }); fn default_spec() -> ChainSpec { @@ -561,6 +587,7 @@ impl EthSpec for GnosisEthSpec { type EpochsPerSlashingsVector = U8192; type HistoricalRootsLimit = U16777216; type ValidatorRegistryLimit = U1099511627776; + type BuilderPendingWithdrawalsLimit = U1048576; type MaxProposerSlashings = U16; type MaxAttesterSlashings = U2; type MaxAttestations = U128; @@ -601,6 +628,8 @@ impl EthSpec for GnosisEthSpec { type CellsPerExtBlob = U128; type NumberOfColumns = U128; type ProposerLookaheadSlots = U32; // Derived from (MIN_SEED_LOOKAHEAD + 1) * SLOTS_PER_EPOCH + type PTCSize = U64; // todo: verify if needs to be U512 for some reason like in Mark's OG implementation + type MaxPayloadAttestations = U2; fn default_spec() -> ChainSpec { ChainSpec::gnosis() diff --git a/consensus/types/src/execution_bid.rs b/consensus/types/src/execution_bid.rs new file mode 100644 index 0000000000..9bbabf10ca --- /dev/null +++ b/consensus/types/src/execution_bid.rs @@ -0,0 +1,47 @@ +use crate::{test_utils::TestRandom, *}; +use derivative::Derivative; +use serde::{Deserialize, Serialize}; +use ssz_derive::{Decode, Encode}; +use test_random_derive::TestRandom; +use tree_hash_derive::TreeHash; + +#[derive( + Default, + Debug, + Clone, + Serialize, + Encode, + Decode, + Deserialize, + TreeHash, + Derivative, + TestRandom, +)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +#[derivative(PartialEq, Hash)] +#[context_deserialize(ForkName)] +// This is what Potuz' spec calls an `ExecutionPayload` even though it's clearly a bid. +pub struct ExecutionBid { + pub parent_block_hash: ExecutionBlockHash, + pub parent_block_root: Hash256, + pub block_hash: ExecutionBlockHash, + #[serde(with = "serde_utils::address_hex")] + pub fee_recipient: Address, // todo(eip-7732): verify if this needs address_hex serialization + #[serde(with = "serde_utils::quoted_u64")] + pub gas_limit: u64, + #[serde(with = "serde_utils::quoted_u64")] + pub builder_index: u64, + pub slot: Slot, + #[serde(with = "serde_utils::quoted_u64")] + pub value: u64, + pub blob_kzg_commitments_root: Hash256, +} + +impl SignedRoot for ExecutionBid {} + +#[cfg(test)] +mod tests { + use super::*; + + ssz_and_tree_hash_tests!(ExecutionBid); +} diff --git a/consensus/types/src/execution_payload_envelope.rs b/consensus/types/src/execution_payload_envelope.rs new file mode 100644 index 0000000000..50a7ac9729 --- /dev/null +++ b/consensus/types/src/execution_payload_envelope.rs @@ -0,0 +1,103 @@ +use crate::test_utils::TestRandom; +use crate::*; +use beacon_block_body::KzgCommitments; +use derivative::Derivative; +use serde::de::{Deserializer, Error as _}; +use serde::{Deserialize, Serialize}; +use ssz_derive::{Decode, Encode}; +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(Gloas, NextFork), + variant_attributes( + derive( + Debug, + Clone, + Serialize, + Deserialize, + Encode, + Decode, + TreeHash, + TestRandom, + Derivative + ), + cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary)), + derivative(PartialEq, Hash(bound = "E: EthSpec")), + serde(bound = "E: EthSpec", deny_unknown_fields), + cfg_attr(feature = "arbitrary", 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") +)] +#[derive(Debug, Clone, Serialize, Encode, Deserialize, TreeHash, Derivative)] +#[derivative(PartialEq, Hash(bound = "E: EthSpec"))] +#[serde(bound = "E: EthSpec", untagged)] +#[ssz(enum_behaviour = "transparent")] +#[tree_hash(enum_behaviour = "transparent")] +pub struct ExecutionPayloadEnvelope { + #[superstruct(only(Gloas), partial_getter(rename = "payload_gloas"))] + pub payload: ExecutionPayloadGloas, + #[superstruct(only(NextFork), partial_getter(rename = "payload_next_fork"))] + pub payload: ExecutionPayloadGloas, + pub execution_requests: ExecutionRequests, + #[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 slot: Slot, + pub blob_kzg_commitments: KzgCommitments, + #[superstruct(getter(copy))] + pub state_root: Hash256, +} + +impl<'a, E: EthSpec> SignedRoot for ExecutionPayloadEnvelopeRef<'a, E> {} + +impl<'a, E: EthSpec> ExecutionPayloadEnvelopeRef<'a, E> { + pub fn payload(&self) -> ExecutionPayloadRef<'a, E> { + match self { + Self::Gloas(envelope) => ExecutionPayloadRef::Gloas(&envelope.payload), + Self::NextFork(envelope) => ExecutionPayloadRef::Gloas(&envelope.payload), + } + } +} + +impl<'de, E: EthSpec> ContextDeserialize<'de, ForkName> for ExecutionPayloadEnvelope { + fn context_deserialize(deserializer: D, context: ForkName) -> Result + where + D: Deserializer<'de>, + { + let value: Self = serde::Deserialize::deserialize(deserializer)?; + + match (context, &value) { + (ForkName::Gloas, Self::Gloas { .. }) => Ok(value), + _ => Err(D::Error::custom(format!( + "ExecutionPayloadEnvelope does not support fork {context:?}" + ))), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::MainnetEthSpec; + + mod gloas { + use super::*; + ssz_and_tree_hash_tests!(ExecutionPayloadEnvelopeGloas); + } + + mod next_fork { + use super::*; + ssz_and_tree_hash_tests!(ExecutionPayloadEnvelopeNextFork); + } +} diff --git a/consensus/types/src/indexed_payload_attestation.rs b/consensus/types/src/indexed_payload_attestation.rs new file mode 100644 index 0000000000..646bff2d45 --- /dev/null +++ b/consensus/types/src/indexed_payload_attestation.rs @@ -0,0 +1,41 @@ +use crate::test_utils::TestRandom; +use crate::*; +use core::slice::Iter; +use serde::{Deserialize, Serialize}; +use ssz_derive::{Decode, Encode}; +use test_random_derive::TestRandom; +use tree_hash_derive::TreeHash; + +#[derive( + TestRandom, + TreeHash, + Debug, + Clone, + PartialEq, + Encode, + Decode, + Serialize, + Deserialize, +)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +#[serde(bound = "E: EthSpec", deny_unknown_fields)] +#[cfg_attr(feature = "arbitrary", arbitrary(bound = "E: EthSpec"))] +#[context_deserialize(ForkName)] +pub struct IndexedPayloadAttestation { + pub attesting_indices: VariableList, + pub data: PayloadAttestationData, + pub signature: AggregateSignature, +} + +impl IndexedPayloadAttestation { + pub fn attesting_indices_iter(&self) -> Iter<'_, u64> { + self.attesting_indices.iter() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + ssz_and_tree_hash_tests!(IndexedPayloadAttestation); +} diff --git a/consensus/types/src/lib.rs b/consensus/types/src/lib.rs index 8e83fed1d9..89a73cb3f2 100644 --- a/consensus/types/src/lib.rs +++ b/consensus/types/src/lib.rs @@ -26,6 +26,8 @@ pub mod beacon_response; pub mod beacon_state; pub mod bls_to_execution_change; pub mod builder_bid; +pub mod builder_pending_payment; +pub mod builder_pending_withdrawal; pub mod chain_spec; pub mod checkpoint; pub mod consolidation_request; @@ -39,8 +41,10 @@ pub mod deposit_tree_snapshot; pub mod enr_fork_id; pub mod eth1_data; pub mod eth_spec; +pub mod execution_bid; pub mod execution_block_hash; pub mod execution_payload; +pub mod execution_payload_envelope; pub mod execution_payload_header; pub mod fork; pub mod fork_data; @@ -49,10 +53,15 @@ pub mod graffiti; pub mod historical_batch; pub mod historical_summary; pub mod indexed_attestation; +pub mod indexed_payload_attestation; pub mod light_client_bootstrap; pub mod light_client_finality_update; pub mod light_client_optimistic_update; pub mod light_client_update; +pub mod payload; +pub mod payload_attestation; +pub mod payload_attestation_data; +pub mod payload_attestation_message; pub mod pending_attestation; pub mod pending_consolidation; pub mod pending_deposit; @@ -67,6 +76,8 @@ pub mod signed_beacon_block; pub mod signed_beacon_block_header; pub mod signed_bls_to_execution_change; pub mod signed_contribution_and_proof; +pub mod signed_execution_bid; +pub mod signed_execution_payload_envelope; pub mod signed_voluntary_exit; pub mod signing_data; pub mod sync_committee_subscription; @@ -84,7 +95,6 @@ pub mod execution_block_header; pub mod execution_requests; pub mod fork_context; pub mod participation_flags; -pub mod payload; pub mod preset; pub mod slot_epoch; pub mod subnet_id; @@ -144,6 +154,8 @@ pub use crate::beacon_response::{ pub use crate::beacon_state::{Error as BeaconStateError, *}; pub use crate::blob_sidecar::{BlobIdentifier, BlobSidecar, BlobSidecarList, BlobsList}; pub use crate::bls_to_execution_change::BlsToExecutionChange; +pub use crate::builder_pending_payment::BuilderPendingPayment; +pub use crate::builder_pending_withdrawal::BuilderPendingWithdrawal; pub use crate::chain_spec::{ChainSpec, Config, Domain}; pub use crate::checkpoint::Checkpoint; pub use crate::config_and_preset::{ @@ -163,8 +175,9 @@ pub use crate::deposit_request::DepositRequest; pub use crate::deposit_tree_snapshot::{DepositTreeSnapshot, FinalizedExecutionBlock}; pub use crate::enr_fork_id::EnrForkId; pub use crate::epoch_cache::{EpochCache, EpochCacheError, EpochCacheKey}; -pub use crate::eth_spec::EthSpecId; pub use crate::eth1_data::Eth1Data; +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_payload::{ @@ -172,6 +185,10 @@ pub use crate::execution_payload::{ ExecutionPayloadElectra, ExecutionPayloadFulu, ExecutionPayloadGloas, ExecutionPayloadRef, Transaction, Transactions, Withdrawals, }; +pub use crate::execution_payload_envelope::{ + ExecutionPayloadEnvelope, ExecutionPayloadEnvelopeGloas, ExecutionPayloadEnvelopeNextFork, + ExecutionPayloadEnvelopeRef, +}; pub use crate::execution_payload_header::{ ExecutionPayloadHeader, ExecutionPayloadHeaderBellatrix, ExecutionPayloadHeaderCapella, ExecutionPayloadHeaderDeneb, ExecutionPayloadHeaderElectra, ExecutionPayloadHeaderFulu, @@ -187,6 +204,7 @@ pub use crate::historical_batch::HistoricalBatch; pub use crate::indexed_attestation::{ IndexedAttestation, IndexedAttestationBase, IndexedAttestationElectra, IndexedAttestationRef, }; +pub use crate::indexed_payload_attestation::IndexedPayloadAttestation; pub use crate::light_client_bootstrap::{ LightClientBootstrap, LightClientBootstrapAltair, LightClientBootstrapCapella, LightClientBootstrapDeneb, LightClientBootstrapElectra, LightClientBootstrapFulu, @@ -220,6 +238,9 @@ pub use crate::payload::{ FullPayloadCapella, FullPayloadDeneb, FullPayloadElectra, FullPayloadFulu, FullPayloadGloas, FullPayloadRef, OwnedExecPayload, }; +pub use crate::payload_attestation::PayloadAttestation; +pub use crate::payload_attestation_data::PayloadAttestationData; +pub use crate::payload_attestation_message::PayloadAttestationMessage; pub use crate::pending_attestation::PendingAttestation; pub use crate::pending_consolidation::PendingConsolidation; pub use crate::pending_deposit::PendingDeposit; @@ -247,6 +268,8 @@ pub use crate::signed_beacon_block::{ pub use crate::signed_beacon_block_header::SignedBeaconBlockHeader; pub use crate::signed_bls_to_execution_change::SignedBlsToExecutionChange; pub use crate::signed_contribution_and_proof::SignedContributionAndProof; +pub use crate::signed_execution_bid::SignedExecutionBid; +pub use crate::signed_execution_payload_envelope::SignedExecutionPayloadEnvelope; pub use crate::signed_voluntary_exit::SignedVoluntaryExit; pub use crate::signing_data::{SignedRoot, SigningData}; pub use crate::slot_epoch::{Epoch, Slot}; diff --git a/consensus/types/src/payload_attestation.rs b/consensus/types/src/payload_attestation.rs new file mode 100644 index 0000000000..1cc8f32d81 --- /dev/null +++ b/consensus/types/src/payload_attestation.rs @@ -0,0 +1,36 @@ +use crate::test_utils::TestRandom; +use crate::*; +use derivative::Derivative; +use serde::{Deserialize, Serialize}; +use ssz_derive::{Decode, Encode}; +use test_random_derive::TestRandom; +use tree_hash_derive::TreeHash; + +#[derive( + TestRandom, + TreeHash, + Debug, + Clone, + Encode, + Decode, + Serialize, + Deserialize, + Derivative, +)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +#[serde(bound = "E: EthSpec", deny_unknown_fields)] +#[cfg_attr(feature = "arbitrary", arbitrary(bound = "E: EthSpec"))] +#[derivative(PartialEq, Hash)] +#[context_deserialize(ForkName)] +pub struct PayloadAttestation { + pub aggregation_bits: BitList, + pub data: PayloadAttestationData, + pub signature: AggregateSignature, +} + +#[cfg(test)] +mod payload_attestation_tests { + use super::*; + + ssz_and_tree_hash_tests!(PayloadAttestation); +} diff --git a/consensus/types/src/payload_attestation_data.rs b/consensus/types/src/payload_attestation_data.rs new file mode 100644 index 0000000000..5962bfeb23 --- /dev/null +++ b/consensus/types/src/payload_attestation_data.rs @@ -0,0 +1,27 @@ +use crate::test_utils::TestRandom; +use crate::*; +use serde::{Deserialize, Serialize}; +use ssz_derive::{Decode, Encode}; +use test_random_derive::TestRandom; +use tree_hash_derive::TreeHash; + +#[derive( + TestRandom, TreeHash, Debug, Clone, PartialEq, Eq, Encode, Decode, Serialize, Deserialize, Hash, +)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +#[context_deserialize(ForkName)] +pub struct PayloadAttestationData { + pub beacon_block_root: Hash256, + pub slot: Slot, + pub payload_present: bool, +} +// todo(eip-7732): Mark's implementation has PayloadStatus as an enum, but spec calls for a bool. Need to clarify this. + +impl SignedRoot for PayloadAttestationData {} + +#[cfg(test)] +mod payload_attestation_data_tests { + use super::*; + + ssz_and_tree_hash_tests!(PayloadAttestationData); +} diff --git a/consensus/types/src/payload_attestation_message.rs b/consensus/types/src/payload_attestation_message.rs new file mode 100644 index 0000000000..87852e0ea2 --- /dev/null +++ b/consensus/types/src/payload_attestation_message.rs @@ -0,0 +1,33 @@ +use crate::test_utils::TestRandom; +use crate::*; +use serde::{Deserialize, Serialize}; +use ssz_derive::{Decode, Encode}; +use test_random_derive::TestRandom; +use tree_hash_derive::TreeHash; + +#[derive( + TestRandom, + TreeHash, + Debug, + Clone, + PartialEq, + Encode, + Decode, + Serialize, + Deserialize, +)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +#[context_deserialize(ForkName)] +pub struct PayloadAttestationMessage { + #[serde(with = "serde_utils::quoted_u64")] + pub validator_index: u64, + pub data: PayloadAttestationData, + pub signature: AggregateSignature, +} + +#[cfg(test)] +mod tests { + use super::*; + + ssz_and_tree_hash_tests!(PayloadAttestationMessage); +} diff --git a/consensus/types/src/signed_execution_bid.rs b/consensus/types/src/signed_execution_bid.rs new file mode 100644 index 0000000000..945e96af38 --- /dev/null +++ b/consensus/types/src/signed_execution_bid.rs @@ -0,0 +1,42 @@ +use crate::test_utils::TestRandom; +use crate::*; +use derivative::Derivative; +use serde::{Deserialize, Serialize}; +use ssz_derive::{Decode, Encode}; +use test_random_derive::TestRandom; +use tree_hash_derive::TreeHash; + +#[derive( + TestRandom, + TreeHash, + Debug, + Clone, + Encode, + Decode, + Serialize, + Deserialize, + Derivative, +)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +#[derivative(PartialEq, Hash)] +#[context_deserialize(ForkName)] +pub struct SignedExecutionBid { + pub message: ExecutionBid, + pub signature: Signature, +} + +impl SignedExecutionBid { + pub fn empty() -> Self { + Self { + message: ExecutionBid::default(), + signature: Signature::empty(), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + ssz_and_tree_hash_tests!(SignedExecutionBid); +} diff --git a/consensus/types/src/signed_execution_payload_envelope.rs b/consensus/types/src/signed_execution_payload_envelope.rs new file mode 100644 index 0000000000..6d5c6d1c12 --- /dev/null +++ b/consensus/types/src/signed_execution_payload_envelope.rs @@ -0,0 +1,91 @@ +use crate::test_utils::TestRandom; +use crate::*; +use derivative::Derivative; +use serde::de::{Deserializer, Error as _}; +use serde::{Deserialize, Serialize}; +use ssz_derive::{Decode, Encode}; +use superstruct::superstruct; +use test_random_derive::TestRandom; +use tree_hash_derive::TreeHash; + +#[superstruct( + variants(Gloas, NextFork), + variant_attributes( + derive( + Debug, + Clone, + Serialize, + Deserialize, + Encode, + Decode, + TreeHash, + TestRandom, + Derivative + ), + cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary)), + derivative(PartialEq, Hash(bound = "E: EthSpec")), + serde(bound = "E: EthSpec", deny_unknown_fields), + cfg_attr(feature = "arbitrary", 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") +)] +#[derive(Debug, Clone, Serialize, Encode, Deserialize, TreeHash, Derivative)] +#[derivative(PartialEq, Hash(bound = "E: EthSpec"))] +#[serde(bound = "E: EthSpec", untagged)] +#[ssz(enum_behaviour = "transparent")] +#[tree_hash(enum_behaviour = "transparent")] +pub struct SignedExecutionPayloadEnvelope { + #[superstruct(only(Gloas), partial_getter(rename = "message_gloas"))] + pub message: ExecutionPayloadEnvelopeGloas, + #[superstruct(only(NextFork), partial_getter(rename = "message_next_fork"))] + pub message: crate::execution_payload_envelope::ExecutionPayloadEnvelopeNextFork, + pub signature: Signature, +} + +impl SignedExecutionPayloadEnvelope { + pub fn message(&self) -> ExecutionPayloadEnvelopeRef { + match self { + Self::Gloas(ref signed) => ExecutionPayloadEnvelopeRef::Gloas(&signed.message), + Self::NextFork(ref signed) => ExecutionPayloadEnvelopeRef::NextFork(&signed.message), + } + } + + // todo(eip-7732): implement verify_signature since spec calls for verify_execution_payload_envelope_signature +} + +impl<'de, E: EthSpec> ContextDeserialize<'de, ForkName> for SignedExecutionPayloadEnvelope { + fn context_deserialize(deserializer: D, context: ForkName) -> Result + where + D: Deserializer<'de>, + { + let value: Self = Deserialize::deserialize(deserializer)?; + + match (context, &value) { + (ForkName::Gloas, Self::Gloas { .. }) => Ok(value), + _ => Err(D::Error::custom(format!( + "SignedExecutionPayloadEnvelope does not support fork {context:?}" + ))), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::MainnetEthSpec; + + mod gloas { + use super::*; + ssz_and_tree_hash_tests!(SignedExecutionPayloadEnvelopeGloas); + } + + mod next_fork { + use super::*; + ssz_and_tree_hash_tests!(SignedExecutionPayloadEnvelopeNextFork); + } +}