From 255fdf072467fdc0afa2a7566b85afb4b6346d7f Mon Sep 17 00:00:00 2001 From: ethDreamer <37123614+ethDreamer@users.noreply.github.com> Date: Thu, 13 Oct 2022 09:37:20 -0500 Subject: [PATCH] Added Capella Data Structures to consensus/types (#3637) * Ran Cargo fmt * Added Capella Data Structures to consensus/types --- Cargo.lock | 20 +- .../beacon_chain/src/execution_payload.rs | 4 +- beacon_node/http_api/src/publish_blobs.rs | 6 +- .../beacon_processor/worker/gossip_methods.rs | 5 +- consensus/types/Cargo.toml | 2 +- consensus/types/src/beacon_block.rs | 35 +- consensus/types/src/beacon_block_body.rs | 138 ++- consensus/types/src/beacon_state.rs | 59 +- consensus/types/src/builder_bid.rs | 15 +- consensus/types/src/chain_spec.rs | 76 ++ consensus/types/src/eth_spec.rs | 41 + consensus/types/src/execution_payload.rs | 72 +- .../types/src/execution_payload_header.rs | 127 ++- consensus/types/src/fork_name.rs | 25 +- consensus/types/src/lib.rs | 28 +- consensus/types/src/payload.rs | 1005 +++++++++++++++-- consensus/types/src/signed_beacon_block.rs | 97 +- consensus/types/src/withdrawal.rs | 27 + 18 files changed, 1602 insertions(+), 180 deletions(-) create mode 100644 consensus/types/src/withdrawal.rs diff --git a/Cargo.lock b/Cargo.lock index 4dfd070f4d..653e0fc3d2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -428,7 +428,7 @@ dependencies = [ "state_processing", "store", "strum", - "superstruct", + "superstruct 0.5.0", "task_executor", "tempfile", "tokio", @@ -3692,7 +3692,7 @@ dependencies = [ "smallvec", "snap", "strum", - "superstruct", + "superstruct 0.5.0", "task_executor", "tempfile", "tiny-keccak", @@ -6395,6 +6395,20 @@ dependencies = [ "syn", ] +[[package]] +name = "superstruct" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75b9e5728aa1a87141cefd4e7509903fc01fa0dcb108022b1e841a67c5159fc5" +dependencies = [ + "darling", + "itertools", + "proc-macro2", + "quote", + "smallvec", + "syn", +] + [[package]] name = "swap_or_not_shuffle" version = "0.2.0" @@ -7125,7 +7139,7 @@ dependencies = [ "slog", "smallvec", "state_processing", - "superstruct", + "superstruct 0.6.0", "swap_or_not_shuffle", "tempfile", "test_random_derive", diff --git a/beacon_node/beacon_chain/src/execution_payload.rs b/beacon_node/beacon_chain/src/execution_payload.rs index 7684343300..16c758b377 100644 --- a/beacon_node/beacon_chain/src/execution_payload.rs +++ b/beacon_node/beacon_chain/src/execution_payload.rs @@ -25,8 +25,8 @@ use std::sync::Arc; use tokio::task::JoinHandle; use tree_hash::TreeHash; use types::{ - BeaconBlockRef, BeaconState, BeaconStateError, Blob, EthSpec, ExecPayload, - ExecutionBlockHash, Hash256, KzgCommitment, SignedBeaconBlock, Slot, + BeaconBlockRef, BeaconState, BeaconStateError, Blob, EthSpec, ExecPayload, ExecutionBlockHash, + Hash256, KzgCommitment, SignedBeaconBlock, Slot, }; pub type PreparePayloadResult = diff --git a/beacon_node/http_api/src/publish_blobs.rs b/beacon_node/http_api/src/publish_blobs.rs index 5748ab6960..cf08ac2d2b 100644 --- a/beacon_node/http_api/src/publish_blobs.rs +++ b/beacon_node/http_api/src/publish_blobs.rs @@ -3,12 +3,10 @@ use beacon_chain::validator_monitor::{get_slot_delay_ms, timestamp_now}; use beacon_chain::{BeaconChain, BeaconChainTypes}; use lighthouse_network::PubsubMessage; use network::NetworkMessage; -use slog::{Logger}; +use slog::Logger; use std::sync::Arc; use tokio::sync::mpsc::UnboundedSender; -use types::{ - SignedBlobsSidecar, -}; +use types::SignedBlobsSidecar; use warp::Rejection; /// Handles a request from the HTTP API for full blocks. diff --git a/beacon_node/network/src/beacon_processor/worker/gossip_methods.rs b/beacon_node/network/src/beacon_processor/worker/gossip_methods.rs index 2cabd1ba37..debfdff3d7 100644 --- a/beacon_node/network/src/beacon_processor/worker/gossip_methods.rs +++ b/beacon_node/network/src/beacon_processor/worker/gossip_methods.rs @@ -2257,7 +2257,10 @@ impl Worker { BlobError::ProposalSignatureInvalid => { self.propagate_validation_result(message_id, peer_id, MessageAcceptance::Reject); } - BlobError::RepeatSidecar { proposer: _, slot: _ } => { + BlobError::RepeatSidecar { + proposer: _, + slot: _, + } => { self.propagate_validation_result(message_id, peer_id, MessageAcceptance::Ignore); } BlobError::UnknownHeadBlock { beacon_block_root } => { diff --git a/consensus/types/Cargo.toml b/consensus/types/Cargo.toml index 397d916dce..b4164021d9 100644 --- a/consensus/types/Cargo.toml +++ b/consensus/types/Cargo.toml @@ -44,7 +44,7 @@ regex = "1.5.5" lazy_static = "1.4.0" parking_lot = "0.12.0" itertools = "0.10.0" -superstruct = "0.5.0" +superstruct = "0.6.0" serde_json = "1.0.74" smallvec = "1.8.0" serde_with = "1.13.0" diff --git a/consensus/types/src/beacon_block.rs b/consensus/types/src/beacon_block.rs index 44f4fd2292..d58e890c60 100644 --- a/consensus/types/src/beacon_block.rs +++ b/consensus/types/src/beacon_block.rs @@ -17,7 +17,7 @@ use tree_hash_derive::TreeHash; /// A block of the `BeaconChain`. #[superstruct( - variants(Base, Altair, Merge, Eip4844), + variants(Base, Altair, Merge, Capella, Eip4844), variant_attributes( derive( Debug, @@ -48,7 +48,7 @@ use tree_hash_derive::TreeHash; #[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))] #[tree_hash(enum_behaviour = "transparent")] #[ssz(enum_behaviour = "transparent")] -pub struct BeaconBlock = FullPayload> { +pub struct BeaconBlock = FullPayload> { #[superstruct(getter(copy))] pub slot: Slot, #[superstruct(getter(copy))] @@ -64,16 +64,22 @@ pub struct BeaconBlock = FullPayload> { pub body: BeaconBlockBodyAltair, #[superstruct(only(Merge), partial_getter(rename = "body_merge"))] pub body: BeaconBlockBodyMerge, + #[superstruct(only(Capella), partial_getter(rename = "body_capella"))] + pub body: BeaconBlockBodyCapella, #[superstruct(only(Eip4844), partial_getter(rename = "body_eip4844"))] pub body: BeaconBlockBodyEip4844, } pub type BlindedBeaconBlock = BeaconBlock>; -impl> SignedRoot for BeaconBlock {} -impl<'a, T: EthSpec, Payload: ExecPayload> SignedRoot for BeaconBlockRef<'a, T, Payload> {} +impl> SignedRoot for BeaconBlock {} +impl<'a, T: EthSpec, Payload: AbstractExecPayload> SignedRoot + for BeaconBlockRef<'a, T, Payload> +{ +} -impl> BeaconBlock { +impl> BeaconBlock { + // FIXME: deal with capella / eip4844 forks here as well /// Returns an empty block to be used during genesis. pub fn empty(spec: &ChainSpec) -> Self { if spec.bellatrix_fork_epoch == Some(T::genesis_epoch()) { @@ -180,7 +186,7 @@ impl> BeaconBlock { } } -impl<'a, T: EthSpec, Payload: ExecPayload> BeaconBlockRef<'a, T, Payload> { +impl<'a, T: EthSpec, Payload: AbstractExecPayload> BeaconBlockRef<'a, T, Payload> { /// Returns the name of the fork pertaining to `self`. /// /// Will return an `Err` if `self` has been instantiated to a variant conflicting with the fork @@ -191,6 +197,7 @@ impl<'a, T: EthSpec, Payload: ExecPayload> BeaconBlockRef<'a, T, Payload> { BeaconBlockRef::Base { .. } => ForkName::Base, BeaconBlockRef::Altair { .. } => ForkName::Altair, BeaconBlockRef::Merge { .. } => ForkName::Merge, + BeaconBlockRef::Capella { .. } => ForkName::Capella, BeaconBlockRef::Eip4844 { .. } => ForkName::Eip4844, }; @@ -245,12 +252,12 @@ impl<'a, T: EthSpec, Payload: ExecPayload> BeaconBlockRef<'a, T, Payload> { /// Extracts a reference to an execution payload from a block, returning an error if the block /// is pre-merge. - pub fn execution_payload(&self) -> Result<&Payload, Error> { + pub fn execution_payload(&self) -> Result, Error> { self.body().execution_payload() } } -impl<'a, T: EthSpec, Payload: ExecPayload> BeaconBlockRefMut<'a, T, Payload> { +impl<'a, T: EthSpec, Payload: AbstractExecPayload> BeaconBlockRefMut<'a, T, Payload> { /// Convert a mutable reference to a beacon block to a mutable ref to its body. pub fn body_mut(self) -> BeaconBlockBodyRefMut<'a, T, Payload> { map_beacon_block_ref_mut_into_beacon_block_body_ref_mut!(&'a _, self, |block, cons| cons( @@ -259,7 +266,7 @@ impl<'a, T: EthSpec, Payload: ExecPayload> BeaconBlockRefMut<'a, T, Payload> } } -impl> BeaconBlockBase { +impl> BeaconBlockBase { /// Returns an empty block to be used during genesis. pub fn empty(spec: &ChainSpec) -> Self { BeaconBlockBase { @@ -380,7 +387,7 @@ impl> BeaconBlockBase { } } -impl> BeaconBlockAltair { +impl> BeaconBlockAltair { /// Returns an empty Altair block to be used during genesis. pub fn empty(spec: &ChainSpec) -> Self { BeaconBlockAltair { @@ -439,7 +446,7 @@ impl> BeaconBlockAltair { } } -impl> BeaconBlockMerge { +impl> BeaconBlockMerge { /// Returns an empty Merge block to be used during genesis. pub fn empty(spec: &ChainSpec) -> Self { BeaconBlockMerge { @@ -461,7 +468,7 @@ impl> BeaconBlockMerge { deposits: VariableList::empty(), voluntary_exits: VariableList::empty(), sync_aggregate: SyncAggregate::empty(), - execution_payload: Payload::default(), + execution_payload: Payload::Merge::default(), }, } } @@ -536,7 +543,7 @@ macro_rules! impl_from { parent_root, state_root, body, - }, payload) + }, payload.map(Into::into)) } } } @@ -545,6 +552,7 @@ macro_rules! impl_from { impl_from!(BeaconBlockBase, >, >, |body: BeaconBlockBodyBase<_, _>| body.into()); impl_from!(BeaconBlockAltair, >, >, |body: BeaconBlockBodyAltair<_, _>| body.into()); impl_from!(BeaconBlockMerge, >, >, |body: BeaconBlockBodyMerge<_, _>| body.into()); +impl_from!(BeaconBlockCapella, >, >, |body: BeaconBlockBodyCapella<_, _>| body.into()); impl_from!(BeaconBlockEip4844, >, >, |body: BeaconBlockBodyEip4844<_, _>| body.into()); // We can clone blocks with payloads to blocks without payloads, without cloning the payload. @@ -576,6 +584,7 @@ macro_rules! impl_clone_as_blinded { impl_clone_as_blinded!(BeaconBlockBase, >, >); impl_clone_as_blinded!(BeaconBlockAltair, >, >); impl_clone_as_blinded!(BeaconBlockMerge, >, >); +impl_clone_as_blinded!(BeaconBlockCapella, >, >); impl_clone_as_blinded!(BeaconBlockEip4844, >, >); // A reference to a full beacon block can be cloned into a blinded beacon block, without cloning the diff --git a/consensus/types/src/beacon_block_body.rs b/consensus/types/src/beacon_block_body.rs index 61bf56f323..36e0ce7700 100644 --- a/consensus/types/src/beacon_block_body.rs +++ b/consensus/types/src/beacon_block_body.rs @@ -14,7 +14,7 @@ use tree_hash_derive::TreeHash; /// /// This *superstruct* abstracts over the hard-fork. #[superstruct( - variants(Base, Altair, Merge, Eip4844), + variants(Base, Altair, Merge, Capella, Eip4844), variant_attributes( derive( Debug, @@ -39,7 +39,7 @@ use tree_hash_derive::TreeHash; #[serde(untagged)] #[serde(bound = "T: EthSpec, Payload: ExecPayload")] #[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))] -pub struct BeaconBlockBody = FullPayload> { +pub struct BeaconBlockBody = FullPayload> { pub randao_reveal: Signature, pub eth1_data: Eth1Data, pub graffiti: Graffiti, @@ -48,14 +48,20 @@ pub struct BeaconBlockBody = FullPayload> pub attestations: VariableList, T::MaxAttestations>, pub deposits: VariableList, pub voluntary_exits: VariableList, - #[superstruct(only(Altair, Merge, Eip4844))] + #[superstruct(only(Altair, Merge, Capella, Eip4844))] pub sync_aggregate: SyncAggregate, // We flatten the execution payload so that serde can use the name of the inner type, // either `execution_payload` for full payloads, or `execution_payload_header` for blinded // payloads. - #[superstruct(only(Merge, Eip4844))] + #[superstruct(only(Merge), partial_getter(rename = "execution_payload_merge"))] #[serde(flatten)] - pub execution_payload: Payload, + pub execution_payload: Payload::Merge, + #[superstruct(only(Capella), partial_getter(rename = "execution_payload_capella"))] + #[serde(flatten)] + pub execution_payload: Payload::Capella, + #[superstruct(only(Eip4844), partial_getter(rename = "execution_payload_eip4844"))] + #[serde(flatten)] + pub execution_payload: Payload::Eip4844, #[superstruct(only(Eip4844))] pub blob_kzg_commitments: VariableList, #[superstruct(only(Base, Altair))] @@ -65,6 +71,23 @@ pub struct BeaconBlockBody = FullPayload> pub _phantom: PhantomData, } +impl> BeaconBlockBody { + pub fn execution_payload<'a>(&'a self) -> Result, Error> { + self.to_ref().execution_payload() + } +} + +impl<'a, T: EthSpec, Payload: AbstractExecPayload> BeaconBlockBodyRef<'a, T, Payload> { + pub fn execution_payload(&self) -> Result, Error> { + match self { + Self::Base(_) | Self::Altair(_) => Err(Error::IncorrectStateVariant), + Self::Merge(body) => Ok(Payload::Ref::from(&body.execution_payload)), + Self::Capella(body) => Ok(Payload::Ref::from(&body.execution_payload)), + Self::Eip4844(body) => Ok(Payload::Ref::from(&body.execution_payload)), + } + } +} + impl<'a, T: EthSpec> BeaconBlockBodyRef<'a, T> { /// Get the fork_name of this object pub fn fork_name(self) -> ForkName { @@ -72,6 +95,7 @@ impl<'a, T: EthSpec> BeaconBlockBodyRef<'a, T> { BeaconBlockBodyRef::Base { .. } => ForkName::Base, BeaconBlockBodyRef::Altair { .. } => ForkName::Altair, BeaconBlockBodyRef::Merge { .. } => ForkName::Merge, + BeaconBlockBodyRef::Capella { .. } => ForkName::Capella, BeaconBlockBodyRef::Eip4844 { .. } => ForkName::Eip4844, } } @@ -218,7 +242,7 @@ impl From>> impl From>> for ( BeaconBlockBodyMerge>, - Option>, + Option>, ) { fn from(body: BeaconBlockBodyMerge>) -> Self { @@ -232,7 +256,7 @@ impl From>> deposits, voluntary_exits, sync_aggregate, - execution_payload: FullPayload { execution_payload }, + execution_payload: FullPayloadMerge { execution_payload }, } = body; ( @@ -246,8 +270,48 @@ impl From>> deposits, voluntary_exits, sync_aggregate, - execution_payload: BlindedPayload { - execution_payload_header: From::from(&execution_payload), + execution_payload: BlindedPayloadMerge { + execution_payload_header: From::from(execution_payload.clone()), + }, + }, + Some(execution_payload), + ) + } +} + +impl From>> + for ( + BeaconBlockBodyCapella>, + Option>, + ) +{ + fn from(body: BeaconBlockBodyCapella>) -> Self { + let BeaconBlockBodyCapella { + randao_reveal, + eth1_data, + graffiti, + proposer_slashings, + attester_slashings, + attestations, + deposits, + voluntary_exits, + sync_aggregate, + execution_payload: FullPayloadCapella { execution_payload }, + } = body; + + ( + BeaconBlockBodyCapella { + randao_reveal, + eth1_data, + graffiti, + proposer_slashings, + attester_slashings, + attestations, + deposits, + voluntary_exits, + sync_aggregate, + execution_payload: BlindedPayloadCapella { + execution_payload_header: From::from(execution_payload.clone()), }, }, Some(execution_payload), @@ -258,7 +322,7 @@ impl From>> impl From>> for ( BeaconBlockBodyEip4844>, - Option>, + Option>, ) { fn from(body: BeaconBlockBodyEip4844>) -> Self { @@ -272,7 +336,7 @@ impl From>> deposits, voluntary_exits, sync_aggregate, - execution_payload: FullPayload { execution_payload }, + execution_payload: FullPayloadEip4844 { execution_payload }, blob_kzg_commitments, } = body; @@ -287,12 +351,12 @@ impl From>> deposits, voluntary_exits, sync_aggregate, - execution_payload: BlindedPayload { - execution_payload_header: From::from(&execution_payload), + execution_payload: BlindedPayloadEip4844 { + execution_payload_header: From::from(execution_payload.clone()), }, blob_kzg_commitments, }, - None, + Some(execution_payload), ) } } @@ -324,7 +388,7 @@ impl BeaconBlockBodyMerge> { deposits, voluntary_exits, sync_aggregate, - execution_payload: FullPayload { execution_payload }, + execution_payload: FullPayloadMerge { execution_payload }, } = self; BeaconBlockBodyMerge { @@ -337,8 +401,40 @@ impl BeaconBlockBodyMerge> { deposits: deposits.clone(), voluntary_exits: voluntary_exits.clone(), sync_aggregate: sync_aggregate.clone(), - execution_payload: BlindedPayload { - execution_payload_header: From::from(execution_payload), + execution_payload: BlindedPayloadMerge { + execution_payload_header: From::from(execution_payload.clone()), + }, + } + } +} + +impl BeaconBlockBodyCapella> { + pub fn clone_as_blinded(&self) -> BeaconBlockBodyCapella> { + let BeaconBlockBodyCapella { + randao_reveal, + eth1_data, + graffiti, + proposer_slashings, + attester_slashings, + attestations, + deposits, + voluntary_exits, + sync_aggregate, + execution_payload: FullPayloadCapella { execution_payload }, + } = self; + + BeaconBlockBodyCapella { + randao_reveal: randao_reveal.clone(), + eth1_data: eth1_data.clone(), + graffiti: *graffiti, + proposer_slashings: proposer_slashings.clone(), + attester_slashings: attester_slashings.clone(), + attestations: attestations.clone(), + deposits: deposits.clone(), + voluntary_exits: voluntary_exits.clone(), + sync_aggregate: sync_aggregate.clone(), + execution_payload: BlindedPayloadCapella { + execution_payload_header: From::from(execution_payload.clone()), }, } } @@ -356,7 +452,7 @@ impl BeaconBlockBodyEip4844> { deposits, voluntary_exits, sync_aggregate, - execution_payload: FullPayload { execution_payload }, + execution_payload: FullPayloadEip4844 { execution_payload }, blob_kzg_commitments, } = self; @@ -370,8 +466,8 @@ impl BeaconBlockBodyEip4844> { deposits: deposits.clone(), voluntary_exits: voluntary_exits.clone(), sync_aggregate: sync_aggregate.clone(), - execution_payload: BlindedPayload { - execution_payload_header: From::from(execution_payload), + execution_payload: BlindedPayloadEip4844 { + execution_payload_header: From::from(execution_payload.clone()), }, blob_kzg_commitments: blob_kzg_commitments.clone(), } @@ -387,7 +483,7 @@ impl From>> fn from(body: BeaconBlockBody>) -> Self { map_beacon_block_body!(body, |inner, cons| { let (block, payload) = inner.into(); - (cons(block), payload) + (cons(block), payload.map(Into::into)) }) } } diff --git a/consensus/types/src/beacon_state.rs b/consensus/types/src/beacon_state.rs index 3a006e5461..6438a0a7e1 100644 --- a/consensus/types/src/beacon_state.rs +++ b/consensus/types/src/beacon_state.rs @@ -172,7 +172,7 @@ impl From for Hash256 { /// The state of the `BeaconChain` at some slot. #[superstruct( - variants(Base, Altair, Merge, Eip4844), + variants(Base, Altair, Merge, Capella, Eip4844), variant_attributes( derive( Derivative, @@ -250,9 +250,9 @@ where pub current_epoch_attestations: VariableList, T::MaxPendingAttestations>, // Participation (Altair and later) - #[superstruct(only(Altair, Merge, Eip4844))] + #[superstruct(only(Altair, Merge, Capella, Eip4844))] pub previous_epoch_participation: VariableList, - #[superstruct(only(Altair, Merge, Eip4844))] + #[superstruct(only(Altair, Merge, Capella, Eip4844))] pub current_epoch_participation: VariableList, // Finality @@ -267,18 +267,39 @@ where // Inactivity #[serde(with = "ssz_types::serde_utils::quoted_u64_var_list")] - #[superstruct(only(Altair, Merge, Eip4844))] + #[superstruct(only(Altair, Merge, Capella, Eip4844))] pub inactivity_scores: VariableList, // Light-client sync committees - #[superstruct(only(Altair, Merge, Eip4844))] + #[superstruct(only(Altair, Merge, Capella, Eip4844))] pub current_sync_committee: Arc>, - #[superstruct(only(Altair, Merge, Eip4844))] + #[superstruct(only(Altair, Merge, Capella, Eip4844))] pub next_sync_committee: Arc>, // Execution - #[superstruct(only(Merge, Eip4844))] - pub latest_execution_payload_header: ExecutionPayloadHeader, + #[superstruct( + only(Merge), + partial_getter(rename = "latest_execution_payload_header_merge") + )] + pub latest_execution_payload_header: ExecutionPayloadHeaderMerge, + #[superstruct( + only(Capella), + partial_getter(rename = "latest_execution_payload_header_capella") + )] + pub latest_execution_payload_header: ExecutionPayloadHeaderCapella, + #[superstruct( + only(Eip4844), + partial_getter(rename = "latest_execution_payload_header_eip4844") + )] + pub latest_execution_payload_header: ExecutionPayloadHeaderEip4844, + + // Withdrawals + #[superstruct(only(Capella, Eip4844))] + pub withdrawal_queue: VariableList, + #[superstruct(only(Capella, Eip4844))] + pub next_withdrawal_index: u64, + #[superstruct(only(Capella, Eip4844))] + pub next_partial_withdrawal_validator_index: u64, // Caching (not in the spec) #[serde(skip_serializing, skip_deserializing)] @@ -389,6 +410,7 @@ impl BeaconState { BeaconState::Base { .. } => ForkName::Base, BeaconState::Altair { .. } => ForkName::Altair, BeaconState::Merge { .. } => ForkName::Merge, + BeaconState::Capella { .. } => ForkName::Capella, BeaconState::Eip4844 { .. } => ForkName::Eip4844, }; @@ -679,6 +701,23 @@ impl BeaconState { .ok_or(Error::ShuffleIndexOutOfBounds(index)) } + // TODO: check this implementation + /// Convenience accessor for the `execution_payload_header` as an `ExecutionPayloadHeaderRef`. + pub fn latest_execution_payload_header(&self) -> Result, Error> { + match self { + BeaconState::Base(_) | BeaconState::Altair(_) => Err(Error::IncorrectStateVariant), + BeaconState::Merge(state) => Ok(ExecutionPayloadHeaderRef::Merge( + &state.latest_execution_payload_header, + )), + BeaconState::Capella(state) => Ok(ExecutionPayloadHeaderRef::Capella( + &state.latest_execution_payload_header, + )), + BeaconState::Eip4844(state) => Ok(ExecutionPayloadHeaderRef::Eip4844( + &state.latest_execution_payload_header, + )), + } + } + /// Return `true` if the validator who produced `slot_signature` is eligible to aggregate. /// /// Spec v0.12.1 @@ -1103,6 +1142,7 @@ impl BeaconState { BeaconState::Base(state) => (&mut state.validators, &mut state.balances), BeaconState::Altair(state) => (&mut state.validators, &mut state.balances), BeaconState::Merge(state) => (&mut state.validators, &mut state.balances), + BeaconState::Capella(state) => (&mut state.validators, &mut state.balances), BeaconState::Eip4844(state) => (&mut state.validators, &mut state.balances), } } @@ -1300,6 +1340,7 @@ impl BeaconState { BeaconState::Base(_) => Err(BeaconStateError::IncorrectStateVariant), BeaconState::Altair(state) => Ok(&mut state.current_epoch_participation), BeaconState::Merge(state) => Ok(&mut state.current_epoch_participation), + BeaconState::Capella(state) => Ok(&mut state.current_epoch_participation), BeaconState::Eip4844(state) => Ok(&mut state.current_epoch_participation), } } else if epoch == self.previous_epoch() { @@ -1307,6 +1348,7 @@ impl BeaconState { BeaconState::Base(_) => Err(BeaconStateError::IncorrectStateVariant), BeaconState::Altair(state) => Ok(&mut state.previous_epoch_participation), BeaconState::Merge(state) => Ok(&mut state.previous_epoch_participation), + BeaconState::Capella(state) => Ok(&mut state.previous_epoch_participation), BeaconState::Eip4844(state) => Ok(&mut state.previous_epoch_participation), } } else { @@ -1612,6 +1654,7 @@ impl BeaconState { BeaconState::Base(inner) => BeaconState::Base(inner.clone()), BeaconState::Altair(inner) => BeaconState::Altair(inner.clone()), BeaconState::Merge(inner) => BeaconState::Merge(inner.clone()), + BeaconState::Capella(inner) => BeaconState::Capella(inner.clone()), BeaconState::Eip4844(inner) => BeaconState::Eip4844(inner.clone()), }; if config.committee_caches { diff --git a/consensus/types/src/builder_bid.rs b/consensus/types/src/builder_bid.rs index 047bceae7e..818ec52b81 100644 --- a/consensus/types/src/builder_bid.rs +++ b/consensus/types/src/builder_bid.rs @@ -1,4 +1,7 @@ -use crate::{ChainSpec, EthSpec, ExecPayload, ExecutionPayloadHeader, SignedRoot, Uint256}; +use crate::{ + AbstractExecPayload, ChainSpec, EthSpec, ExecPayload, ExecutionPayloadHeader, SignedRoot, + Uint256, +}; use bls::PublicKeyBytes; use bls::Signature; use serde::{Deserialize as De, Deserializer, Serialize as Ser, Serializer}; @@ -10,7 +13,7 @@ use tree_hash_derive::TreeHash; #[serde_as] #[derive(PartialEq, Debug, Serialize, Deserialize, TreeHash, Clone)] #[serde(bound = "E: EthSpec, Payload: ExecPayload")] -pub struct BuilderBid> { +pub struct BuilderBid> { #[serde_as(as = "BlindedPayloadAsHeader")] pub header: Payload, #[serde(with = "eth2_serde_utils::quoted_u256")] @@ -21,12 +24,12 @@ pub struct BuilderBid> { _phantom_data: PhantomData, } -impl> SignedRoot for BuilderBid {} +impl> SignedRoot for BuilderBid {} /// Validator registration, for use in interacting with servers implementing the builder API. #[derive(PartialEq, Debug, Serialize, Deserialize, Clone)] #[serde(bound = "E: EthSpec, Payload: ExecPayload")] -pub struct SignedBuilderBid> { +pub struct SignedBuilderBid> { pub message: BuilderBid, pub signature: Signature, } @@ -42,7 +45,7 @@ impl> SerializeAs for BlindedPayloa } } -impl<'de, E: EthSpec, Payload: ExecPayload> DeserializeAs<'de, Payload> +impl<'de, E: EthSpec, Payload: AbstractExecPayload> DeserializeAs<'de, Payload> for BlindedPayloadAsHeader { fn deserialize_as(deserializer: D) -> Result @@ -55,7 +58,7 @@ impl<'de, E: EthSpec, Payload: ExecPayload> DeserializeAs<'de, Payload> } } -impl> SignedBuilderBid { +impl> SignedBuilderBid { pub fn verify_signature(&self, spec: &ChainSpec) -> bool { self.message .pubkey diff --git a/consensus/types/src/chain_spec.rs b/consensus/types/src/chain_spec.rs index aa477b22e5..af22823d2e 100644 --- a/consensus/types/src/chain_spec.rs +++ b/consensus/types/src/chain_spec.rs @@ -11,6 +11,7 @@ use tree_hash::TreeHash; /// Each of the BLS signature domains. #[derive(Debug, PartialEq, Clone, Copy)] pub enum Domain { + BlsToExecutionChange, BeaconProposer, BeaconAttester, BlobsSideCar, @@ -152,6 +153,13 @@ pub struct ChainSpec { pub terminal_block_hash_activation_epoch: Epoch, pub safe_slots_to_import_optimistically: u64, + /* + * Capella hard fork params + */ + pub capella_fork_version: [u8; 4], + /// The Capella fork epoch is optional, with `None` representing "Merge never happens". + pub capella_fork_epoch: Option, + /* * Eip4844 hard fork params */ @@ -174,6 +182,11 @@ pub struct ChainSpec { * Application params */ pub(crate) domain_application_mask: u32, + + /* + * Capella params + */ + pub(crate) domain_bls_to_execution_change: u32, } impl ChainSpec { @@ -256,6 +269,7 @@ impl ChainSpec { ForkName::Base => self.genesis_fork_version, ForkName::Altair => self.altair_fork_version, ForkName::Merge => self.bellatrix_fork_version, + ForkName::Capella => self.capella_fork_version, ForkName::Eip4844 => self.eip4844_fork_version, } } @@ -266,6 +280,7 @@ impl ChainSpec { ForkName::Base => Some(Epoch::new(0)), ForkName::Altair => self.altair_fork_epoch, ForkName::Merge => self.bellatrix_fork_epoch, + ForkName::Capella => self.capella_fork_epoch, ForkName::Eip4844 => self.eip4844_fork_epoch, } } @@ -276,6 +291,7 @@ impl ChainSpec { BeaconState::Base(_) => self.inactivity_penalty_quotient, BeaconState::Altair(_) => self.inactivity_penalty_quotient_altair, BeaconState::Merge(_) => self.inactivity_penalty_quotient_bellatrix, + BeaconState::Capella(_) => self.inactivity_penalty_quotient_bellatrix, BeaconState::Eip4844(_) => self.inactivity_penalty_quotient_bellatrix, } } @@ -289,6 +305,7 @@ impl ChainSpec { BeaconState::Base(_) => self.proportional_slashing_multiplier, BeaconState::Altair(_) => self.proportional_slashing_multiplier_altair, BeaconState::Merge(_) => self.proportional_slashing_multiplier_bellatrix, + BeaconState::Capella(_) => self.proportional_slashing_multiplier_bellatrix, BeaconState::Eip4844(_) => self.proportional_slashing_multiplier_bellatrix, } } @@ -302,6 +319,7 @@ impl ChainSpec { BeaconState::Base(_) => self.min_slashing_penalty_quotient, BeaconState::Altair(_) => self.min_slashing_penalty_quotient_altair, BeaconState::Merge(_) => self.min_slashing_penalty_quotient_bellatrix, + BeaconState::Capella(_) => self.min_slashing_penalty_quotient_bellatrix, BeaconState::Eip4844(_) => self.min_slashing_penalty_quotient_bellatrix, } } @@ -351,6 +369,7 @@ impl ChainSpec { Domain::ContributionAndProof => self.domain_contribution_and_proof, Domain::SyncCommitteeSelectionProof => self.domain_sync_committee_selection_proof, Domain::ApplicationMask(application_domain) => application_domain.get_domain_constant(), + Domain::BlsToExecutionChange => self.domain_bls_to_execution_change, } } @@ -586,6 +605,12 @@ impl ChainSpec { terminal_block_hash_activation_epoch: Epoch::new(u64::MAX), safe_slots_to_import_optimistically: 128u64, + /* + * Capella hard fork params + */ + capella_fork_version: [0x03, 00, 00, 00], + capella_fork_epoch: Some(Epoch::new(18446744073709551615)), + /* * Eip4844 hard fork params */ @@ -608,6 +633,11 @@ impl ChainSpec { * Application specific */ domain_application_mask: APPLICATION_DOMAIN_BUILDER, + + /* + * Capella params + */ + domain_bls_to_execution_change: 10, } } @@ -647,6 +677,9 @@ impl ChainSpec { // `Uint256::MAX` which is `2*256- 1`. .checked_add(Uint256::one()) .expect("addition does not overflow"), + // Capella + capella_fork_version: [0x03, 0x00, 0x00, 0x01], + capella_fork_epoch: None, // Eip4844 eip4844_fork_version: [0x04, 0x00, 0x00, 0x01], eip4844_fork_epoch: None, @@ -806,6 +839,15 @@ impl ChainSpec { terminal_block_hash_activation_epoch: Epoch::new(u64::MAX), safe_slots_to_import_optimistically: 128u64, + /* + * Capella hard fork params + */ + capella_fork_version: [0x03, 0x00, 0x00, 0x64], + capella_fork_epoch: None, + + /* + * Eip4844 hard fork params + */ eip4844_fork_version: [0x04, 0x00, 0x00, 0x64], eip4844_fork_epoch: None, @@ -825,6 +867,11 @@ impl ChainSpec { * Application specific */ domain_application_mask: APPLICATION_DOMAIN_BUILDER, + + /* + * Capella params + */ + domain_bls_to_execution_change: 10, } } } @@ -884,6 +931,14 @@ pub struct Config { #[serde(deserialize_with = "deserialize_fork_epoch")] pub bellatrix_fork_epoch: Option>, + #[serde(default = "default_capella_fork_version")] + #[serde(with = "eth2_serde_utils::bytes_4_hex")] + capella_fork_version: [u8; 4], + #[serde(default)] + #[serde(serialize_with = "serialize_fork_epoch")] + #[serde(deserialize_with = "deserialize_fork_epoch")] + pub capella_fork_epoch: Option>, + #[serde(default = "default_eip4844_fork_version")] #[serde(with = "eth2_serde_utils::bytes_4_hex")] eip4844_fork_version: [u8; 4], @@ -928,6 +983,11 @@ fn default_bellatrix_fork_version() -> [u8; 4] { [0xff, 0xff, 0xff, 0xff] } +fn default_capella_fork_version() -> [u8; 4] { + // TODO: determine if the bellatrix example should be copied like this + [0xff, 0xff, 0xff, 0xff] +} + fn default_eip4844_fork_version() -> [u8; 4] { // This value shouldn't be used. [0xff, 0xff, 0xff, 0xff] @@ -1029,6 +1089,10 @@ impl Config { bellatrix_fork_epoch: spec .bellatrix_fork_epoch .map(|epoch| MaybeQuoted { value: epoch }), + capella_fork_version: spec.capella_fork_version, + capella_fork_epoch: spec + .capella_fork_epoch + .map(|epoch| MaybeQuoted { value: epoch }), eip4844_fork_version: spec.eip4844_fork_version, eip4844_fork_epoch: spec .eip4844_fork_epoch @@ -1078,6 +1142,8 @@ impl Config { altair_fork_epoch, bellatrix_fork_epoch, bellatrix_fork_version, + capella_fork_epoch, + capella_fork_version, eip4844_fork_epoch, eip4844_fork_version, seconds_per_slot, @@ -1110,6 +1176,8 @@ impl Config { altair_fork_epoch: altair_fork_epoch.map(|q| q.value), bellatrix_fork_epoch: bellatrix_fork_epoch.map(|q| q.value), bellatrix_fork_version, + capella_fork_epoch: capella_fork_epoch.map(|q| q.value), + capella_fork_version, eip4844_fork_epoch: eip4844_fork_epoch.map(|q| q.value), eip4844_fork_version, seconds_per_slot, @@ -1204,6 +1272,14 @@ mod tests { apply_bit_mask(builder_domain_pre_mask, &spec), &spec, ); + + test_domain( + Domain::BlsToExecutionChange, + spec.domain_bls_to_execution_change, + &spec, + ); + + test_domain(Domain::BlobsSideCar, spec.domain_blobs_sidecar, &spec); } fn apply_bit_mask(domain_bytes: [u8; 4], spec: &ChainSpec) -> u32 { diff --git a/consensus/types/src/eth_spec.rs b/consensus/types/src/eth_spec.rs index 716754c7e5..4cf102bd77 100644 --- a/consensus/types/src/eth_spec.rs +++ b/consensus/types/src/eth_spec.rs @@ -95,6 +95,13 @@ pub trait EthSpec: 'static + Default + Sync + Send + Clone + Debug + PartialEq + type GasLimitDenominator: Unsigned + Clone + Sync + Send + Debug + PartialEq; type MinGasLimit: Unsigned + Clone + Sync + Send + Debug + PartialEq; type MaxExtraDataBytes: Unsigned + Clone + Sync + Send + Debug + PartialEq; + /* + * New in Capella + */ + type MaxPartialWithdrawalsPerEpoch: Unsigned + Clone + Sync + Send + Debug + PartialEq; + type WithdrawalQueueLimit: Unsigned + Clone + Sync + Send + Debug + PartialEq; + type MaxBlsToExecutionChanges: Unsigned + Clone + Sync + Send + Debug + PartialEq; + type MaxWithdrawalsPerPayload: Unsigned + Clone + Sync + Send + Debug + PartialEq; /* * New in Eip4844 */ @@ -228,10 +235,32 @@ pub trait EthSpec: 'static + Default + Sync + Send + Clone + Debug + PartialEq + Self::BytesPerLogsBloom::to_usize() } + /// Returns the `MAX_PARTIAL_WITHDRAWALS_PER_EPOCH` constant for this specification. + fn max_partial_withdrawals_per_epoch() -> usize { + Self::MaxPartialWithdrawalsPerEpoch::to_usize() + } + + /// Returns the `WITHDRAWAL_QUEUE_LIMIT` constant for this specification. + fn withdrawal_queue_limit() -> usize { + Self::WithdrawalQueueLimit::to_usize() + } + + /// Returns the `MAX_BLS_TO_EXECUTION_CHANGES` constant for this specification. + fn max_bls_to_execution_changes() -> usize { + Self::MaxBlsToExecutionChanges::to_usize() + } + + /// Returns the `MAX_WITHDRAWALS_PER_PAYLOAD` constant for this specification. + fn max_withdrawals_per_payload() -> usize { + Self::MaxWithdrawalsPerPayload::to_usize() + } + + /// Returns the `MAX_BLOBS_PER_BLOCK` constant for this specification. fn max_blobs_per_block() -> usize { Self::MaxBlobsPerBlock::to_usize() } + /// FIXME: why is this called chunks_per_blob?? fn chunks_per_blob() -> usize { Self::FieldElementsPerBlob::to_usize() } @@ -280,6 +309,10 @@ impl EthSpec for MainnetEthSpec { type SyncSubcommitteeSize = U128; // 512 committee size / 4 sync committee subnet count type MaxPendingAttestations = U4096; // 128 max attestations * 32 slots per epoch type SlotsPerEth1VotingPeriod = U2048; // 64 epochs * 32 slots per epoch + type MaxPartialWithdrawalsPerEpoch = U256; + type WithdrawalQueueLimit = U1099511627776; + type MaxBlsToExecutionChanges = U16; + type MaxWithdrawalsPerPayload = U16; fn default_spec() -> ChainSpec { ChainSpec::mainnet() @@ -325,6 +358,10 @@ impl EthSpec for MinimalEthSpec { GasLimitDenominator, MinGasLimit, MaxExtraDataBytes, + MaxPartialWithdrawalsPerEpoch, + WithdrawalQueueLimit, + MaxBlsToExecutionChanges, + MaxWithdrawalsPerPayload, MaxBlobsPerBlock, FieldElementsPerBlob }); @@ -371,6 +408,10 @@ impl EthSpec for GnosisEthSpec { type SyncSubcommitteeSize = U128; // 512 committee size / 4 sync committee subnet count type MaxPendingAttestations = U2048; // 128 max attestations * 16 slots per epoch type SlotsPerEth1VotingPeriod = U1024; // 64 epochs * 16 slots per epoch + type MaxPartialWithdrawalsPerEpoch = U256; + type WithdrawalQueueLimit = U1099511627776; + type MaxBlsToExecutionChanges = U16; + type MaxWithdrawalsPerPayload = U16; type MaxBlobsPerBlock = U16; // 2**4 = 16 type FieldElementsPerBlob = U4096; diff --git a/consensus/types/src/execution_payload.rs b/consensus/types/src/execution_payload.rs index 78a53a3675..f68e563e41 100644 --- a/consensus/types/src/execution_payload.rs +++ b/consensus/types/src/execution_payload.rs @@ -13,12 +13,35 @@ pub type Transactions = VariableList< ::MaxTransactionsPerPayload, >; -#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))] -#[derive( - Default, Debug, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom, Derivative, +#[superstruct( + variants(Merge, Capella, Eip4844), + variant_attributes( + derive( + Default, + Debug, + Clone, + Serialize, + Deserialize, + Encode, + Decode, + TreeHash, + TestRandom, + Derivative, + ), + derivative(PartialEq, Hash(bound = "T: EthSpec")), + serde(bound = "T: EthSpec", deny_unknown_fields), + cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary)) + ), + 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 = "T: EthSpec"))] +#[serde(untagged)] #[serde(bound = "T: EthSpec")] +#[ssz(enum_behaviour = "transparent")] +#[tree_hash(enum_behaviour = "transparent")] +#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))] pub struct ExecutionPayload { pub parent_hash: ExecutionBlockHash, pub fee_recipient: Address, @@ -39,28 +62,57 @@ pub struct ExecutionPayload { pub extra_data: VariableList, #[serde(with = "eth2_serde_utils::quoted_u256")] pub base_fee_per_gas: Uint256, + #[superstruct(only(Eip4844))] + #[serde(with = "eth2_serde_utils::quoted_u64")] + pub excess_blobs: u64, pub block_hash: ExecutionBlockHash, #[serde(with = "ssz_types::serde_utils::list_of_hex_var_list")] pub transactions: Transactions, + #[superstruct(only(Capella, Eip4844))] + pub withdrawals: VariableList, } impl ExecutionPayload { - pub fn empty() -> Self { - Self::default() - } - #[allow(clippy::integer_arithmetic)] /// Returns the maximum size of an execution payload. - pub fn max_execution_payload_size() -> usize { + pub fn max_execution_payload_merge_size() -> usize { // Fixed part - Self::empty().as_ssz_bytes().len() + ExecutionPayloadMerge::::default().as_ssz_bytes().len() // Max size of variable length `extra_data` field + (T::max_extra_data_bytes() * ::ssz_fixed_len()) // Max size of variable length `transactions` field + (T::max_transactions_per_payload() * (ssz::BYTES_PER_LENGTH_OFFSET + T::max_bytes_per_transaction())) } + #[allow(clippy::integer_arithmetic)] + /// Returns the maximum size of an execution payload. + pub fn max_execution_payload_capella_size() -> usize { + // Fixed part + ExecutionPayloadCapella::::default().as_ssz_bytes().len() + // Max size of variable length `extra_data` field + + (T::max_extra_data_bytes() * ::ssz_fixed_len()) + // Max size of variable length `transactions` field + + (T::max_transactions_per_payload() * (ssz::BYTES_PER_LENGTH_OFFSET + T::max_bytes_per_transaction())) + // Max size of variable length `withdrawals` field + // TODO: check this + + (T::max_withdrawals_per_payload() * (ssz::BYTES_PER_LENGTH_OFFSET + ::ssz_fixed_len())) + } + + #[allow(clippy::integer_arithmetic)] + /// Returns the maximum size of an execution payload. + pub fn max_execution_payload_eip4844_size() -> usize { + // Fixed part + ExecutionPayloadEip4844::::default().as_ssz_bytes().len() + // Max size of variable length `extra_data` field + + (T::max_extra_data_bytes() * ::ssz_fixed_len()) + // Max size of variable length `transactions` field + + (T::max_transactions_per_payload() * (ssz::BYTES_PER_LENGTH_OFFSET + T::max_bytes_per_transaction())) + // Max size of variable length `withdrawals` field + // TODO: check this + + (T::max_withdrawals_per_payload() * (ssz::BYTES_PER_LENGTH_OFFSET + ::ssz_fixed_len())) + } + pub fn blob_txns_iter(&self) -> Iter<'_, Transaction> { - self.transactions.iter() + self.transactions().iter() } } diff --git a/consensus/types/src/execution_payload_header.rs b/consensus/types/src/execution_payload_header.rs index 01780fa1c3..f92ab956e1 100644 --- a/consensus/types/src/execution_payload_header.rs +++ b/consensus/types/src/execution_payload_header.rs @@ -5,12 +5,37 @@ use ssz_derive::{Decode, Encode}; use test_random_derive::TestRandom; use tree_hash::TreeHash; use tree_hash_derive::TreeHash; +use BeaconStateError; -#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))] -#[derive( - Default, Debug, Clone, Serialize, Deserialize, Derivative, Encode, Decode, TreeHash, TestRandom, +#[superstruct( + variants(Merge, Capella, Eip4844), + variant_attributes( + derive( + Default, + Debug, + Clone, + Serialize, + Deserialize, + Encode, + Decode, + TreeHash, + TestRandom, + Derivative, + ), + derivative(PartialEq, Hash(bound = "T: EthSpec")), + serde(bound = "T: EthSpec", deny_unknown_fields), + cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary)) + ), + ref_attributes(derive(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, Deserialize, Encode, TreeHash, Derivative)] #[derivative(PartialEq, Hash(bound = "T: EthSpec"))] +#[serde(bound = "T: EthSpec")] +#[tree_hash(enum_behaviour = "transparent")] +#[ssz(enum_behaviour = "transparent")] +#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))] pub struct ExecutionPayloadHeader { pub parent_hash: ExecutionBlockHash, pub fee_recipient: Address, @@ -31,33 +56,107 @@ pub struct ExecutionPayloadHeader { pub extra_data: VariableList, #[serde(with = "eth2_serde_utils::quoted_u256")] pub base_fee_per_gas: Uint256, + #[superstruct(only(Eip4844))] + #[serde(with = "eth2_serde_utils::quoted_u64")] + pub excess_blobs: u64, pub block_hash: ExecutionBlockHash, pub transactions_root: Hash256, + #[superstruct(only(Capella, Eip4844))] + pub withdrawals_root: Hash256, } -impl ExecutionPayloadHeader { - pub fn empty() -> Self { - Self::default() - } -} - -impl<'a, T: EthSpec> From<&'a ExecutionPayload> for ExecutionPayloadHeader { - fn from(payload: &'a ExecutionPayload) -> Self { - ExecutionPayloadHeader { +impl From> for ExecutionPayloadHeaderMerge { + fn from(payload: ExecutionPayloadMerge) -> 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(), + logs_bloom: payload.logs_bloom, 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(), + extra_data: payload.extra_data, base_fee_per_gas: payload.base_fee_per_gas, block_hash: payload.block_hash, transactions_root: payload.transactions.tree_hash_root(), } } } +impl From> for ExecutionPayloadHeaderCapella { + fn from(payload: ExecutionPayloadCapella) -> 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, + 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, + 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(), + } + } +} +impl From> for ExecutionPayloadHeaderEip4844 { + fn from(payload: ExecutionPayloadEip4844) -> 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, + 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, + base_fee_per_gas: payload.base_fee_per_gas, + excess_blobs: payload.excess_blobs, + block_hash: payload.block_hash, + transactions_root: payload.transactions.tree_hash_root(), + withdrawals_root: payload.withdrawals.tree_hash_root(), + } + } +} + +impl TryFrom> for ExecutionPayloadHeaderMerge { + type Error = BeaconStateError; + fn try_from(header: ExecutionPayloadHeader) -> Result { + match header { + ExecutionPayloadHeader::Merge(execution_payload_header) => Ok(execution_payload_header), + _ => Err(BeaconStateError::IncorrectStateVariant), + } + } +} +impl TryFrom> for ExecutionPayloadHeaderCapella { + type Error = BeaconStateError; + fn try_from(header: ExecutionPayloadHeader) -> Result { + match header { + ExecutionPayloadHeader::Capella(execution_payload_header) => { + Ok(execution_payload_header) + } + _ => Err(BeaconStateError::IncorrectStateVariant), + } + } +} +impl TryFrom> for ExecutionPayloadHeaderEip4844 { + type Error = BeaconStateError; + fn try_from(header: ExecutionPayloadHeader) -> Result { + match header { + ExecutionPayloadHeader::Eip4844(execution_payload_header) => { + Ok(execution_payload_header) + } + _ => Err(BeaconStateError::IncorrectStateVariant), + } + } +} diff --git a/consensus/types/src/fork_name.rs b/consensus/types/src/fork_name.rs index dc45565d41..42b8bdded7 100644 --- a/consensus/types/src/fork_name.rs +++ b/consensus/types/src/fork_name.rs @@ -11,6 +11,7 @@ pub enum ForkName { Base, Altair, Merge, + Capella, Eip4844, } @@ -20,6 +21,7 @@ impl ForkName { ForkName::Base, ForkName::Altair, ForkName::Merge, + ForkName::Capella, ForkName::Eip4844, ] } @@ -32,24 +34,35 @@ impl ForkName { ForkName::Base => { spec.altair_fork_epoch = None; spec.bellatrix_fork_epoch = None; + spec.capella_fork_epoch = None; spec.eip4844_fork_epoch = None; spec } ForkName::Altair => { spec.altair_fork_epoch = Some(Epoch::new(0)); spec.bellatrix_fork_epoch = None; + spec.capella_fork_epoch = None; spec.eip4844_fork_epoch = None; spec } ForkName::Merge => { spec.altair_fork_epoch = Some(Epoch::new(0)); spec.bellatrix_fork_epoch = Some(Epoch::new(0)); + spec.capella_fork_epoch = None; + spec.eip4844_fork_epoch = None; + spec + } + ForkName::Capella => { + spec.altair_fork_epoch = Some(Epoch::new(0)); + spec.bellatrix_fork_epoch = Some(Epoch::new(0)); + spec.capella_fork_epoch = Some(Epoch::new(0)); spec.eip4844_fork_epoch = None; spec } ForkName::Eip4844 => { spec.altair_fork_epoch = Some(Epoch::new(0)); spec.bellatrix_fork_epoch = Some(Epoch::new(0)); + spec.capella_fork_epoch = Some(Epoch::new(0)); spec.eip4844_fork_epoch = Some(Epoch::new(0)); spec } @@ -64,7 +77,8 @@ impl ForkName { ForkName::Base => None, ForkName::Altair => Some(ForkName::Base), ForkName::Merge => Some(ForkName::Altair), - ForkName::Eip4844 => Some(ForkName::Merge), + ForkName::Capella => Some(ForkName::Merge), + ForkName::Eip4844 => Some(ForkName::Capella), } } @@ -75,7 +89,8 @@ impl ForkName { match self { ForkName::Base => Some(ForkName::Altair), ForkName::Altair => Some(ForkName::Merge), - ForkName::Merge => Some(ForkName::Eip4844), + ForkName::Merge => Some(ForkName::Capella), + ForkName::Capella => Some(ForkName::Eip4844), ForkName::Eip4844 => None, } } @@ -118,6 +133,10 @@ macro_rules! map_fork_name_with { let (value, extra_data) = $body; ($t::Merge(value), extra_data) } + ForkName::Capella => { + let (value, extra_data) = $body; + ($t::Capella(value), extra_data) + } ForkName::Eip4844 => { let (value, extra_data) = $body; ($t::Eip4844(value), extra_data) @@ -134,6 +153,7 @@ impl FromStr for ForkName { "phase0" | "base" => ForkName::Base, "altair" => ForkName::Altair, "bellatrix" | "merge" => ForkName::Merge, + "capella" => ForkName::Capella, "eip4844" => ForkName::Eip4844, _ => return Err(format!("unknown fork name: {}", fork_name)), }) @@ -146,6 +166,7 @@ impl Display for ForkName { ForkName::Base => "phase0".fmt(f), ForkName::Altair => "altair".fmt(f), ForkName::Merge => "bellatrix".fmt(f), + ForkName::Capella => "capella".fmt(f), ForkName::Eip4844 => "eip4844".fmt(f), } } diff --git a/consensus/types/src/lib.rs b/consensus/types/src/lib.rs index 4bb383dfba..527b54f478 100644 --- a/consensus/types/src/lib.rs +++ b/consensus/types/src/lib.rs @@ -1,5 +1,5 @@ //! Ethereum 2.0 types - +#![feature(generic_associated_types)] // Required for big type-level numbers #![recursion_limit = "128"] // Clippy lint set up @@ -85,6 +85,7 @@ pub mod sync_selection_proof; pub mod sync_subnet_id; mod tree_hash_impls; pub mod validator_registration_data; +pub mod withdrawal; pub mod slot_data; #[cfg(feature = "sqlite")] @@ -105,12 +106,12 @@ pub use crate::attestation_data::AttestationData; pub use crate::attestation_duty::AttestationDuty; pub use crate::attester_slashing::AttesterSlashing; pub use crate::beacon_block::{ - BeaconBlock, BeaconBlockAltair, BeaconBlockBase, BeaconBlockEip4844, BeaconBlockMerge, - BeaconBlockRef, BeaconBlockRefMut, + BeaconBlock, BeaconBlockAltair, BeaconBlockBase, BeaconBlockCapella, BeaconBlockEip4844, + BeaconBlockMerge, BeaconBlockRef, BeaconBlockRefMut, BlindedBeaconBlock, }; pub use crate::beacon_block_body::{ - BeaconBlockBody, BeaconBlockBodyAltair, BeaconBlockBodyBase, BeaconBlockBodyEip4844, - BeaconBlockBodyMerge, BeaconBlockBodyRef, BeaconBlockBodyRefMut, + BeaconBlockBody, BeaconBlockBodyAltair, BeaconBlockBodyBase, BeaconBlockBodyCapella, + BeaconBlockBodyEip4844, BeaconBlockBodyMerge, BeaconBlockBodyRef, BeaconBlockBodyRefMut, }; pub use crate::beacon_block_header::BeaconBlockHeader; pub use crate::beacon_committee::{BeaconCommittee, OwnedBeaconCommittee}; @@ -129,8 +130,14 @@ pub use crate::enr_fork_id::EnrForkId; pub use crate::eth1_data::Eth1Data; pub use crate::eth_spec::EthSpecId; pub use crate::execution_block_hash::ExecutionBlockHash; -pub use crate::execution_payload::{ExecutionPayload, Transaction, Transactions}; -pub use crate::execution_payload_header::ExecutionPayloadHeader; +pub use crate::execution_payload::{ + ExecutionPayload, ExecutionPayloadCapella, ExecutionPayloadEip4844, ExecutionPayloadMerge, + ExecutionPayloadRef, Transaction, Transactions, +}; +pub use crate::execution_payload_header::{ + ExecutionPayloadHeader, ExecutionPayloadHeaderCapella, ExecutionPayloadHeaderEip4844, + ExecutionPayloadHeaderMerge, ExecutionPayloadHeaderRef, ExecutionPayloadHeaderRefMut, +}; pub use crate::fork::Fork; pub use crate::fork_context::ForkContext; pub use crate::fork_data::ForkData; @@ -143,7 +150,11 @@ pub use crate::kzg_commitment::KzgCommitment; pub use crate::kzg_proof::KzgProof; pub use crate::participation_flags::ParticipationFlags; pub use crate::participation_list::ParticipationList; -pub use crate::payload::{BlindedPayload, BlockType, ExecPayload, FullPayload}; +pub use crate::payload::{ + AbstractExecPayload, BlindedPayload, BlindedPayloadCapella, BlindedPayloadEip4844, + BlindedPayloadMerge, BlindedPayloadRef, BlockType, ExecPayload, FullPayload, + FullPayloadCapella, FullPayloadEip4844, FullPayloadMerge, FullPayloadRef, +}; pub use crate::pending_attestation::PendingAttestation; pub use crate::preset::{AltairPreset, BasePreset, BellatrixPreset}; pub use crate::proposer_preparation_data::ProposerPreparationData; @@ -176,6 +187,7 @@ pub use crate::validator::Validator; pub use crate::validator_registration_data::*; pub use crate::validator_subscription::ValidatorSubscription; pub use crate::voluntary_exit::VoluntaryExit; +pub use crate::withdrawal::Withdrawal; use serde_big_array::BigArray; pub type CommitteeIndex = u64; diff --git a/consensus/types/src/payload.rs b/consensus/types/src/payload.rs index 5004d65a9e..d3a8fd698a 100644 --- a/consensus/types/src/payload.rs +++ b/consensus/types/src/payload.rs @@ -1,14 +1,15 @@ use crate::{test_utils::TestRandom, *}; -use core::hash::Hasher; use derivative::Derivative; use serde::de::DeserializeOwned; use serde::{Deserialize, Serialize}; -use ssz::{Decode, DecodeError, Encode}; +use ssz::{Decode, Encode}; +use ssz_derive::{Decode, Encode}; use std::convert::TryFrom; use std::fmt::Debug; use std::hash::Hash; use test_random_derive::TestRandom; -use tree_hash::{PackedEncoding, TreeHash}; +use tree_hash::TreeHash; +use tree_hash_derive::TreeHash; #[derive(Debug)] pub enum BlockType { @@ -16,24 +17,8 @@ pub enum BlockType { Full, } -pub trait ExecPayload: - Debug - + Clone - + Encode - + Debug - + Decode - + TestRandom - + TreeHash - + Default - + PartialEq - + Serialize - + DeserializeOwned - + Hash - + TryFrom> - + From> - + Send - + 'static -{ +// + TryFrom> +pub trait ExecPayload: Debug + Clone + PartialEq + Hash + TreeHash + Send { fn block_type() -> BlockType; /// Convert the payload into a payload header. @@ -49,15 +34,109 @@ pub trait ExecPayload: fn block_hash(&self) -> ExecutionBlockHash; fn fee_recipient(&self) -> Address; fn gas_limit(&self) -> u64; + + // Is this a default payload? (pre-merge) + fn is_default(&self) -> bool; } -impl ExecPayload for FullPayload { +pub trait OwnedExecPayload: + ExecPayload + Default + Serialize + DeserializeOwned + Encode + Decode + TestRandom + 'static +{ +} + +impl OwnedExecPayload for P where + P: ExecPayload + + Default + + Serialize + + DeserializeOwned + + Encode + + Decode + + TestRandom + + 'static +{ +} + +pub trait AbstractExecPayload: + ExecPayload + Sized + From> + TryFrom> +{ + type Ref<'a>: ExecPayload + + Copy + + From<&'a Self::Merge> + + From<&'a Self::Capella> + + From<&'a Self::Eip4844>; + + type Merge: OwnedExecPayload + + Into + + From> + + TryFrom>; + type Capella: OwnedExecPayload + + Into + + From> + + TryFrom>; + type Eip4844: OwnedExecPayload + + Into + + From> + + TryFrom>; +} + +#[superstruct( + variants(Merge, Capella, Eip4844), + variant_attributes( + derive( + Debug, + Clone, + Serialize, + Deserialize, + Encode, + Decode, + TestRandom, + TreeHash, + Derivative, + ), + derivative(PartialEq, Hash(bound = "T: EthSpec")), + serde(bound = "T: EthSpec", deny_unknown_fields), + cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary)) + ), + ref_attributes( + derive(Debug, Derivative, TreeHash), + derivative(PartialEq, Hash(bound = "T: EthSpec")), + tree_hash(enum_behaviour = "transparent"), + ), + cast_error(ty = "Error", expr = "BeaconStateError::IncorrectStateVariant"), + partial_getter_error(ty = "Error", expr = "BeaconStateError::IncorrectStateVariant") +)] +#[derive(Debug, Clone, Serialize, Deserialize, TreeHash, Derivative)] +#[derivative(PartialEq, Hash(bound = "T: EthSpec"))] +#[serde(bound = "T: EthSpec")] +#[tree_hash(enum_behaviour = "transparent")] +pub struct FullPayload { + #[superstruct(only(Merge), partial_getter(rename = "execution_payload_merge"))] + pub execution_payload: ExecutionPayloadMerge, + #[superstruct(only(Capella), partial_getter(rename = "execution_payload_capella"))] + pub execution_payload: ExecutionPayloadCapella, + #[superstruct(only(Eip4844), partial_getter(rename = "execution_payload_eip4844"))] + pub execution_payload: ExecutionPayloadEip4844, +} + +impl From> for ExecutionPayload { + fn from(full_payload: FullPayload) -> Self { + match full_payload { + FullPayload::Merge(payload) => ExecutionPayload::Merge(payload.execution_payload), + FullPayload::Capella(payload) => ExecutionPayload::Capella(payload.execution_payload), + FullPayload::Eip4844(payload) => ExecutionPayload::Eip4844(payload.execution_payload), + } + } +} + +impl ExecPayload for FullPayloadMerge { fn block_type() -> BlockType { BlockType::Full } fn to_execution_payload_header(&self) -> ExecutionPayloadHeader { - ExecutionPayloadHeader::from(&self.execution_payload) + ExecutionPayloadHeader::Merge(ExecutionPayloadHeaderMerge::from( + self.execution_payload.clone(), + )) } fn parent_hash(&self) -> ExecutionBlockHash { @@ -87,6 +166,381 @@ impl ExecPayload for FullPayload { fn gas_limit(&self) -> u64 { self.execution_payload.gas_limit } + + // TODO: can this function be optimized? + fn is_default(&self) -> bool { + self.execution_payload == ExecutionPayloadMerge::default() + } +} +impl ExecPayload for FullPayloadCapella { + fn block_type() -> BlockType { + BlockType::Full + } + + fn to_execution_payload_header(&self) -> ExecutionPayloadHeader { + ExecutionPayloadHeader::Capella(ExecutionPayloadHeaderCapella::from( + self.execution_payload.clone(), + )) + } + + fn parent_hash(&self) -> ExecutionBlockHash { + self.execution_payload.parent_hash + } + + fn prev_randao(&self) -> Hash256 { + self.execution_payload.prev_randao + } + + fn block_number(&self) -> u64 { + self.execution_payload.block_number + } + + fn timestamp(&self) -> u64 { + self.execution_payload.timestamp + } + + fn block_hash(&self) -> ExecutionBlockHash { + self.execution_payload.block_hash + } + + fn fee_recipient(&self) -> Address { + self.execution_payload.fee_recipient + } + + fn gas_limit(&self) -> u64 { + self.execution_payload.gas_limit + } + + // TODO: can this function be optimized? + fn is_default(&self) -> bool { + self.execution_payload == ExecutionPayloadCapella::default() + } +} +impl ExecPayload for FullPayloadEip4844 { + fn block_type() -> BlockType { + BlockType::Full + } + + fn to_execution_payload_header(&self) -> ExecutionPayloadHeader { + ExecutionPayloadHeader::Eip4844(ExecutionPayloadHeaderEip4844::from( + self.execution_payload.clone(), + )) + } + + fn parent_hash(&self) -> ExecutionBlockHash { + self.execution_payload.parent_hash + } + + fn prev_randao(&self) -> Hash256 { + self.execution_payload.prev_randao + } + + fn block_number(&self) -> u64 { + self.execution_payload.block_number + } + + fn timestamp(&self) -> u64 { + self.execution_payload.timestamp + } + + fn block_hash(&self) -> ExecutionBlockHash { + self.execution_payload.block_hash + } + + fn fee_recipient(&self) -> Address { + self.execution_payload.fee_recipient + } + + fn gas_limit(&self) -> u64 { + self.execution_payload.gas_limit + } + + // TODO: can this function be optimized? + fn is_default(&self) -> bool { + self.execution_payload == ExecutionPayloadEip4844::default() + } +} + +impl ExecPayload for FullPayload { + fn block_type() -> BlockType { + BlockType::Full + } + + fn to_execution_payload_header(&self) -> ExecutionPayloadHeader { + match self { + Self::Merge(payload) => payload.to_execution_payload_header(), + Self::Capella(payload) => payload.to_execution_payload_header(), + Self::Eip4844(payload) => payload.to_execution_payload_header(), + } + } + + fn parent_hash<'a>(&'a self) -> ExecutionBlockHash { + map_full_payload_ref!(&'a _, self.to_ref(), move |payload, cons| { + cons(payload); + payload.execution_payload.parent_hash + }) + } + + fn prev_randao<'a>(&'a self) -> Hash256 { + map_full_payload_ref!(&'a _, self.to_ref(), move |payload, cons| { + cons(payload); + payload.execution_payload.prev_randao + }) + } + + fn block_number<'a>(&'a self) -> u64 { + map_full_payload_ref!(&'a _, self.to_ref(), move |payload, cons| { + cons(payload); + payload.execution_payload.block_number + }) + } + + fn timestamp<'a>(&'a self) -> u64 { + map_full_payload_ref!(&'a _, self.to_ref(), move |payload, cons| { + cons(payload); + payload.execution_payload.timestamp + }) + } + + fn block_hash<'a>(&'a self) -> ExecutionBlockHash { + map_full_payload_ref!(&'a _, self.to_ref(), move |payload, cons| { + cons(payload); + payload.execution_payload.block_hash + }) + } + + fn fee_recipient<'a>(&'a self) -> Address { + map_full_payload_ref!(&'a _, self.to_ref(), move |payload, cons| { + cons(payload); + payload.execution_payload.fee_recipient + }) + } + + fn gas_limit<'a>(&'a self) -> u64 { + map_full_payload_ref!(&'a _, self.to_ref(), move |payload, cons| { + cons(payload); + payload.execution_payload.gas_limit + }) + } + + fn is_default(&self) -> bool { + match self { + Self::Merge(payload) => payload.is_default(), + Self::Capella(payload) => payload.is_default(), + Self::Eip4844(payload) => payload.is_default(), + } + } +} + +impl FullPayload { + pub fn execution_payload(&self) -> ExecutionPayload { + match self { + Self::Merge(full) => ExecutionPayload::Merge(full.execution_payload.clone()), + Self::Capella(full) => ExecutionPayload::Capella(full.execution_payload.clone()), + Self::Eip4844(full) => ExecutionPayload::Eip4844(full.execution_payload.clone()), + } + } +} + +impl<'b, T: EthSpec> ExecPayload for FullPayloadRef<'b, T> { + fn block_type() -> BlockType { + BlockType::Full + } + + fn to_execution_payload_header(&self) -> ExecutionPayloadHeader { + match self { + Self::Merge(payload) => payload.to_execution_payload_header(), + Self::Capella(payload) => payload.to_execution_payload_header(), + Self::Eip4844(payload) => payload.to_execution_payload_header(), + } + } + + fn parent_hash<'a>(&'a self) -> ExecutionBlockHash { + map_full_payload_ref!(&'a _, self, move |payload, cons| { + cons(payload); + payload.execution_payload.parent_hash + }) + } + + fn prev_randao<'a>(&'a self) -> Hash256 { + map_full_payload_ref!(&'a _, self, move |payload, cons| { + cons(payload); + payload.execution_payload.prev_randao + }) + } + + fn block_number<'a>(&'a self) -> u64 { + map_full_payload_ref!(&'a _, self, move |payload, cons| { + cons(payload); + payload.execution_payload.block_number + }) + } + + fn timestamp<'a>(&'a self) -> u64 { + map_full_payload_ref!(&'a _, self, move |payload, cons| { + cons(payload); + payload.execution_payload.timestamp + }) + } + + fn block_hash<'a>(&'a self) -> ExecutionBlockHash { + map_full_payload_ref!(&'a _, self, move |payload, cons| { + cons(payload); + payload.execution_payload.block_hash + }) + } + + fn fee_recipient<'a>(&'a self) -> Address { + map_full_payload_ref!(&'a _, self, move |payload, cons| { + cons(payload); + payload.execution_payload.fee_recipient + }) + } + + fn gas_limit<'a>(&'a self) -> u64 { + map_full_payload_ref!(&'a _, self, move |payload, cons| { + cons(payload); + payload.execution_payload.gas_limit + }) + } + + // TODO: can this function be optimized? + fn is_default<'a>(&'a self) -> bool { + match self { + Self::Merge(payload_ref) => { + payload_ref.execution_payload == ExecutionPayloadMerge::default() + } + Self::Capella(payload_ref) => { + payload_ref.execution_payload == ExecutionPayloadCapella::default() + } + Self::Eip4844(payload_ref) => { + payload_ref.execution_payload == ExecutionPayloadEip4844::default() + } + } + } +} + +impl AbstractExecPayload for FullPayload { + type Ref<'a> = FullPayloadRef<'a, T>; + type Merge = FullPayloadMerge; + type Capella = FullPayloadCapella; + type Eip4844 = FullPayloadEip4844; +} + +impl From> for FullPayload { + fn from(execution_payload: ExecutionPayload) -> Self { + match execution_payload { + ExecutionPayload::Merge(execution_payload) => { + Self::Merge(FullPayloadMerge { execution_payload }) + } + ExecutionPayload::Capella(execution_payload) => { + Self::Capella(FullPayloadCapella { execution_payload }) + } + ExecutionPayload::Eip4844(execution_payload) => { + Self::Eip4844(FullPayloadEip4844 { execution_payload }) + } + } + } +} + +impl TryFrom> for FullPayload { + type Error = (); + fn try_from(_: ExecutionPayloadHeader) -> Result { + Err(()) + } +} + +impl From> for FullPayloadMerge { + fn from(execution_payload: ExecutionPayloadMerge) -> Self { + Self { execution_payload } + } +} +impl From> for FullPayloadCapella { + fn from(execution_payload: ExecutionPayloadCapella) -> Self { + Self { execution_payload } + } +} +impl From> for FullPayloadEip4844 { + fn from(execution_payload: ExecutionPayloadEip4844) -> Self { + Self { execution_payload } + } +} + +impl TryFrom> for FullPayloadMerge { + type Error = (); + fn try_from(_: ExecutionPayloadHeader) -> Result { + Err(()) + } +} +impl TryFrom> for FullPayloadCapella { + type Error = (); + fn try_from(_: ExecutionPayloadHeader) -> Result { + Err(()) + } +} +impl TryFrom> for FullPayloadEip4844 { + type Error = (); + fn try_from(_: ExecutionPayloadHeader) -> Result { + Err(()) + } +} + +impl TryFrom> for FullPayloadMerge { + type Error = (); + fn try_from(_: ExecutionPayloadHeaderMerge) -> Result { + Err(()) + } +} +impl TryFrom> for FullPayloadCapella { + type Error = (); + fn try_from(_: ExecutionPayloadHeaderCapella) -> Result { + Err(()) + } +} +impl TryFrom> for FullPayloadEip4844 { + type Error = (); + fn try_from(_: ExecutionPayloadHeaderEip4844) -> Result { + Err(()) + } +} + +#[superstruct( + variants(Merge, Capella, Eip4844), + variant_attributes( + derive( + Debug, + Clone, + Serialize, + Deserialize, + Encode, + Decode, + TestRandom, + TreeHash, + Derivative, + ), + derivative(PartialEq, Hash(bound = "T: EthSpec")), + serde(bound = "T: EthSpec", deny_unknown_fields), + cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary)) + ), + ref_attributes( + derive(Debug, Derivative, TreeHash), + derivative(PartialEq, Hash(bound = "T: EthSpec")), + tree_hash(enum_behaviour = "transparent"), + ), + cast_error(ty = "Error", expr = "BeaconStateError::IncorrectStateVariant"), + partial_getter_error(ty = "Error", expr = "BeaconStateError::IncorrectStateVariant") +)] +#[derive(Debug, Clone, Serialize, Deserialize, TreeHash, Derivative)] +#[derivative(PartialEq, Hash(bound = "T: EthSpec"))] +#[serde(bound = "T: EthSpec")] +#[tree_hash(enum_behaviour = "transparent")] +pub struct BlindedPayload { + #[superstruct(only(Merge), partial_getter(rename = "execution_payload_merge"))] + pub execution_payload_header: ExecutionPayloadHeaderMerge, + #[superstruct(only(Capella), partial_getter(rename = "execution_payload_capella"))] + pub execution_payload_header: ExecutionPayloadHeaderCapella, + #[superstruct(only(Eip4844), partial_getter(rename = "execution_payload_eip4844"))] + pub execution_payload_header: ExecutionPayloadHeaderEip4844, } impl ExecPayload for BlindedPayload { @@ -95,7 +549,197 @@ impl ExecPayload for BlindedPayload { } fn to_execution_payload_header(&self) -> ExecutionPayloadHeader { - self.execution_payload_header.clone() + match self { + Self::Merge(payload) => { + ExecutionPayloadHeader::Merge(payload.execution_payload_header.clone()) + } + Self::Capella(payload) => { + ExecutionPayloadHeader::Capella(payload.execution_payload_header.clone()) + } + Self::Eip4844(payload) => { + ExecutionPayloadHeader::Eip4844(payload.execution_payload_header.clone()) + } + } + } + + fn parent_hash(&self) -> ExecutionBlockHash { + match self { + Self::Merge(payload) => payload.execution_payload_header.parent_hash, + Self::Capella(payload) => payload.execution_payload_header.parent_hash, + Self::Eip4844(payload) => payload.execution_payload_header.parent_hash, + } + } + + fn prev_randao(&self) -> Hash256 { + match self { + Self::Merge(payload) => payload.execution_payload_header.prev_randao, + Self::Capella(payload) => payload.execution_payload_header.prev_randao, + Self::Eip4844(payload) => payload.execution_payload_header.prev_randao, + } + } + + fn block_number(&self) -> u64 { + match self { + Self::Merge(payload) => payload.execution_payload_header.block_number, + Self::Capella(payload) => payload.execution_payload_header.block_number, + Self::Eip4844(payload) => payload.execution_payload_header.block_number, + } + } + + fn timestamp(&self) -> u64 { + match self { + Self::Merge(payload) => payload.execution_payload_header.timestamp, + Self::Capella(payload) => payload.execution_payload_header.timestamp, + Self::Eip4844(payload) => payload.execution_payload_header.timestamp, + } + } + + fn block_hash(&self) -> ExecutionBlockHash { + match self { + Self::Merge(payload) => payload.execution_payload_header.block_hash, + Self::Capella(payload) => payload.execution_payload_header.block_hash, + Self::Eip4844(payload) => payload.execution_payload_header.block_hash, + } + } + + fn fee_recipient(&self) -> Address { + match self { + Self::Merge(payload) => payload.execution_payload_header.fee_recipient, + Self::Capella(payload) => payload.execution_payload_header.fee_recipient, + Self::Eip4844(payload) => payload.execution_payload_header.fee_recipient, + } + } + + fn gas_limit(&self) -> u64 { + match self { + Self::Merge(payload) => payload.execution_payload_header.gas_limit, + Self::Capella(payload) => payload.execution_payload_header.gas_limit, + Self::Eip4844(payload) => payload.execution_payload_header.gas_limit, + } + } + + // TODO: can this function be optimized? + fn is_default(&self) -> bool { + match self { + /* + Self::Merge(payload) => { + payload.execution_payload_header == ExecutionPayloadHeaderMerge::default() + } + Self::Capella(payload) => { + payload.execution_payload_header == ExecutionPayloadHeaderCapella::default() + } + Self::Eip4844(payload) => { + payload.execution_payload_header == ExecutionPayloadHeaderEip4844::default() + } + */ + Self::Merge(payload) => payload.is_default(), + Self::Capella(payload) => payload.is_default(), + Self::Eip4844(payload) => payload.is_default(), + } + } +} + +// FIXME(sproul): deduplicate this +impl<'b, T: EthSpec> ExecPayload for BlindedPayloadRef<'b, T> { + fn block_type() -> BlockType { + BlockType::Blinded + } + + fn to_execution_payload_header(&self) -> ExecutionPayloadHeader { + match self { + Self::Merge(payload) => { + ExecutionPayloadHeader::Merge(payload.execution_payload_header.clone()) + } + Self::Capella(payload) => { + ExecutionPayloadHeader::Capella(payload.execution_payload_header.clone()) + } + Self::Eip4844(payload) => { + ExecutionPayloadHeader::Eip4844(payload.execution_payload_header.clone()) + } + } + } + + fn parent_hash(&self) -> ExecutionBlockHash { + match self { + Self::Merge(payload) => payload.execution_payload_header.parent_hash, + Self::Capella(payload) => payload.execution_payload_header.parent_hash, + Self::Eip4844(payload) => payload.execution_payload_header.parent_hash, + } + } + + fn prev_randao(&self) -> Hash256 { + match self { + Self::Merge(payload) => payload.execution_payload_header.prev_randao, + Self::Capella(payload) => payload.execution_payload_header.prev_randao, + Self::Eip4844(payload) => payload.execution_payload_header.prev_randao, + } + } + + fn block_number(&self) -> u64 { + match self { + Self::Merge(payload) => payload.execution_payload_header.block_number, + Self::Capella(payload) => payload.execution_payload_header.block_number, + Self::Eip4844(payload) => payload.execution_payload_header.block_number, + } + } + + fn timestamp(&self) -> u64 { + match self { + Self::Merge(payload) => payload.execution_payload_header.timestamp, + Self::Capella(payload) => payload.execution_payload_header.timestamp, + Self::Eip4844(payload) => payload.execution_payload_header.timestamp, + } + } + + fn block_hash(&self) -> ExecutionBlockHash { + match self { + Self::Merge(payload) => payload.execution_payload_header.block_hash, + Self::Capella(payload) => payload.execution_payload_header.block_hash, + Self::Eip4844(payload) => payload.execution_payload_header.block_hash, + } + } + + fn fee_recipient(&self) -> Address { + match self { + Self::Merge(payload) => payload.execution_payload_header.fee_recipient, + Self::Capella(payload) => payload.execution_payload_header.fee_recipient, + Self::Eip4844(payload) => payload.execution_payload_header.fee_recipient, + } + } + + fn gas_limit(&self) -> u64 { + match self { + Self::Merge(payload) => payload.execution_payload_header.gas_limit, + Self::Capella(payload) => payload.execution_payload_header.gas_limit, + Self::Eip4844(payload) => payload.execution_payload_header.gas_limit, + } + } + + // TODO: can this function be optimized? + fn is_default<'a>(&'a self) -> bool { + match self { + Self::Merge(payload) => { + payload.execution_payload_header == ExecutionPayloadHeaderMerge::default() + } + Self::Capella(payload) => { + payload.execution_payload_header == ExecutionPayloadHeaderCapella::default() + } + Self::Eip4844(payload) => { + payload.execution_payload_header == ExecutionPayloadHeaderEip4844::default() + } + } + } +} + +impl ExecPayload for BlindedPayloadMerge { + fn block_type() -> BlockType { + BlockType::Full + } + + fn to_execution_payload_header(&self) -> ExecutionPayloadHeader { + ExecutionPayloadHeader::Merge(ExecutionPayloadHeaderMerge::from( + self.execution_payload_header.clone(), + )) } fn parent_hash(&self) -> ExecutionBlockHash { @@ -125,13 +769,125 @@ impl ExecPayload for BlindedPayload { fn gas_limit(&self) -> u64 { self.execution_payload_header.gas_limit } + + fn is_default(&self) -> bool { + self.execution_payload_header == ExecutionPayloadHeaderMerge::default() + } +} +impl ExecPayload for BlindedPayloadCapella { + fn block_type() -> BlockType { + BlockType::Full + } + + fn to_execution_payload_header(&self) -> ExecutionPayloadHeader { + ExecutionPayloadHeader::Capella(ExecutionPayloadHeaderCapella::from( + self.execution_payload_header.clone(), + )) + } + + fn parent_hash(&self) -> ExecutionBlockHash { + self.execution_payload_header.parent_hash + } + + fn prev_randao(&self) -> Hash256 { + self.execution_payload_header.prev_randao + } + + fn block_number(&self) -> u64 { + self.execution_payload_header.block_number + } + + fn timestamp(&self) -> u64 { + self.execution_payload_header.timestamp + } + + fn block_hash(&self) -> ExecutionBlockHash { + self.execution_payload_header.block_hash + } + + fn fee_recipient(&self) -> Address { + self.execution_payload_header.fee_recipient + } + + fn gas_limit(&self) -> u64 { + self.execution_payload_header.gas_limit + } + + fn is_default(&self) -> bool { + self.execution_payload_header == ExecutionPayloadHeaderCapella::default() + } +} +impl ExecPayload for BlindedPayloadEip4844 { + fn block_type() -> BlockType { + BlockType::Full + } + + fn to_execution_payload_header(&self) -> ExecutionPayloadHeader { + ExecutionPayloadHeader::Eip4844(ExecutionPayloadHeaderEip4844::from( + self.execution_payload_header.clone(), + )) + } + + fn parent_hash(&self) -> ExecutionBlockHash { + self.execution_payload_header.parent_hash + } + + fn prev_randao(&self) -> Hash256 { + self.execution_payload_header.prev_randao + } + + fn block_number(&self) -> u64 { + self.execution_payload_header.block_number + } + + fn timestamp(&self) -> u64 { + self.execution_payload_header.timestamp + } + + fn block_hash(&self) -> ExecutionBlockHash { + self.execution_payload_header.block_hash + } + + fn fee_recipient(&self) -> Address { + self.execution_payload_header.fee_recipient + } + + fn gas_limit(&self) -> u64 { + self.execution_payload_header.gas_limit + } + + fn is_default(&self) -> bool { + self.execution_payload_header == ExecutionPayloadHeaderEip4844::default() + } } -#[derive(Debug, Clone, TestRandom, Serialize, Deserialize, Derivative)] -#[derivative(PartialEq, Hash(bound = "T: EthSpec"))] -#[serde(bound = "T: EthSpec")] -pub struct BlindedPayload { - pub execution_payload_header: ExecutionPayloadHeader, +impl AbstractExecPayload for BlindedPayload { + type Ref<'a> = BlindedPayloadRef<'a, T>; + type Merge = BlindedPayloadMerge; + type Capella = BlindedPayloadCapella; + type Eip4844 = BlindedPayloadEip4844; +} + +impl Default for FullPayloadMerge { + fn default() -> Self { + Self { + execution_payload: ExecutionPayloadMerge::default(), + } + } +} +impl Default for FullPayloadCapella { + fn default() -> Self { + Self { + execution_payload: ExecutionPayloadCapella::default(), + } + } +} +impl Default for FullPayloadEip4844 { + fn default() -> Self { + Self { + execution_payload: ExecutionPayloadEip4844::default(), + } + } } // NOTE: the `Default` implementation for `BlindedPayload` needs to be different from the `Default` @@ -141,6 +897,7 @@ pub struct BlindedPayload { // The default `BlindedPayload` is therefore the payload header that results from blinding the // default `ExecutionPayload`, which differs from the default `ExecutionPayloadHeader` in that // its `transactions_root` is the hash of the empty list rather than 0x0. +/* impl Default for BlindedPayload { fn default() -> Self { Self { @@ -148,9 +905,86 @@ impl Default for BlindedPayload { } } } +*/ + +impl Default for BlindedPayloadMerge { + fn default() -> Self { + Self { + execution_payload_header: ExecutionPayloadHeaderMerge::from( + ExecutionPayloadMerge::default(), + ), + } + } +} + +impl Default for BlindedPayloadCapella { + fn default() -> Self { + Self { + execution_payload_header: ExecutionPayloadHeaderCapella::from( + ExecutionPayloadCapella::default(), + ), + } + } +} + +impl Default for BlindedPayloadEip4844 { + fn default() -> Self { + Self { + execution_payload_header: ExecutionPayloadHeaderEip4844::from( + ExecutionPayloadEip4844::default(), + ), + } + } +} + +impl From> for BlindedPayload { + fn from(payload: ExecutionPayload) -> Self { + match payload { + ExecutionPayload::Merge(payload) => BlindedPayload::Merge(payload.into()), + ExecutionPayload::Capella(payload) => BlindedPayload::Capella(payload.into()), + ExecutionPayload::Eip4844(payload) => BlindedPayload::Eip4844(payload.into()), + } + } +} impl From> for BlindedPayload { fn from(execution_payload_header: ExecutionPayloadHeader) -> Self { + match execution_payload_header { + ExecutionPayloadHeader::Merge(execution_payload_header) => { + Self::Merge(BlindedPayloadMerge { + execution_payload_header, + }) + } + ExecutionPayloadHeader::Capella(execution_payload_header) => { + Self::Capella(BlindedPayloadCapella { + execution_payload_header, + }) + } + ExecutionPayloadHeader::Eip4844(execution_payload_header) => { + Self::Eip4844(BlindedPayloadEip4844 { + execution_payload_header, + }) + } + } + } +} + +impl From> for BlindedPayloadMerge { + fn from(execution_payload_header: ExecutionPayloadHeaderMerge) -> Self { + Self { + execution_payload_header, + } + } +} +impl From> for BlindedPayloadCapella { + fn from(execution_payload_header: ExecutionPayloadHeaderCapella) -> Self { + Self { + execution_payload_header, + } + } +} +impl From> for BlindedPayloadEip4844 { + fn from(execution_payload_header: ExecutionPayloadHeaderEip4844) -> Self { Self { execution_payload_header, } @@ -159,36 +993,79 @@ impl From> for BlindedPayload { impl From> for ExecutionPayloadHeader { fn from(blinded: BlindedPayload) -> Self { - blinded.execution_payload_header - } -} - -impl From> for BlindedPayload { - fn from(execution_payload: ExecutionPayload) -> Self { - Self { - execution_payload_header: ExecutionPayloadHeader::from(&execution_payload), + match blinded { + BlindedPayload::Merge(blinded_payload) => { + ExecutionPayloadHeader::Merge(blinded_payload.execution_payload_header) + } + BlindedPayload::Capella(blinded_payload) => { + ExecutionPayloadHeader::Capella(blinded_payload.execution_payload_header) + } + BlindedPayload::Eip4844(blinded_payload) => { + ExecutionPayloadHeader::Eip4844(blinded_payload.execution_payload_header) + } } } } -impl TreeHash for BlindedPayload { - fn tree_hash_type() -> tree_hash::TreeHashType { - >::tree_hash_type() +// FIXME(sproul): consider adding references to these From impls +impl From> for BlindedPayloadMerge { + fn from(execution_payload: ExecutionPayloadMerge) -> Self { + Self { + execution_payload_header: ExecutionPayloadHeaderMerge::from(execution_payload), + } } - - fn tree_hash_packed_encoding(&self) -> PackedEncoding { - self.execution_payload_header.tree_hash_packed_encoding() +} +impl From> for BlindedPayloadCapella { + fn from(execution_payload: ExecutionPayloadCapella) -> Self { + Self { + execution_payload_header: ExecutionPayloadHeaderCapella::from(execution_payload), + } } - - fn tree_hash_packing_factor() -> usize { - >::tree_hash_packing_factor() - } - - fn tree_hash_root(&self) -> tree_hash::Hash256 { - self.execution_payload_header.tree_hash_root() +} +impl From> for BlindedPayloadEip4844 { + fn from(execution_payload: ExecutionPayloadEip4844) -> Self { + Self { + execution_payload_header: ExecutionPayloadHeaderEip4844::from(execution_payload), + } } } +impl TryFrom> for BlindedPayloadMerge { + type Error = (); + fn try_from(header: ExecutionPayloadHeader) -> Result { + match header { + ExecutionPayloadHeader::Merge(execution_payload_header) => { + Ok(execution_payload_header.into()) + } + _ => Err(()), + } + } +} +impl TryFrom> for BlindedPayloadCapella { + type Error = (); + fn try_from(header: ExecutionPayloadHeader) -> Result { + match header { + ExecutionPayloadHeader::Capella(execution_payload_header) => { + Ok(execution_payload_header.into()) + } + _ => Err(()), + } + } +} + +impl TryFrom> for BlindedPayloadEip4844 { + type Error = (); + fn try_from(header: ExecutionPayloadHeader) -> Result { + match header { + ExecutionPayloadHeader::Eip4844(execution_payload_header) => { + Ok(execution_payload_header.into()) + } + _ => Err(()), + } + } +} + +/* impl Decode for BlindedPayload { fn is_ssz_fixed_len() -> bool { as Decode>::is_ssz_fixed_len() @@ -204,7 +1081,9 @@ impl Decode for BlindedPayload { }) } } + */ +/* impl Encode for BlindedPayload { fn is_ssz_fixed_len() -> bool { as Encode>::is_ssz_fixed_len() @@ -218,28 +1097,9 @@ impl Encode for BlindedPayload { self.execution_payload_header.ssz_bytes_len() } } +*/ -#[derive(Default, Debug, Clone, Serialize, Deserialize, TestRandom, Derivative)] -#[derivative(PartialEq, Hash(bound = "T: EthSpec"))] -#[serde(bound = "T: EthSpec")] -pub struct FullPayload { - pub execution_payload: ExecutionPayload, -} - -impl From> for FullPayload { - fn from(execution_payload: ExecutionPayload) -> Self { - Self { execution_payload } - } -} - -impl TryFrom> for FullPayload { - type Error = (); - - fn try_from(_: ExecutionPayloadHeader) -> Result { - Err(()) - } -} - +/* impl TreeHash for FullPayload { fn tree_hash_type() -> tree_hash::TreeHashType { >::tree_hash_type() @@ -257,7 +1117,9 @@ impl TreeHash for FullPayload { self.execution_payload.tree_hash_root() } } +*/ +/* impl Decode for FullPayload { fn is_ssz_fixed_len() -> bool { as Decode>::is_ssz_fixed_len() @@ -283,3 +1145,4 @@ impl Encode for FullPayload { self.execution_payload.ssz_bytes_len() } } +*/ diff --git a/consensus/types/src/signed_beacon_block.rs b/consensus/types/src/signed_beacon_block.rs index 4b937912ba..70cc4c1125 100644 --- a/consensus/types/src/signed_beacon_block.rs +++ b/consensus/types/src/signed_beacon_block.rs @@ -38,7 +38,7 @@ impl From for Hash256 { /// A `BeaconBlock` and a signature from its proposer. #[superstruct( - variants(Base, Altair, Merge, Eip4844), + variants(Base, Altair, Merge, Capella, Eip4844), variant_attributes( derive( Debug, @@ -65,13 +65,15 @@ impl From for Hash256 { #[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))] #[tree_hash(enum_behaviour = "transparent")] #[ssz(enum_behaviour = "transparent")] -pub struct SignedBeaconBlock = FullPayload> { +pub struct SignedBeaconBlock = FullPayload> { #[superstruct(only(Base), partial_getter(rename = "message_base"))] pub message: BeaconBlockBase, #[superstruct(only(Altair), partial_getter(rename = "message_altair"))] pub message: BeaconBlockAltair, #[superstruct(only(Merge), partial_getter(rename = "message_merge"))] pub message: BeaconBlockMerge, + #[superstruct(only(Capella), partial_getter(rename = "message_capella"))] + pub message: BeaconBlockCapella, #[superstruct(only(Eip4844), partial_getter(rename = "message_eip4844"))] pub message: BeaconBlockEip4844, pub signature: Signature, @@ -79,7 +81,7 @@ pub struct SignedBeaconBlock = FullPayload = SignedBeaconBlock>; -impl> SignedBeaconBlock { +impl> SignedBeaconBlock { /// Returns the name of the fork pertaining to `self`. /// /// Will return an `Err` if `self` has been instantiated to a variant conflicting with the fork @@ -131,6 +133,9 @@ impl> SignedBeaconBlock { BeaconBlock::Merge(message) => { SignedBeaconBlock::Merge(SignedBeaconBlockMerge { message, signature }) } + BeaconBlock::Capella(message) => { + SignedBeaconBlock::Capella(SignedBeaconBlockCapella { message, signature }) + } BeaconBlock::Eip4844(message) => { SignedBeaconBlock::Eip4844(SignedBeaconBlockEip4844 { message, signature }) } @@ -263,7 +268,7 @@ impl From>> impl SignedBeaconBlockMerge> { pub fn into_full_block( self, - execution_payload: ExecutionPayload, + execution_payload: ExecutionPayloadMerge, ) -> SignedBeaconBlockMerge> { let SignedBeaconBlockMerge { message: @@ -283,7 +288,7 @@ impl SignedBeaconBlockMerge> { deposits, voluntary_exits, sync_aggregate, - execution_payload: BlindedPayload { .. }, + execution_payload: BlindedPayloadMerge { .. }, }, }, signature, @@ -304,7 +309,59 @@ impl SignedBeaconBlockMerge> { deposits, voluntary_exits, sync_aggregate, - execution_payload: FullPayload { execution_payload }, + execution_payload: FullPayloadMerge { execution_payload }, + }, + }, + signature, + } + } +} + +impl SignedBeaconBlockCapella> { + pub fn into_full_block( + self, + execution_payload: ExecutionPayloadCapella, + ) -> SignedBeaconBlockCapella> { + let SignedBeaconBlockCapella { + message: + BeaconBlockCapella { + slot, + proposer_index, + parent_root, + state_root, + body: + BeaconBlockBodyCapella { + randao_reveal, + eth1_data, + graffiti, + proposer_slashings, + attester_slashings, + attestations, + deposits, + voluntary_exits, + sync_aggregate, + execution_payload: BlindedPayloadCapella { .. }, + }, + }, + signature, + } = self; + SignedBeaconBlockCapella { + message: BeaconBlockCapella { + slot, + proposer_index, + parent_root, + state_root, + body: BeaconBlockBodyCapella { + randao_reveal, + eth1_data, + graffiti, + proposer_slashings, + attester_slashings, + attestations, + deposits, + voluntary_exits, + sync_aggregate, + execution_payload: FullPayloadCapella { execution_payload }, }, }, signature, @@ -315,7 +372,7 @@ impl SignedBeaconBlockMerge> { impl SignedBeaconBlockEip4844> { pub fn into_full_block( self, - execution_payload: ExecutionPayload, + execution_payload: ExecutionPayloadEip4844, ) -> SignedBeaconBlockEip4844> { let SignedBeaconBlockEip4844 { message: @@ -335,7 +392,7 @@ impl SignedBeaconBlockEip4844> { deposits, voluntary_exits, sync_aggregate, - execution_payload: BlindedPayload { .. }, + execution_payload: BlindedPayloadEip4844 { .. }, blob_kzg_commitments, }, }, @@ -357,7 +414,7 @@ impl SignedBeaconBlockEip4844> { deposits, voluntary_exits, sync_aggregate, - execution_payload: FullPayload { execution_payload }, + execution_payload: FullPayloadEip4844 { execution_payload }, blob_kzg_commitments, }, }, @@ -371,15 +428,23 @@ impl SignedBeaconBlock> { self, execution_payload: Option>, ) -> Option>> { - let full_block = match self { - SignedBeaconBlock::Base(block) => SignedBeaconBlock::Base(block.into()), - SignedBeaconBlock::Altair(block) => SignedBeaconBlock::Altair(block.into()), - SignedBeaconBlock::Merge(block) => { - SignedBeaconBlock::Merge(block.into_full_block(execution_payload?)) + let full_block = match (self, execution_payload) { + (SignedBeaconBlock::Base(block), _) => SignedBeaconBlock::Base(block.into()), + (SignedBeaconBlock::Altair(block), _) => SignedBeaconBlock::Altair(block.into()), + (SignedBeaconBlock::Merge(block), Some(ExecutionPayload::Merge(payload))) => { + SignedBeaconBlock::Merge(block.into_full_block(payload)) } - SignedBeaconBlock::Eip4844(block) => { - SignedBeaconBlock::Eip4844(block.into_full_block(execution_payload?)) + (SignedBeaconBlock::Capella(block), Some(ExecutionPayload::Capella(payload))) => { + SignedBeaconBlock::Capella(block.into_full_block(payload)) } + (SignedBeaconBlock::Eip4844(block), Some(ExecutionPayload::Eip4844(payload))) => { + SignedBeaconBlock::Eip4844(block.into_full_block(payload)) + } + // avoid wildcard matching forks so that compiler will + // direct us here when a new fork has been added + (SignedBeaconBlock::Merge(_), _) => return None, + (SignedBeaconBlock::Capella(_), _) => return None, + (SignedBeaconBlock::Eip4844(_), _) => return None, }; Some(full_block) } diff --git a/consensus/types/src/withdrawal.rs b/consensus/types/src/withdrawal.rs new file mode 100644 index 0000000000..7368847913 --- /dev/null +++ b/consensus/types/src/withdrawal.rs @@ -0,0 +1,27 @@ +use crate::test_utils::TestRandom; +use crate::*; +use serde_derive::{Deserialize, Serialize}; +use ssz_derive::{Decode, Encode}; +use test_random_derive::TestRandom; +use tree_hash_derive::TreeHash; + +/// A deposit to potentially become a beacon chain validator. +/// +/// Spec v0.12.1 +#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))] +#[derive( + Debug, PartialEq, Hash, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom, +)] +pub struct Withdrawal { + #[serde(with = "eth2_serde_utils::quoted_u64")] + pub index: u64, + pub address: Address, + pub amount: u64, +} + +#[cfg(test)] +mod tests { + use super::*; + + ssz_and_tree_hash_tests!(Withdrawal); +}