From 221c433d62dc963bd26d9bcd4f47c6921b54626b Mon Sep 17 00:00:00 2001 From: ethDreamer <37123614+ethDreamer@users.noreply.github.com> Date: Fri, 14 Oct 2022 17:35:10 -0500 Subject: [PATCH] Fixed a ton of state_processing stuff (#3642) FIXME's: * consensus/fork_choice/src/fork_choice.rs * consensus/state_processing/src/per_epoch_processing/capella.rs * consensus/types/src/execution_payload_header.rs TODO's: * consensus/state_processing/src/per_epoch_processing/capella/partial_withdrawals.rs * consensus/state_processing/src/per_epoch_processing/capella/full_withdrawals.rs --- beacon_node/store/src/hot_cold_store.rs | 4 +- .../store/src/impls/execution_payload.rs | 39 ++++++++- beacon_node/store/src/partial_beacon_state.rs | 80 +++++++++++++++--- consensus/fork_choice/src/fork_choice.rs | 12 ++- .../fork_choice/src/fork_choice_store.rs | 4 +- .../src/common/slash_validator.rs | 11 +-- consensus/state_processing/src/genesis.rs | 44 +++++++++- .../src/per_block_processing.rs | 39 ++++++--- .../process_operations.rs | 1 + .../per_block_processing/signature_sets.rs | 2 +- .../src/per_epoch_processing.rs | 6 +- .../src/per_epoch_processing/capella.rs | 81 +++++++++++++++++++ .../capella/full_withdrawals.rs | 10 +++ .../capella/partial_withdrawals.rs | 10 +++ consensus/state_processing/src/upgrade.rs | 4 + .../state_processing/src/upgrade/capella.rs | 74 +++++++++++++++++ .../state_processing/src/upgrade/eip4844.rs | 73 +++++++++++++++++ .../state_processing/src/upgrade/merge.rs | 4 +- consensus/types/src/beacon_state.rs | 17 ++++ .../types/src/execution_payload_header.rs | 67 +++++++++++++++ consensus/types/src/payload.rs | 11 --- 21 files changed, 538 insertions(+), 55 deletions(-) create mode 100644 consensus/state_processing/src/per_epoch_processing/capella.rs create mode 100644 consensus/state_processing/src/per_epoch_processing/capella/full_withdrawals.rs create mode 100644 consensus/state_processing/src/per_epoch_processing/capella/partial_withdrawals.rs create mode 100644 consensus/state_processing/src/upgrade/capella.rs create mode 100644 consensus/state_processing/src/upgrade/eip4844.rs diff --git a/beacon_node/store/src/hot_cold_store.rs b/beacon_node/store/src/hot_cold_store.rs index d44b57258a..cfba40c0bf 100644 --- a/beacon_node/store/src/hot_cold_store.rs +++ b/beacon_node/store/src/hot_cold_store.rs @@ -428,7 +428,7 @@ impl, Cold: ItemStore> HotColdDB } /// Fetch a block from the store, ignoring which fork variant it *should* be for. - pub fn get_block_any_variant>( + pub fn get_block_any_variant>( &self, block_root: &Hash256, ) -> Result>, Error> { @@ -439,7 +439,7 @@ impl, Cold: ItemStore> HotColdDB /// /// This is useful for e.g. ignoring the slot-indicated fork to forcefully load a block as if it /// were for a different fork. - pub fn get_block_with>( + pub fn get_block_with>( &self, block_root: &Hash256, decoder: impl FnOnce(&[u8]) -> Result, ssz::DecodeError>, diff --git a/beacon_node/store/src/impls/execution_payload.rs b/beacon_node/store/src/impls/execution_payload.rs index ddb9a44628..ad68d1fba0 100644 --- a/beacon_node/store/src/impls/execution_payload.rs +++ b/beacon_node/store/src/impls/execution_payload.rs @@ -1,7 +1,35 @@ use crate::{DBColumn, Error, StoreItem}; use ssz::{Decode, Encode}; -use types::{EthSpec, ExecutionPayload}; +use types::{ + EthSpec, ExecutionPayload, ExecutionPayloadCapella, ExecutionPayloadEip4844, + ExecutionPayloadMerge, +}; +macro_rules! impl_store_item { + ($ty_name:ident) => { + impl StoreItem for $ty_name { + fn db_column() -> DBColumn { + DBColumn::ExecPayload + } + + fn as_store_bytes(&self) -> Vec { + self.as_ssz_bytes() + } + + fn from_store_bytes(bytes: &[u8]) -> Result { + Ok(Self::from_ssz_bytes(bytes)?) + } + } + }; +} +impl_store_item!(ExecutionPayloadMerge); +impl_store_item!(ExecutionPayloadCapella); +impl_store_item!(ExecutionPayloadEip4844); + +/// This fork-agnostic implementation should be only used for writing. +/// +/// It is very inefficient at reading, and decoding the desired fork-specific variant is recommended +/// instead. impl StoreItem for ExecutionPayload { fn db_column() -> DBColumn { DBColumn::ExecPayload @@ -12,6 +40,13 @@ impl StoreItem for ExecutionPayload { } fn from_store_bytes(bytes: &[u8]) -> Result { - Ok(Self::from_ssz_bytes(bytes)?) + ExecutionPayloadEip4844::from_ssz_bytes(bytes) + .map(Self::Eip4844) + .or_else(|_| { + ExecutionPayloadCapella::from_ssz_bytes(bytes) + .map(Self::Capella) + .or_else(|_| ExecutionPayloadMerge::from_ssz_bytes(bytes).map(Self::Merge)) + }) + .map_err(Into::into) } } diff --git a/beacon_node/store/src/partial_beacon_state.rs b/beacon_node/store/src/partial_beacon_state.rs index 46bc0274f4..74e63c58ea 100644 --- a/beacon_node/store/src/partial_beacon_state.rs +++ b/beacon_node/store/src/partial_beacon_state.rs @@ -14,7 +14,7 @@ use types::*; /// /// Utilises lazy-loading from separate storage for its vector fields. #[superstruct( - variants(Base, Altair, Merge, Eip4844), + variants(Base, Altair, Merge, Capella, Eip4844), variant_attributes(derive(Debug, PartialEq, Clone, Encode, Decode)) )] #[derive(Debug, PartialEq, Clone, Encode)] @@ -66,9 +66,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 @@ -78,18 +78,39 @@ where pub finalized_checkpoint: Checkpoint, // Inactivity - #[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, } /// Implement the conversion function from BeaconState -> PartialBeaconState. @@ -178,6 +199,23 @@ impl PartialBeaconState { latest_execution_payload_header ] ), + BeaconState::Capella(s) => impl_from_state_forgetful!( + s, + outer, + Capella, + PartialBeaconStateCapella, + [ + previous_epoch_participation, + current_epoch_participation, + current_sync_committee, + next_sync_committee, + inactivity_scores, + latest_execution_payload_header, + withdrawal_queue, + next_withdrawal_index, + next_partial_withdrawal_validator_index + ] + ), BeaconState::Eip4844(s) => impl_from_state_forgetful!( s, outer, @@ -189,7 +227,10 @@ impl PartialBeaconState { current_sync_committee, next_sync_committee, inactivity_scores, - latest_execution_payload_header + latest_execution_payload_header, + withdrawal_queue, + next_withdrawal_index, + next_partial_withdrawal_validator_index ] ), } @@ -379,6 +420,22 @@ impl TryInto> for PartialBeaconState { latest_execution_payload_header ] ), + PartialBeaconState::Capella(inner) => impl_try_into_beacon_state!( + inner, + Capella, + BeaconStateCapella, + [ + previous_epoch_participation, + current_epoch_participation, + current_sync_committee, + next_sync_committee, + inactivity_scores, + latest_execution_payload_header, + withdrawal_queue, + next_withdrawal_index, + next_partial_withdrawal_validator_index + ] + ), PartialBeaconState::Eip4844(inner) => impl_try_into_beacon_state!( inner, Eip4844, @@ -389,7 +446,10 @@ impl TryInto> for PartialBeaconState { current_sync_committee, next_sync_committee, inactivity_scores, - latest_execution_payload_header + latest_execution_payload_header, + withdrawal_queue, + next_withdrawal_index, + next_partial_withdrawal_validator_index ] ), }; diff --git a/consensus/fork_choice/src/fork_choice.rs b/consensus/fork_choice/src/fork_choice.rs index 7b3111ecda..5f4c9931f0 100644 --- a/consensus/fork_choice/src/fork_choice.rs +++ b/consensus/fork_choice/src/fork_choice.rs @@ -12,9 +12,10 @@ use std::collections::BTreeSet; use std::marker::PhantomData; use std::time::Duration; use types::{ - consts::merge::INTERVALS_PER_SLOT, AttestationShufflingId, AttesterSlashing, BeaconBlockRef, - BeaconState, BeaconStateError, ChainSpec, Checkpoint, Epoch, EthSpec, ExecPayload, - ExecutionBlockHash, Hash256, IndexedAttestation, RelativeEpoch, SignedBeaconBlock, Slot, + consts::merge::INTERVALS_PER_SLOT, AbstractExecPayload, AttestationShufflingId, + AttesterSlashing, BeaconBlockRef, BeaconState, BeaconStateError, ChainSpec, Checkpoint, Epoch, + EthSpec, ExecPayload, ExecutionBlockHash, Hash256, IndexedAttestation, RelativeEpoch, + SignedBeaconBlock, Slot, }; #[derive(Debug)] @@ -665,7 +666,7 @@ where /// The supplied block **must** pass the `state_transition` function as it will not be run /// here. #[allow(clippy::too_many_arguments)] - pub fn on_block>( + pub fn on_block>( &mut self, system_time_current_slot: Slot, block: BeaconBlockRef, @@ -777,7 +778,10 @@ where (parent_justified, parent_finalized) } else { let justification_and_finalization_state = match block { + // FIXME: verify this is correct for Capella/Eip4844 because + // epoch processing changes in Capella.. BeaconBlockRef::Eip4844(_) + | BeaconBlockRef::Capella(_) | BeaconBlockRef::Merge(_) | BeaconBlockRef::Altair(_) => { let participation_cache = diff --git a/consensus/fork_choice/src/fork_choice_store.rs b/consensus/fork_choice/src/fork_choice_store.rs index 9604e25475..a9a32173b6 100644 --- a/consensus/fork_choice/src/fork_choice_store.rs +++ b/consensus/fork_choice/src/fork_choice_store.rs @@ -1,6 +1,6 @@ use std::collections::BTreeSet; use std::fmt::Debug; -use types::{BeaconBlockRef, BeaconState, Checkpoint, EthSpec, ExecPayload, Hash256, Slot}; +use types::{AbstractExecPayload, BeaconBlockRef, BeaconState, Checkpoint, EthSpec, Hash256, Slot}; /// Approximates the `Store` in "Ethereum 2.0 Phase 0 -- Beacon Chain Fork Choice": /// @@ -33,7 +33,7 @@ pub trait ForkChoiceStore: Sized { /// Called whenever `ForkChoice::on_block` has verified a block, but not yet added it to fork /// choice. Allows the implementer to performing caching or other housekeeping duties. - fn on_verified_block>( + fn on_verified_block>( &mut self, block: BeaconBlockRef, block_root: Hash256, diff --git a/consensus/state_processing/src/common/slash_validator.rs b/consensus/state_processing/src/common/slash_validator.rs index 02006d0c23..f085a41b9e 100644 --- a/consensus/state_processing/src/common/slash_validator.rs +++ b/consensus/state_processing/src/common/slash_validator.rs @@ -45,11 +45,12 @@ pub fn slash_validator( validator_effective_balance.safe_div(spec.whistleblower_reward_quotient)?; let proposer_reward = match state { BeaconState::Base(_) => whistleblower_reward.safe_div(spec.proposer_reward_quotient)?, - BeaconState::Altair(_) | BeaconState::Merge(_) | BeaconState::Eip4844(_) => { - whistleblower_reward - .safe_mul(PROPOSER_WEIGHT)? - .safe_div(WEIGHT_DENOMINATOR)? - } + BeaconState::Altair(_) + | BeaconState::Merge(_) + | BeaconState::Capella(_) + | BeaconState::Eip4844(_) => whistleblower_reward + .safe_mul(PROPOSER_WEIGHT)? + .safe_div(WEIGHT_DENOMINATOR)?, }; // Ensure the whistleblower index is in the validator registry. diff --git a/consensus/state_processing/src/genesis.rs b/consensus/state_processing/src/genesis.rs index 32160a4815..3f9328f4d5 100644 --- a/consensus/state_processing/src/genesis.rs +++ b/consensus/state_processing/src/genesis.rs @@ -2,7 +2,9 @@ use super::per_block_processing::{ errors::BlockProcessingError, process_operations::process_deposit, }; use crate::common::DepositDataTree; -use crate::upgrade::{upgrade_to_altair, upgrade_to_bellatrix}; +use crate::upgrade::{ + upgrade_to_altair, upgrade_to_bellatrix, upgrade_to_capella, upgrade_to_eip4844, +}; use safe_arith::{ArithError, SafeArith}; use tree_hash::TreeHash; use types::DEPOSIT_TREE_DEPTH; @@ -61,6 +63,7 @@ pub fn initialize_beacon_state_from_eth1( .bellatrix_fork_epoch .map_or(false, |fork_epoch| fork_epoch == T::genesis_epoch()) { + // this will set state.latest_execution_payload_header = ExecutionPayloadHeaderMerge::default() upgrade_to_bellatrix(&mut state, spec)?; // Remove intermediate Altair fork from `state.fork`. @@ -68,8 +71,43 @@ pub fn initialize_beacon_state_from_eth1( // Override latest execution payload header. // See https://github.com/ethereum/consensus-specs/blob/v1.1.0/specs/bellatrix/beacon-chain.md#testing - *state.latest_execution_payload_header_mut()? = - execution_payload_header.unwrap_or_default(); + if let Some(ExecutionPayloadHeader::Merge(ref header)) = execution_payload_header { + *state.latest_execution_payload_header_merge_mut()? = header.clone(); + } + } + + // Upgrade to capella if configured from genesis + if spec + .capella_fork_epoch + .map_or(false, |fork_epoch| fork_epoch == T::genesis_epoch()) + { + upgrade_to_capella(&mut state, spec)?; + + // Remove intermediate Bellatrix fork from `state.fork`. + state.fork_mut().previous_version = spec.capella_fork_version; + + // Override latest execution payload header. + // See https://github.com/ethereum/consensus-specs/blob/dev/specs/capella/beacon-chain.md#testing + if let Some(ExecutionPayloadHeader::Capella(ref header)) = execution_payload_header { + *state.latest_execution_payload_header_capella_mut()? = header.clone(); + } + } + + // Upgrade to eip4844 if configured from genesis + if spec + .eip4844_fork_epoch + .map_or(false, |fork_epoch| fork_epoch == T::genesis_epoch()) + { + upgrade_to_eip4844(&mut state, spec)?; + + // Remove intermediate Capella fork from `state.fork`. + state.fork_mut().previous_version = spec.eip4844_fork_version; + + // Override latest execution payload header. + // See https://github.com/ethereum/consensus-specs/blob/dev/specs/eip4844/beacon-chain.md#testing + if let Some(ExecutionPayloadHeader::Eip4844(header)) = execution_payload_header { + *state.latest_execution_payload_header_eip4844_mut()? = header; + } } // Now that we have our validators, initialize the caches (including the committees) diff --git a/consensus/state_processing/src/per_block_processing.rs b/consensus/state_processing/src/per_block_processing.rs index 417e963ed2..71ceb71c14 100644 --- a/consensus/state_processing/src/per_block_processing.rs +++ b/consensus/state_processing/src/per_block_processing.rs @@ -154,7 +154,7 @@ pub fn per_block_processing>( // previous block. if is_execution_enabled(state, block.body()) { let payload = block.body().execution_payload()?; - process_execution_payload(state, payload, spec)?; + process_execution_payload::(state, payload, spec)?; } process_randao(state, block, verify_randao, spec)?; @@ -319,16 +319,16 @@ pub fn get_new_eth1_data( /// Contains a partial set of checks from the `process_execution_payload` function: /// /// https://github.com/ethereum/consensus-specs/blob/v1.1.5/specs/merge/beacon-chain.md#process_execution_payload -pub fn partially_verify_execution_payload>( +pub fn partially_verify_execution_payload<'payload, T: EthSpec, Payload: AbstractExecPayload>( state: &BeaconState, - payload: &Payload, + payload: Payload::Ref<'payload>, spec: &ChainSpec, ) -> Result<(), BlockProcessingError> { if is_merge_transition_complete(state) { block_verify!( - payload.parent_hash() == state.latest_execution_payload_header()?.block_hash, + payload.parent_hash() == *state.latest_execution_payload_header()?.block_hash(), BlockProcessingError::ExecutionHashChainIncontiguous { - expected: state.latest_execution_payload_header()?.block_hash, + expected: *state.latest_execution_payload_header()?.block_hash(), found: payload.parent_hash(), } ); @@ -360,14 +360,33 @@ pub fn partially_verify_execution_payload>( /// Partially equivalent to the `process_execution_payload` function: /// /// https://github.com/ethereum/consensus-specs/blob/v1.1.5/specs/merge/beacon-chain.md#process_execution_payload -pub fn process_execution_payload>( +pub fn process_execution_payload<'payload, T: EthSpec, Payload: AbstractExecPayload>( state: &mut BeaconState, - payload: &Payload, + payload: Payload::Ref<'payload>, spec: &ChainSpec, ) -> Result<(), BlockProcessingError> { - partially_verify_execution_payload(state, payload, spec)?; + partially_verify_execution_payload::(state, payload, spec)?; - *state.latest_execution_payload_header_mut()? = payload.to_execution_payload_header(); + match state.latest_execution_payload_header_mut()? { + ExecutionPayloadHeaderRefMut::Merge(header_mut) => { + match payload.to_execution_payload_header() { + ExecutionPayloadHeader::Merge(header) => *header_mut = header, + _ => return Err(BlockProcessingError::IncorrectStateType), + } + } + ExecutionPayloadHeaderRefMut::Capella(header_mut) => { + match payload.to_execution_payload_header() { + ExecutionPayloadHeader::Capella(header) => *header_mut = header, + _ => return Err(BlockProcessingError::IncorrectStateType), + } + } + ExecutionPayloadHeaderRefMut::Eip4844(header_mut) => { + match payload.to_execution_payload_header() { + ExecutionPayloadHeader::Eip4844(header) => *header_mut = header, + _ => return Err(BlockProcessingError::IncorrectStateType), + } + } + } Ok(()) } @@ -380,7 +399,7 @@ pub fn process_execution_payload>( pub fn is_merge_transition_complete(state: &BeaconState) -> bool { state .latest_execution_payload_header() - .map(|header| *header != >::default()) + .map(|header| !header.is_default()) .unwrap_or(false) } /// https://github.com/ethereum/consensus-specs/blob/dev/specs/bellatrix/beacon-chain.md#is_merge_transition_block diff --git a/consensus/state_processing/src/per_block_processing/process_operations.rs b/consensus/state_processing/src/per_block_processing/process_operations.rs index b1f857b576..33e1a19c51 100644 --- a/consensus/state_processing/src/per_block_processing/process_operations.rs +++ b/consensus/state_processing/src/per_block_processing/process_operations.rs @@ -232,6 +232,7 @@ pub fn process_attestations<'a, T: EthSpec, Payload: AbstractExecPayload>( } BeaconBlockBodyRef::Altair(_) | BeaconBlockBodyRef::Merge(_) + | BeaconBlockBodyRef::Capella(_) | BeaconBlockBodyRef::Eip4844(_) => { altair::process_attestations( state, diff --git a/consensus/state_processing/src/per_block_processing/signature_sets.rs b/consensus/state_processing/src/per_block_processing/signature_sets.rs index 82a33acd73..8b60c145c8 100644 --- a/consensus/state_processing/src/per_block_processing/signature_sets.rs +++ b/consensus/state_processing/src/per_block_processing/signature_sets.rs @@ -8,7 +8,7 @@ use std::borrow::Cow; use tree_hash::TreeHash; use types::{ AbstractExecPayload, AggregateSignature, AttesterSlashing, BeaconBlockRef, BeaconState, - BeaconStateError, ChainSpec, DepositData, Domain, Epoch, EthSpec, ExecPayload, Fork, Hash256, + BeaconStateError, ChainSpec, DepositData, Domain, Epoch, EthSpec, Fork, Hash256, InconsistentFork, IndexedAttestation, ProposerSlashing, PublicKey, PublicKeyBytes, Signature, SignedAggregateAndProof, SignedBeaconBlock, SignedBeaconBlockHeader, SignedContributionAndProof, SignedRoot, SignedVoluntaryExit, SigningData, Slot, SyncAggregate, diff --git a/consensus/state_processing/src/per_epoch_processing.rs b/consensus/state_processing/src/per_epoch_processing.rs index 5d357dc966..565fae9db9 100644 --- a/consensus/state_processing/src/per_epoch_processing.rs +++ b/consensus/state_processing/src/per_epoch_processing.rs @@ -11,6 +11,7 @@ pub use weigh_justification_and_finalization::weigh_justification_and_finalizati pub mod altair; pub mod base; +pub mod capella; pub mod effective_balance_updates; pub mod epoch_processing_summary; pub mod errors; @@ -37,9 +38,8 @@ pub fn process_epoch( match state { BeaconState::Base(_) => base::process_epoch(state, spec), - BeaconState::Altair(_) | BeaconState::Merge(_) | BeaconState::Eip4844(_) => { - altair::process_epoch(state, spec) - } + BeaconState::Altair(_) | BeaconState::Merge(_) => altair::process_epoch(state, spec), + BeaconState::Capella(_) | BeaconState::Eip4844(_) => capella::process_epoch(state, spec), } } diff --git a/consensus/state_processing/src/per_epoch_processing/capella.rs b/consensus/state_processing/src/per_epoch_processing/capella.rs new file mode 100644 index 0000000000..4886b28053 --- /dev/null +++ b/consensus/state_processing/src/per_epoch_processing/capella.rs @@ -0,0 +1,81 @@ +use super::{process_registry_updates, process_slashings, EpochProcessingSummary, Error}; +use crate::per_epoch_processing::{ + altair, + effective_balance_updates::process_effective_balance_updates, + historical_roots_update::process_historical_roots_update, + resets::{process_eth1_data_reset, process_randao_mixes_reset, process_slashings_reset}, +}; +pub use full_withdrawals::process_full_withdrawals; +pub use partial_withdrawals::process_partial_withdrawals; +use types::{BeaconState, ChainSpec, EthSpec, RelativeEpoch}; + +pub mod full_withdrawals; +pub mod partial_withdrawals; + +pub fn process_epoch( + state: &mut BeaconState, + spec: &ChainSpec, +) -> Result, Error> { + // Ensure the committee caches are built. + state.build_committee_cache(RelativeEpoch::Previous, spec)?; + state.build_committee_cache(RelativeEpoch::Current, spec)?; + state.build_committee_cache(RelativeEpoch::Next, spec)?; + + // Pre-compute participating indices and total balances. + let participation_cache = altair::ParticipationCache::new(state, spec)?; + let sync_committee = state.current_sync_committee()?.clone(); + + // Justification and finalization. + let justification_and_finalization_state = + altair::process_justification_and_finalization(state, &participation_cache)?; + justification_and_finalization_state.apply_changes_to_state(state); + + altair::process_inactivity_updates(state, &participation_cache, spec)?; + + // Rewards and Penalties. + altair::process_rewards_and_penalties(state, &participation_cache, spec)?; + + // Registry Updates. + process_registry_updates(state, spec)?; + + // Slashings. + process_slashings( + state, + participation_cache.current_epoch_total_active_balance(), + spec, + )?; + + // Reset eth1 data votes. + process_eth1_data_reset(state)?; + + // Update effective balances with hysteresis (lag). + process_effective_balance_updates(state, spec)?; + + // Reset slashings + process_slashings_reset(state)?; + + // Set randao mix + process_randao_mixes_reset(state)?; + + // Set historical root accumulator + process_historical_roots_update(state)?; + + // Rotate current/previous epoch participation + altair::process_participation_flag_updates(state)?; + + altair::process_sync_committee_updates(state, spec)?; + + // Withdrawals + process_full_withdrawals(state)?; + + process_partial_withdrawals(state)?; + + // Rotate the epoch caches to suit the epoch transition. + state.advance_caches(spec)?; + + // FIXME: do we need a Capella variant for this? + Ok(EpochProcessingSummary::Altair { + participation_cache, + sync_committee, + }) +} diff --git a/consensus/state_processing/src/per_epoch_processing/capella/full_withdrawals.rs b/consensus/state_processing/src/per_epoch_processing/capella/full_withdrawals.rs new file mode 100644 index 0000000000..d7747c1fe9 --- /dev/null +++ b/consensus/state_processing/src/per_epoch_processing/capella/full_withdrawals.rs @@ -0,0 +1,10 @@ +use crate::EpochProcessingError; +use types::beacon_state::BeaconState; +use types::eth_spec::EthSpec; + +pub fn process_full_withdrawals( + state: &mut BeaconState, +) -> Result<(), EpochProcessingError> { + todo!("implement this"); + Ok(()) +} diff --git a/consensus/state_processing/src/per_epoch_processing/capella/partial_withdrawals.rs b/consensus/state_processing/src/per_epoch_processing/capella/partial_withdrawals.rs new file mode 100644 index 0000000000..2a57670081 --- /dev/null +++ b/consensus/state_processing/src/per_epoch_processing/capella/partial_withdrawals.rs @@ -0,0 +1,10 @@ +use crate::EpochProcessingError; +use types::beacon_state::BeaconState; +use types::eth_spec::EthSpec; + +pub fn process_partial_withdrawals( + state: &mut BeaconState, +) -> Result<(), EpochProcessingError> { + todo!("implement this"); + Ok(()) +} diff --git a/consensus/state_processing/src/upgrade.rs b/consensus/state_processing/src/upgrade.rs index fdf13c8281..01b6571056 100644 --- a/consensus/state_processing/src/upgrade.rs +++ b/consensus/state_processing/src/upgrade.rs @@ -1,5 +1,9 @@ pub mod altair; +pub mod capella; +pub mod eip4844; pub mod merge; pub use altair::upgrade_to_altair; +pub use capella::upgrade_to_capella; +pub use eip4844::upgrade_to_eip4844; pub use merge::upgrade_to_bellatrix; diff --git a/consensus/state_processing/src/upgrade/capella.rs b/consensus/state_processing/src/upgrade/capella.rs new file mode 100644 index 0000000000..b2abd3be20 --- /dev/null +++ b/consensus/state_processing/src/upgrade/capella.rs @@ -0,0 +1,74 @@ +use ssz_types::VariableList; +use std::mem; +use types::{BeaconState, BeaconStateCapella, BeaconStateError as Error, ChainSpec, EthSpec, Fork}; + +/// Transform a `Merge` state into an `Capella` state. +pub fn upgrade_to_capella( + pre_state: &mut BeaconState, + spec: &ChainSpec, +) -> Result<(), Error> { + let epoch = pre_state.current_epoch(); + let pre = pre_state.as_merge_mut()?; + + // Where possible, use something like `mem::take` to move fields from behind the &mut + // reference. For other fields that don't have a good default value, use `clone`. + // + // Fixed size vectors get cloned because replacing them would require the same size + // allocation as cloning. + let post = BeaconState::Capella(BeaconStateCapella { + // Versioning + genesis_time: pre.genesis_time, + genesis_validators_root: pre.genesis_validators_root, + slot: pre.slot, + fork: Fork { + previous_version: pre.fork.current_version, + current_version: spec.capella_fork_version, + epoch, + }, + // History + latest_block_header: pre.latest_block_header.clone(), + block_roots: pre.block_roots.clone(), + state_roots: pre.state_roots.clone(), + historical_roots: mem::take(&mut pre.historical_roots), + // Eth1 + eth1_data: pre.eth1_data.clone(), + eth1_data_votes: mem::take(&mut pre.eth1_data_votes), + eth1_deposit_index: pre.eth1_deposit_index, + // Registry + validators: mem::take(&mut pre.validators), + balances: mem::take(&mut pre.balances), + // Randomness + randao_mixes: pre.randao_mixes.clone(), + // Slashings + slashings: pre.slashings.clone(), + // `Participation + previous_epoch_participation: mem::take(&mut pre.previous_epoch_participation), + current_epoch_participation: mem::take(&mut pre.current_epoch_participation), + // Finality + justification_bits: pre.justification_bits.clone(), + previous_justified_checkpoint: pre.previous_justified_checkpoint, + current_justified_checkpoint: pre.current_justified_checkpoint, + finalized_checkpoint: pre.finalized_checkpoint, + // Inactivity + inactivity_scores: mem::take(&mut pre.inactivity_scores), + // Sync committees + current_sync_committee: pre.current_sync_committee.clone(), + next_sync_committee: pre.next_sync_committee.clone(), + // Execution + latest_execution_payload_header: pre.latest_execution_payload_header.upgrade_to_capella(), + // Withdrawals + withdrawal_queue: VariableList::empty(), + next_withdrawal_index: 0, + next_partial_withdrawal_validator_index: 0, + // Caches + total_active_balance: pre.total_active_balance, + committee_caches: mem::take(&mut pre.committee_caches), + pubkey_cache: mem::take(&mut pre.pubkey_cache), + exit_cache: mem::take(&mut pre.exit_cache), + tree_hash_cache: mem::take(&mut pre.tree_hash_cache), + }); + + *pre_state = post; + + Ok(()) +} diff --git a/consensus/state_processing/src/upgrade/eip4844.rs b/consensus/state_processing/src/upgrade/eip4844.rs new file mode 100644 index 0000000000..666d5b0c68 --- /dev/null +++ b/consensus/state_processing/src/upgrade/eip4844.rs @@ -0,0 +1,73 @@ +use std::mem; +use types::{BeaconState, BeaconStateEip4844, BeaconStateError as Error, ChainSpec, EthSpec, Fork}; + +/// Transform a `Capella` state into an `Eip4844` state. +pub fn upgrade_to_eip4844( + pre_state: &mut BeaconState, + spec: &ChainSpec, +) -> Result<(), Error> { + let epoch = pre_state.current_epoch(); + let pre = pre_state.as_capella_mut()?; + + // Where possible, use something like `mem::take` to move fields from behind the &mut + // reference. For other fields that don't have a good default value, use `clone`. + // + // Fixed size vectors get cloned because replacing them would require the same size + // allocation as cloning. + let post = BeaconState::Eip4844(BeaconStateEip4844 { + // Versioning + genesis_time: pre.genesis_time, + genesis_validators_root: pre.genesis_validators_root, + slot: pre.slot, + fork: Fork { + previous_version: pre.fork.current_version, + current_version: spec.eip4844_fork_version, + epoch, + }, + // History + latest_block_header: pre.latest_block_header.clone(), + block_roots: pre.block_roots.clone(), + state_roots: pre.state_roots.clone(), + historical_roots: mem::take(&mut pre.historical_roots), + // Eth1 + eth1_data: pre.eth1_data.clone(), + eth1_data_votes: mem::take(&mut pre.eth1_data_votes), + eth1_deposit_index: pre.eth1_deposit_index, + // Registry + validators: mem::take(&mut pre.validators), + balances: mem::take(&mut pre.balances), + // Randomness + randao_mixes: pre.randao_mixes.clone(), + // Slashings + slashings: pre.slashings.clone(), + // `Participation + previous_epoch_participation: mem::take(&mut pre.previous_epoch_participation), + current_epoch_participation: mem::take(&mut pre.current_epoch_participation), + // Finality + justification_bits: pre.justification_bits.clone(), + previous_justified_checkpoint: pre.previous_justified_checkpoint, + current_justified_checkpoint: pre.current_justified_checkpoint, + finalized_checkpoint: pre.finalized_checkpoint, + // Inactivity + inactivity_scores: mem::take(&mut pre.inactivity_scores), + // Sync committees + current_sync_committee: pre.current_sync_committee.clone(), + next_sync_committee: pre.next_sync_committee.clone(), + // Execution + latest_execution_payload_header: pre.latest_execution_payload_header.upgrade_to_eip4844(), + // Withdrawals + withdrawal_queue: mem::take(&mut pre.withdrawal_queue), + next_withdrawal_index: pre.next_withdrawal_index, + next_partial_withdrawal_validator_index: pre.next_partial_withdrawal_validator_index, + // Caches + total_active_balance: pre.total_active_balance, + committee_caches: mem::take(&mut pre.committee_caches), + pubkey_cache: mem::take(&mut pre.pubkey_cache), + exit_cache: mem::take(&mut pre.exit_cache), + tree_hash_cache: mem::take(&mut pre.tree_hash_cache), + }); + + *pre_state = post; + + Ok(()) +} diff --git a/consensus/state_processing/src/upgrade/merge.rs b/consensus/state_processing/src/upgrade/merge.rs index 2e4ed441a4..c172466248 100644 --- a/consensus/state_processing/src/upgrade/merge.rs +++ b/consensus/state_processing/src/upgrade/merge.rs @@ -1,7 +1,7 @@ use std::mem; use types::{ BeaconState, BeaconStateError as Error, BeaconStateMerge, ChainSpec, EthSpec, - ExecutionPayloadHeader, Fork, + ExecutionPayloadHeaderMerge, Fork, }; /// Transform a `Altair` state into an `Merge` state. @@ -57,7 +57,7 @@ pub fn upgrade_to_bellatrix( current_sync_committee: pre.current_sync_committee.clone(), next_sync_committee: pre.next_sync_committee.clone(), // Execution - latest_execution_payload_header: >::default(), + latest_execution_payload_header: >::default(), // Caches total_active_balance: pre.total_active_balance, committee_caches: mem::take(&mut pre.committee_caches), diff --git a/consensus/types/src/beacon_state.rs b/consensus/types/src/beacon_state.rs index 6438a0a7e1..10596a769e 100644 --- a/consensus/types/src/beacon_state.rs +++ b/consensus/types/src/beacon_state.rs @@ -718,6 +718,23 @@ impl BeaconState { } } + pub fn latest_execution_payload_header_mut( + &mut self, + ) -> Result, Error> { + match self { + BeaconState::Base(_) | BeaconState::Altair(_) => Err(Error::IncorrectStateVariant), + BeaconState::Merge(state) => Ok(ExecutionPayloadHeaderRefMut::Merge( + &mut state.latest_execution_payload_header, + )), + BeaconState::Capella(state) => Ok(ExecutionPayloadHeaderRefMut::Capella( + &mut state.latest_execution_payload_header, + )), + BeaconState::Eip4844(state) => Ok(ExecutionPayloadHeaderRefMut::Eip4844( + &mut state.latest_execution_payload_header, + )), + } + } + /// Return `true` if the validator who produced `slot_signature` is eligible to aggregate. /// /// Spec v0.12.1 diff --git a/consensus/types/src/execution_payload_header.rs b/consensus/types/src/execution_payload_header.rs index f92ab956e1..7f90f1f57b 100644 --- a/consensus/types/src/execution_payload_header.rs +++ b/consensus/types/src/execution_payload_header.rs @@ -65,6 +65,73 @@ pub struct ExecutionPayloadHeader { pub withdrawals_root: Hash256, } +impl<'a, T: EthSpec> ExecutionPayloadHeaderRef<'a, T> { + // FIXME: maybe this could be a derived trait.. + pub fn is_default(self) -> bool { + match self { + ExecutionPayloadHeaderRef::Merge(header) => { + *header == ExecutionPayloadHeaderMerge::default() + } + ExecutionPayloadHeaderRef::Capella(header) => { + *header == ExecutionPayloadHeaderCapella::default() + } + ExecutionPayloadHeaderRef::Eip4844(header) => { + *header == ExecutionPayloadHeaderEip4844::default() + } + } + } +} + +impl ExecutionPayloadHeaderMerge { + pub fn upgrade_to_capella(&self) -> ExecutionPayloadHeaderCapella { + // TODO: if this is correct we should calculate and hardcode this.. + let empty_withdrawals_root = + VariableList::::empty().tree_hash_root(); + ExecutionPayloadHeaderCapella { + parent_hash: self.parent_hash, + fee_recipient: self.fee_recipient, + state_root: self.state_root, + receipts_root: self.receipts_root, + logs_bloom: self.logs_bloom.clone(), + prev_randao: self.prev_randao, + block_number: self.block_number, + gas_limit: self.gas_limit, + gas_used: self.gas_used, + timestamp: self.timestamp, + extra_data: self.extra_data.clone(), + base_fee_per_gas: self.base_fee_per_gas, + block_hash: self.block_hash, + transactions_root: self.transactions_root, + // FIXME: the spec doesn't seem to define what to do here.. + withdrawals_root: empty_withdrawals_root, + } + } +} + +impl ExecutionPayloadHeaderCapella { + pub fn upgrade_to_eip4844(&self) -> ExecutionPayloadHeaderEip4844 { + ExecutionPayloadHeaderEip4844 { + parent_hash: self.parent_hash, + fee_recipient: self.fee_recipient, + state_root: self.state_root, + receipts_root: self.receipts_root, + logs_bloom: self.logs_bloom.clone(), + prev_randao: self.prev_randao, + block_number: self.block_number, + gas_limit: self.gas_limit, + gas_used: self.gas_used, + timestamp: self.timestamp, + extra_data: self.extra_data.clone(), + base_fee_per_gas: self.base_fee_per_gas, + // TODO: verify if this is correct + excess_blobs: 0, + block_hash: self.block_hash, + transactions_root: self.transactions_root, + withdrawals_root: self.withdrawals_root, + } + } +} + impl From> for ExecutionPayloadHeaderMerge { fn from(payload: ExecutionPayloadMerge) -> Self { Self { diff --git a/consensus/types/src/payload.rs b/consensus/types/src/payload.rs index d3a8fd698a..db7a646334 100644 --- a/consensus/types/src/payload.rs +++ b/consensus/types/src/payload.rs @@ -621,17 +621,6 @@ impl ExecPayload for BlindedPayload { // 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(),