From 4b4a708abca5a1afca2dee95d182a0eadf075c87 Mon Sep 17 00:00:00 2001 From: Tumas Date: Mon, 14 Oct 2024 11:54:24 +0300 Subject: [PATCH] Upgrade consensus-spec-tests to v1.5.0-alpha.8 --- consensus-spec-tests | 2 +- genesis/src/lib.rs | 11 +- grandine-snapshot-tests | 2 +- helper_functions/src/accessors.rs | 14 - helper_functions/src/fork.rs | 32 ++- helper_functions/src/mutators.rs | 51 ++-- .../src/altair/block_processing.rs | 3 + .../src/electra/block_processing.rs | 257 ++++++++++++------ .../src/electra/epoch_processing.rs | 189 +++++++++---- transition_functions/src/lib.rs | 1 - .../src/phase0/block_processing.rs | 3 + .../src/unphased/block_processing.rs | 59 ++-- types/src/collections.rs | 5 +- types/src/electra/beacon_state.rs | 4 +- types/src/electra/consts.rs | 2 +- types/src/electra/containers.rs | 9 +- types/src/electra/spec_tests.rs | 6 +- types/src/preset.rs | 21 +- types/src/traits.rs | 10 +- 19 files changed, 413 insertions(+), 268 deletions(-) diff --git a/consensus-spec-tests b/consensus-spec-tests index 4083d107..09c1e41b 160000 --- a/consensus-spec-tests +++ b/consensus-spec-tests @@ -1 +1 @@ -Subproject commit 4083d107723c2f832715ccebc08582218217d25d +Subproject commit 09c1e41b722216efa9b1c6390169b984f0870052 diff --git a/genesis/src/lib.rs b/genesis/src/lib.rs index 22a8ebba..6ebb84fc 100644 --- a/genesis/src/lib.rs +++ b/genesis/src/lib.rs @@ -167,14 +167,19 @@ impl<'config, P: Preset> Incremental<'config, P> { combined::process_deposit_data(self.config, &mut self.beacon_state, data)? { if let Some(state) = self.beacon_state.post_electra_mut() { - let pending_deposits = state.pending_balance_deposits().clone(); + let pending_deposits = state.pending_deposits().clone(); for deposit in &pending_deposits { - let balance = state.balances_mut().get_mut(deposit.index)?; + let validator_index = accessors::index_of_public_key(state, deposit.pubkey) + .expect( + "public keys in state.pending_deposits are taken from state.validators", + ); + + let balance = state.balances_mut().get_mut(validator_index)?; increase_balance(balance, deposit.amount); } - *state.pending_balance_deposits_mut() = PersistentList::default(); + *state.pending_deposits_mut() = PersistentList::default(); } let balance = *self.beacon_state.balances().get(validator_index)?; diff --git a/grandine-snapshot-tests b/grandine-snapshot-tests index 0a07b50b..dc758f0b 160000 --- a/grandine-snapshot-tests +++ b/grandine-snapshot-tests @@ -1 +1 @@ -Subproject commit 0a07b50b77d7b63ade4225482157ff481663f08d +Subproject commit dc758f0b3bb7b7d57aa14dd3b8b94a7d22a3fc13 diff --git a/helper_functions/src/accessors.rs b/helper_functions/src/accessors.rs index 7a10bedd..98720751 100644 --- a/helper_functions/src/accessors.rs +++ b/helper_functions/src/accessors.rs @@ -822,20 +822,6 @@ pub fn get_consolidation_churn_limit( get_balance_churn_limit(config, state) - get_activation_exit_churn_limit(config, state) } -pub fn get_active_balance( - state: &impl BeaconState

, - validator_index: ValidatorIndex, -) -> Result { - let max_effective_balance = - misc::get_max_effective_balance::

(state.validators().get(validator_index)?); - - core::cmp::min( - state.balances().get(validator_index).copied()?, - max_effective_balance, - ) - .pipe(Ok) -} - #[must_use] pub fn get_pending_balance_to_withdraw( state: &impl PostElectraBeaconState

, diff --git a/helper_functions/src/fork.rs b/helper_functions/src/fork.rs index d4d1b492..53e56fef 100644 --- a/helper_functions/src/fork.rs +++ b/helper_functions/src/fork.rs @@ -2,6 +2,7 @@ use core::ops::BitOrAssign as _; use std::sync::Arc; use anyhow::Result; +use bls::SignatureBytes; use itertools::Itertools as _; use ssz::PersistentList; use std_ext::ArcExt as _; @@ -21,15 +22,17 @@ use types::{ containers::ExecutionPayloadHeader as DenebExecutionPayloadHeader, }, electra::{ - beacon_state::BeaconState as ElectraBeaconState, consts::UNSET_DEPOSIT_REQUESTS_START_INDEX, + beacon_state::BeaconState as ElectraBeaconState, + consts::UNSET_DEPOSIT_REQUESTS_START_INDEX, containers::PendingDeposit, }, phase0::{ beacon_state::BeaconState as Phase0BeaconState, - consts::FAR_FUTURE_EPOCH, + consts::{FAR_FUTURE_EPOCH, GENESIS_SLOT}, containers::{Fork, PendingAttestation}, primitives::H256, }, preset::Preset, + traits::{BeaconState as _, PostElectraBeaconState as _}, }; use crate::{accessors, misc, mutators, phase0, predicates}; @@ -639,7 +642,7 @@ pub fn upgrade_to_electra( earliest_exit_epoch, consolidation_balance_to_consume: 0, earliest_consolidation_epoch: misc::compute_activation_exit_epoch::

(epoch), - pending_balance_deposits: PersistentList::default(), + pending_deposits: PersistentList::default(), pending_partial_withdrawals: PersistentList::default(), pending_consolidations: PersistentList::default(), // Cache @@ -661,7 +664,26 @@ pub fn upgrade_to_electra( .map(|(_, index)| index); for index in pre_activation { - mutators::queue_entire_balance_and_reset_validator(&mut post, index)?; + let balance = mutators::balance(&mut post, index)?; + let validator_balance = *balance; + + *balance = 0; + + let validator = post.validators_mut().get_mut(index)?; + + validator.effective_balance = 0; + validator.activation_eligibility_epoch = FAR_FUTURE_EPOCH; + + let withdrawal_credentials = validator.withdrawal_credentials; + let pubkey = validator.pubkey.to_bytes(); + + post.pending_deposits_mut().push(PendingDeposit { + pubkey, + withdrawal_credentials, + amount: validator_balance, + signature: SignatureBytes::empty(), + slot: GENESIS_SLOT, + })?; } for index in post @@ -682,7 +704,7 @@ pub fn upgrade_to_electra( mod spec_tests { use spec_test_utils::Case; use test_generator::test_resources; - use types::preset::{Mainnet, Minimal}; + use types::preset::{Mainnet, Minimal, Preset}; use super::*; diff --git a/helper_functions/src/mutators.rs b/helper_functions/src/mutators.rs index 9513f18f..37eaab7b 100644 --- a/helper_functions/src/mutators.rs +++ b/helper_functions/src/mutators.rs @@ -1,11 +1,12 @@ use core::cmp::Ordering; use anyhow::Result; +use bls::SignatureBytes; use types::{ config::Config, - electra::{consts::COMPOUNDING_WITHDRAWAL_PREFIX, containers::PendingBalanceDeposit}, + electra::{consts::COMPOUNDING_WITHDRAWAL_PREFIX, containers::PendingDeposit}, phase0::{ - consts::FAR_FUTURE_EPOCH, + consts::{FAR_FUTURE_EPOCH, GENESIS_SLOT}, primitives::{Epoch, Gwei, ValidatorIndex}, }, preset::Preset, @@ -19,7 +20,6 @@ use crate::{ }, error::Error, misc::compute_activation_exit_epoch, - predicates::has_eth1_withdrawal_credential, }; pub fn balance( @@ -105,12 +105,10 @@ pub fn switch_to_compounding_validator( ) -> Result<()> { let validator = state.validators_mut().get_mut(index)?; - if has_eth1_withdrawal_credential(validator) { - validator.withdrawal_credentials[..COMPOUNDING_WITHDRAWAL_PREFIX.len()] - .copy_from_slice(COMPOUNDING_WITHDRAWAL_PREFIX); + validator.withdrawal_credentials[..COMPOUNDING_WITHDRAWAL_PREFIX.len()] + .copy_from_slice(COMPOUNDING_WITHDRAWAL_PREFIX); - queue_excess_active_balance(state, index)?; - } + queue_excess_active_balance(state, index)?; Ok(()) } @@ -126,36 +124,19 @@ pub fn queue_excess_active_balance( *state.balances_mut().get_mut(index)? = P::MIN_ACTIVATION_BALANCE; - state - .pending_balance_deposits_mut() - .push(PendingBalanceDeposit { - index, - amount: excess_balance, - })?; - } - - Ok(()) -} - -pub fn queue_entire_balance_and_reset_validator( - state: &mut impl PostElectraBeaconState

, - index: ValidatorIndex, -) -> Result<()> { - let validator_balance = *balance(state, index)?; - - *balance(state, index)? = 0; + let validator = state.validators().get(index)?; - let validator = state.validators_mut().get_mut(index)?; + let pubkey = validator.pubkey.to_bytes(); + let withdrawal_credentials = validator.withdrawal_credentials; - validator.effective_balance = 0; - validator.activation_eligibility_epoch = FAR_FUTURE_EPOCH; - - state - .pending_balance_deposits_mut() - .push(PendingBalanceDeposit { - index, - amount: validator_balance, + state.pending_deposits_mut().push(PendingDeposit { + pubkey, + withdrawal_credentials, + amount: excess_balance, + signature: SignatureBytes::empty(), + slot: GENESIS_SLOT, })?; + } Ok(()) } diff --git a/transition_functions/src/altair/block_processing.rs b/transition_functions/src/altair/block_processing.rs index 1910e66e..c4d0b644 100644 --- a/transition_functions/src/altair/block_processing.rs +++ b/transition_functions/src/altair/block_processing.rs @@ -351,6 +351,7 @@ pub fn process_deposit_data( validator_index, withdrawal_credentials: vec![withdrawal_credentials], amounts: smallvec![amount], + signatures: vec![signature], }; apply_deposits(state, 1, core::iter::once(combined_deposit), NullSlotReport)?; @@ -372,6 +373,7 @@ pub fn process_deposit_data( pubkey, withdrawal_credentials, amounts: smallvec![amount], + signatures: vec![signature], }; apply_deposits(state, 1, core::iter::once(combined_deposit), NullSlotReport)?; @@ -400,6 +402,7 @@ pub fn apply_deposits( pubkey, withdrawal_credentials, amounts, + .. } => { let public_key_bytes = pubkey.to_bytes(); let first_amount = amounts[0]; diff --git a/transition_functions/src/electra/block_processing.rs b/transition_functions/src/electra/block_processing.rs index 217aa32a..c5b709b9 100644 --- a/transition_functions/src/electra/block_processing.rs +++ b/transition_functions/src/electra/block_processing.rs @@ -1,7 +1,9 @@ use core::ops::{Add as _, Index as _, Rem as _}; use anyhow::{ensure, Result}; +use arithmetic::U64Ext as _; use bit_field::BitField as _; +use bls::CachedPublicKey; use execution_engine::{ExecutionEngine, NullExecutionEngine}; use helper_functions::{ accessors::{ @@ -26,7 +28,7 @@ use helper_functions::{ predicates::{ has_compounding_withdrawal_credential, has_eth1_withdrawal_credential, has_execution_withdrawal_credential, is_active_validator, - is_compounding_withdrawal_credential, validate_constructed_indexed_attestation, + validate_constructed_indexed_attestation, }, signing::{SignForAllForks, SignForSingleFork as _}, slot_report::{NullSlotReport, SlotReport}, @@ -50,18 +52,18 @@ use types::{ consts::{FULL_EXIT_REQUEST_AMOUNT, UNSET_DEPOSIT_REQUESTS_START_INDEX}, containers::{ Attestation, BeaconBlock, BeaconBlockBody, ConsolidationRequest, DepositRequest, - PendingBalanceDeposit, PendingConsolidation, PendingPartialWithdrawal, - SignedBeaconBlock, WithdrawalRequest, + PendingConsolidation, PendingDeposit, PendingPartialWithdrawal, SignedBeaconBlock, + WithdrawalRequest, }, }, nonstandard::{smallvec, AttestationEpoch, SlashingKind}, phase0::{ - consts::FAR_FUTURE_EPOCH, + consts::{FAR_FUTURE_EPOCH, GENESIS_SLOT}, containers::{ - AttestationData, Deposit, DepositData, DepositMessage, ProposerSlashing, - SignedVoluntaryExit, Validator, + AttestationData, DepositData, DepositMessage, ProposerSlashing, SignedVoluntaryExit, + Validator, }, - primitives::{DepositIndex, ExecutionAddress, ValidatorIndex, H256}, + primitives::{DepositIndex, ExecutionAddress, Gwei, ValidatorIndex, H256}, }, preset::Preset, traits::{ @@ -173,7 +175,7 @@ pub fn custom_process_block( // > [New in Electra:EIP6110] for deposit_request in &block.body.execution_requests.deposits { - process_deposit_request(config, state, *deposit_request, &mut slot_report)?; + process_deposit_request(state, *deposit_request)?; } // > [New in Electra:EIP7002:EIP7251] @@ -311,6 +313,7 @@ pub fn get_expected_withdrawals( let mut withdrawal_index = state.next_withdrawal_index(); let mut validator_index = state.next_withdrawal_validator_index(); let mut withdrawals = vec![]; + let mut partial_withdrawals_count = 0; // > [New in Electra:EIP7251] Consume pending partial withdrawals for withdrawal in &state.pending_partial_withdrawals().clone() { @@ -347,9 +350,9 @@ pub fn get_expected_withdrawals( withdrawal_index += 1; } - } - let partial_withdrawals_count = withdrawals.len(); + partial_withdrawals_count += 1; + } // > Sweep for remaining for _ in 0..bound { @@ -776,6 +779,7 @@ pub fn process_deposit_data( validator_index, withdrawal_credentials: vec![withdrawal_credentials], amounts: smallvec![amount], + signatures: vec![signature], }; apply_deposits(state, core::iter::once(combined_deposit), NullSlotReport)?; @@ -797,6 +801,7 @@ pub fn process_deposit_data( pubkey, withdrawal_credentials, amounts: smallvec![amount], + signatures: vec![signature], }; apply_deposits(state, core::iter::once(combined_deposit), NullSlotReport)?; @@ -807,6 +812,51 @@ pub fn process_deposit_data( Ok(None) } +pub fn add_validator_to_registry( + state: &mut impl PostElectraBeaconState

, + pubkey: CachedPublicKey, + withdrawal_credentials: H256, + amount: Gwei, +) -> Result<()> { + let public_key_bytes = pubkey.to_bytes(); + let validator_index = state.validators().len_u64(); + + let mut validator = Validator { + pubkey, + withdrawal_credentials, + effective_balance: 0, + slashed: false, + activation_eligibility_epoch: FAR_FUTURE_EPOCH, + activation_epoch: FAR_FUTURE_EPOCH, + exit_epoch: FAR_FUTURE_EPOCH, + withdrawable_epoch: FAR_FUTURE_EPOCH, + }; + + let max_effective_balance = get_max_effective_balance::

(&validator); + + validator.effective_balance = amount + .prev_multiple_of(P::EFFECTIVE_BALANCE_INCREMENT) + .min(max_effective_balance); + + state.validators_mut().push(validator)?; + state.balances_mut().push(amount)?; + state.previous_epoch_participation_mut().push(0)?; + state.current_epoch_participation_mut().push(0)?; + state.inactivity_scores_mut().push(0)?; + + state + .cache_mut() + .validator_indices + .get_mut() + .expect( + "state.cache.validator_indices is initialized by \ + index_of_public_key, which is called before apply_deposits", + ) + .insert(public_key_bytes, validator_index); + + Ok(()) +} + fn apply_deposits( state: &mut impl PostElectraBeaconState

, combined_deposits: impl IntoIterator, @@ -819,45 +869,21 @@ fn apply_deposits( pubkey, withdrawal_credentials, amounts, + signatures, } => { let public_key_bytes = pubkey.to_bytes(); - - let validator = Validator { - pubkey, - withdrawal_credentials, - effective_balance: 0, - slashed: false, - activation_eligibility_epoch: FAR_FUTURE_EPOCH, - activation_epoch: FAR_FUTURE_EPOCH, - exit_epoch: FAR_FUTURE_EPOCH, - withdrawable_epoch: FAR_FUTURE_EPOCH, - }; - let validator_index = state.validators().len_u64(); - state.validators_mut().push(validator)?; - state.balances_mut().push(0)?; - state.previous_epoch_participation_mut().push(0)?; - state.current_epoch_participation_mut().push(0)?; - state.inactivity_scores_mut().push(0)?; + add_validator_to_registry(state, pubkey, withdrawal_credentials, 0)?; - state - .cache_mut() - .validator_indices - .get_mut() - .expect( - "state.cache.validator_indices is initialized by \ - index_of_public_key, which is called before apply_deposits", - ) - .insert(public_key_bytes, validator_index); - - for amount in amounts { - state - .pending_balance_deposits_mut() - .push(PendingBalanceDeposit { - index: validator_index, - amount, - })?; + for (amount, signature) in amounts.into_iter().zip(signatures) { + state.pending_deposits_mut().push(PendingDeposit { + pubkey: public_key_bytes, + withdrawal_credentials, + amount, + signature, + slot: GENESIS_SLOT, + })?; // TODO(feature/electra): slot_report.add_deposit(validator_index, amount); @@ -868,26 +894,24 @@ fn apply_deposits( validator_index, withdrawal_credentials, amounts, + signatures, } => { - for amount in amounts { - state - .pending_balance_deposits_mut() - .push(PendingBalanceDeposit { - index: validator_index, - amount, - })?; - - slot_report.add_deposit(validator_index, amount); - } + let pubkey = accessors::public_key(state, validator_index)?.to_bytes(); - let validator = state.validators().get(validator_index)?; - - if has_eth1_withdrawal_credential(validator) - && withdrawal_credentials - .into_iter() - .any(is_compounding_withdrawal_credential) + for ((amount, signature), withdrawal_credentials) in amounts + .into_iter() + .zip(signatures) + .zip(withdrawal_credentials) { - switch_to_compounding_validator(state, validator_index)?; + state.pending_deposits_mut().push(PendingDeposit { + pubkey, + withdrawal_credentials, + amount, + signature, + slot: GENESIS_SLOT, + })?; + + slot_report.add_deposit(validator_index, amount); } } } @@ -1029,10 +1053,8 @@ fn process_withdrawal_request( } fn process_deposit_request( - config: &Config, state: &mut impl PostElectraBeaconState

, deposit_request: DepositRequest, - mut slot_report: impl SlotReport, ) -> Result<()> { let DepositRequest { pubkey, @@ -1042,28 +1064,20 @@ fn process_deposit_request( index, } = deposit_request; + let slot = state.slot(); + // > Set deposit request start index if state.deposit_requests_start_index() == UNSET_DEPOSIT_REQUESTS_START_INDEX { *state.deposit_requests_start_index_mut() = index; } - let deposit = Deposit { - data: DepositData { - pubkey, - withdrawal_credentials, - amount, - signature, - }, - ..Deposit::default() - }; - - let combined_deposits = unphased::validate_deposits_without_verifying_merkle_branch( - config, - state, - core::iter::once(deposit), - )?; - - apply_deposits(state, combined_deposits, &mut slot_report)?; + state.pending_deposits_mut().push(PendingDeposit { + pubkey, + withdrawal_credentials, + amount, + signature, + slot, + })?; Ok(()) } @@ -1080,6 +1094,19 @@ pub fn process_consolidation_request( target_pubkey, } = consolidation_request; + if is_valid_switch_to_compounding_request(state, consolidation_request)? { + let Some(source_index) = index_of_public_key(state, source_pubkey) else { + return Ok(()); + }; + + return switch_to_compounding_validator(state, source_index); + } + + // > Verify that source != target, so a consolidation cannot be used as an exit. + if source_pubkey == target_pubkey { + return Ok(()); + } + // > If the pending consolidations queue is full, consolidation requests are ignored if state.pending_consolidations().len_usize() == P::PendingConsolidationsLimit::USIZE { return Ok(()); @@ -1101,16 +1128,9 @@ pub fn process_consolidation_request( let source_validator = state.validators().get(source_index)?; let target_validator = state.validators().get(target_index)?; - // > Verify that source != target, so a consolidation cannot be used as an exit. - if source_index == target_index { - return Ok(()); - } - // > Verify source withdrawal credentials let has_correct_credential = has_execution_withdrawal_credential(source_validator); - let prefix_len = H256::len_bytes() - ExecutionAddress::len_bytes(); - let computed_source_address = - ExecutionAddress::from_slice(&source_validator.withdrawal_credentials[prefix_len..]); + let computed_source_address = compute_source_address(source_validator); if !(has_correct_credential && computed_source_address == source_address) { return Ok(()); @@ -1157,9 +1177,68 @@ pub fn process_consolidation_request( target_index, })?; + // > Churn any target excess active balance of target and raise its max + let target_validator = state.validators().get(target_index)?; + + if has_eth1_withdrawal_credential(target_validator) { + return switch_to_compounding_validator(state, target_index); + } + Ok(()) } +fn is_valid_switch_to_compounding_request( + state: &impl PostElectraBeaconState

, + consolidation_request: ConsolidationRequest, +) -> Result { + let ConsolidationRequest { + source_address, + source_pubkey, + target_pubkey, + } = consolidation_request; + + // > Switch to compounding requires source and target be equal + if source_pubkey != target_pubkey { + return Ok(false); + } + + // > Verify pubkey exists + let Some(source_index) = index_of_public_key(state, source_pubkey) else { + return Ok(false); + }; + + let source_validator = state.validators().get(source_index)?; + + // > Verify request has been authorized + if compute_source_address(source_validator) != source_address { + return Ok(false); + } + + // > Verify source withdrawal credentials + if !has_eth1_withdrawal_credential(source_validator) { + return Ok(false); + } + + // > Verify the source is active + let current_epoch = get_current_epoch(state); + + if !is_active_validator(source_validator, current_epoch) { + return Ok(false); + } + + // > Verify exit for source has not been initiated + if source_validator.exit_epoch != FAR_FUTURE_EPOCH { + return Ok(false); + } + + Ok(true) +} + +fn compute_source_address(validator: &Validator) -> ExecutionAddress { + let prefix_len = H256::len_bytes() - ExecutionAddress::len_bytes(); + ExecutionAddress::from_slice(&validator.withdrawal_credentials[prefix_len..]) +} + #[cfg(test)] mod spec_tests { use core::fmt::Debug; @@ -1376,7 +1455,7 @@ mod spec_tests { processing_tests! { process_deposit_request, - |config, state, deposit_request, _| process_deposit_request(config, state, deposit_request, NullSlotReport), + |_, state, deposit_request, _| process_deposit_request(state, deposit_request), "deposit_request", "consensus-spec-tests/tests/mainnet/electra/operations/deposit_request/*/*", "consensus-spec-tests/tests/minimal/electra/operations/deposit_request/*/*", diff --git a/transition_functions/src/electra/epoch_processing.rs b/transition_functions/src/electra/epoch_processing.rs index c227844f..3b3f57a2 100644 --- a/transition_functions/src/electra/epoch_processing.rs +++ b/transition_functions/src/electra/epoch_processing.rs @@ -4,15 +4,17 @@ use anyhow::Result; use arithmetic::{NonZeroExt as _, U64Ext as _}; use helper_functions::{ accessors::{ - get_activation_exit_churn_limit, get_active_balance, get_current_epoch, get_next_epoch, + self, get_activation_exit_churn_limit, get_current_epoch, get_next_epoch, total_active_balance, }, electra::{initiate_validator_exit, is_eligible_for_activation_queue}, - misc::{compute_activation_exit_epoch, vec_of_default}, - mutators::{balance, decrease_balance, increase_balance, switch_to_compounding_validator}, - predicates::{ - has_compounding_withdrawal_credential, is_active_validator, is_eligible_for_activation, + misc::{ + compute_activation_exit_epoch, compute_start_slot_at_epoch, get_max_effective_balance, + vec_of_default, }, + mutators::{balance, decrease_balance, increase_balance}, + predicates::{is_active_validator, is_eligible_for_activation}, + signing::SignForAllForks as _, }; use prometheus_metrics::METRICS; use ssz::{PersistentList, SszHash as _}; @@ -21,13 +23,17 @@ use typenum::Unsigned as _; use types::{ capella::containers::HistoricalSummary, config::Config, - electra::beacon_state::BeaconState as ElectraBeaconState, - phase0::{consts::FAR_FUTURE_EPOCH, primitives::Gwei}, + electra::{beacon_state::BeaconState as ElectraBeaconState, containers::PendingDeposit}, + phase0::{ + consts::{FAR_FUTURE_EPOCH, GENESIS_SLOT}, + containers::DepositMessage, + primitives::Gwei, + }, preset::Preset, traits::{BeaconState, PostElectraBeaconState}, }; -use super::epoch_intermediates; +use super::{block_processing, epoch_intermediates}; use crate::{ altair::{ self, EpochDeltasForTransition, EpochReport, ValidatorSummary as AltairValidatorSummary, @@ -71,7 +77,7 @@ pub fn process_epoch(config: &Config, state: &mut ElectraBeaconState(state, summaries); unphased::process_eth1_data_reset(state); - process_pending_balance_deposits(config, state)?; + process_pending_deposits(config, state)?; process_pending_consolidations(state)?; process_effective_balance_updates(state); unphased::process_slashings_reset(state); @@ -212,7 +218,7 @@ fn process_registry_updates( Ok(()) } -fn process_pending_balance_deposits( +fn process_pending_deposits( config: &Config, state: &mut impl PostElectraBeaconState

, ) -> Result<()> { @@ -221,60 +227,126 @@ fn process_pending_balance_deposits( state.deposit_balance_to_consume() + get_activation_exit_churn_limit(config, state); let mut processed_amount = 0; - let mut next_deposit_index = 0; + let mut next_deposit_index: u64 = 0; let mut deposits_to_postpone = vec![]; + let mut is_churn_limit_reached = false; + let finalized_slot = compute_start_slot_at_epoch::

(state.finalized_checkpoint().epoch); - for deposit in &state.pending_balance_deposits().clone() { - let validator = state.validators().get(deposit.index)?; + for deposit in &state.pending_deposits().clone() { + // > Do not process deposit requests if Eth1 bridge deposits are not yet applied. + if deposit.slot > GENESIS_SLOT + && state.eth1_deposit_index() < state.deposit_requests_start_index() + { + break; + } - // > Validator is exiting, postpone the deposit until after withdrawable epoch - if validator.exit_epoch < FAR_FUTURE_EPOCH { - if next_epoch <= validator.withdrawable_epoch { - deposits_to_postpone.push(*deposit); - } else { - // > Deposited balance will never become active. Increase balance but do not consume churn - increase_balance(balance(state, deposit.index)?, deposit.amount); - } + // > Check if deposit has been finalized, otherwise, stop processing. + if deposit.slot > finalized_slot { + break; + } + + // > Check if number of processed deposits has not reached the limit, otherwise, stop processing. + if next_deposit_index >= P::MAX_PENDING_DEPOSITS_PER_EPOCH { + break; + } + + let mut is_validator_exited = false; + let mut is_validator_withdrawn = false; + + if let Some(validator_index) = accessors::index_of_public_key(state, deposit.pubkey) { + let validator = state.validators().get(validator_index)?; + + is_validator_exited = validator.exit_epoch < FAR_FUTURE_EPOCH; + is_validator_withdrawn = validator.withdrawable_epoch < next_epoch; + } + + if is_validator_withdrawn { + // > Deposited balance will never become active. Increase balance but do not consume churn + apply_pending_deposit(config, state, deposit)?; + } else if is_validator_exited { + // > Validator is exiting, postpone the deposit until after withdrawable epoch + deposits_to_postpone.push(*deposit); } else { - // > Validator is not exiting, attempt to process deposit - // > Deposit does not fit in the churn, no more deposit processing in this epoch. - if processed_amount + deposit.amount > available_for_processing { + // > Check if deposit fits in the churn, otherwise, do no more deposit processing in this epoch. + is_churn_limit_reached = processed_amount + deposit.amount > available_for_processing; + + if is_churn_limit_reached { break; } - // > Deposit fits in the churn, process it. Increase balance and consume churn. - increase_balance(balance(state, deposit.index)?, deposit.amount); + // > Consume churn and apply deposit. processed_amount += deposit.amount; + apply_pending_deposit(config, state, deposit)?; } // > Regardless of how the deposit was handled, we move on in the queue. next_deposit_index += 1; } - *state.pending_balance_deposits_mut() = PersistentList::try_from_iter( + *state.pending_deposits_mut() = PersistentList::try_from_iter( state - .pending_balance_deposits() + .pending_deposits() .into_iter() .copied() - .skip(next_deposit_index), + .skip(next_deposit_index.try_into()?) + .chain(deposits_to_postpone.into_iter()), )?; - if state.pending_balance_deposits().len_usize() == 0 { - *state.deposit_balance_to_consume_mut() = 0; - } else { + if is_churn_limit_reached { *state.deposit_balance_to_consume_mut() = available_for_processing - processed_amount; + } else { + *state.deposit_balance_to_consume_mut() = 0; } - *state.pending_balance_deposits_mut() = PersistentList::try_from_iter( - state - .pending_balance_deposits() - .into_iter() - .copied() - .chain(deposits_to_postpone.into_iter()), - )?; Ok(()) } +fn apply_pending_deposit( + config: &Config, + state: &mut impl PostElectraBeaconState

, + deposit: &PendingDeposit, +) -> Result<()> { + let PendingDeposit { + pubkey, + withdrawal_credentials, + amount, + .. + } = deposit; + + if let Some(validator_index) = accessors::index_of_public_key(state, deposit.pubkey) { + increase_balance(balance(state, validator_index)?, *amount); + } else if is_valid_deposit_signature(config, deposit) { + block_processing::add_validator_to_registry::

( + state, + (*pubkey).into(), + *withdrawal_credentials, + *amount, + )?; + } + + Ok(()) +} + +fn is_valid_deposit_signature(config: &Config, deposit: &PendingDeposit) -> bool { + let PendingDeposit { + pubkey, + withdrawal_credentials, + amount, + signature, + .. + } = *deposit; + + let deposit_message = DepositMessage { + pubkey, + withdrawal_credentials, + amount, + }; + + deposit_message + .verify(config, signature, &pubkey.into()) + .is_ok() +} + fn process_pending_consolidations( state: &mut impl PostElectraBeaconState

, ) -> Result<()> { @@ -293,17 +365,24 @@ fn process_pending_consolidations( break; } - switch_to_compounding_validator(state, pending_consolidation.target_index)?; + // > Calculate the consolidated balance + let max_effective_balance = get_max_effective_balance::

(source_validator); - let active_balance = get_active_balance(state, pending_consolidation.source_index)?; + let source_effective_balance = core::cmp::min( + state + .balances() + .get(pending_consolidation.source_index) + .copied()?, + max_effective_balance, + ); decrease_balance( balance(state, pending_consolidation.source_index)?, - active_balance, + source_effective_balance, ); increase_balance( balance(state, pending_consolidation.target_index)?, - active_balance, + source_effective_balance, ); next_pending_consolidation += 1; @@ -335,11 +414,7 @@ pub fn process_effective_balance_updates(state: &mut impl PostElectra // > Update effective balances with hysteresis validators.update(|validator| { - let effective_balance_limit = if has_compounding_withdrawal_credential(validator) { - P::MAX_EFFECTIVE_BALANCE_ELECTRA - } else { - P::MIN_ACTIVATION_BALANCE - }; + let max_effective_balance = get_max_effective_balance::

(validator); let balance = balances .next() @@ -351,7 +426,7 @@ pub fn process_effective_balance_updates(state: &mut impl PostElectra if below || above { validator.effective_balance = balance .prev_multiple_of(P::EFFECTIVE_BALANCE_INCREMENT) - .min(effective_balance_limit); + .min(max_effective_balance); } }); } @@ -599,17 +674,17 @@ mod spec_tests { } #[test_resources( - "consensus-spec-tests/tests/mainnet/electra/epoch_processing/pending_balance_deposits/*/*" + "consensus-spec-tests/tests/mainnet/electra/epoch_processing/pending_deposits/*/*" )] - fn mainnet_pending_balance_deposits(case: Case) { - run_pending_balance_deposits_case::(case); + fn mainnet_pending_deposits(case: Case) { + run_pending_deposits_case::(case); } #[test_resources( - "consensus-spec-tests/tests/minimal/electra/epoch_processing/pending_balance_deposits/*/*" + "consensus-spec-tests/tests/minimal/electra/epoch_processing/pending_deposits/*/*" )] - fn minimal_pending_balance_deposits(case: Case) { - run_pending_balance_deposits_case::(case); + fn minimal_pending_deposits(case: Case) { + run_pending_deposits_case::(case); } #[test_resources( @@ -735,9 +810,9 @@ mod spec_tests { run_case::

(case, altair::process_sync_committee_updates); } - fn run_pending_balance_deposits_case(case: Case) { + fn run_pending_deposits_case(case: Case) { run_case::

(case, |state| { - process_pending_balance_deposits(&P::default_config(), state) + process_pending_deposits(&P::default_config(), state) }); } diff --git a/transition_functions/src/lib.rs b/transition_functions/src/lib.rs index c651008e..c58fab83 100644 --- a/transition_functions/src/lib.rs +++ b/transition_functions/src/lib.rs @@ -51,7 +51,6 @@ pub mod unphased { pub(crate) use block_processing::{ process_block_header, process_block_header_for_gossip, process_eth1_data, process_randao, process_voluntary_exit, validate_attestation_with_verifier, validate_deposits, - validate_deposits_without_verifying_merkle_branch, validate_proposer_slashing_with_verifier, CombinedDeposit, }; pub(crate) use epoch_intermediates::ValidatorSummary; diff --git a/transition_functions/src/phase0/block_processing.rs b/transition_functions/src/phase0/block_processing.rs index ac7684cd..d6e0f75b 100644 --- a/transition_functions/src/phase0/block_processing.rs +++ b/transition_functions/src/phase0/block_processing.rs @@ -294,6 +294,7 @@ pub fn process_deposit_data( validator_index, withdrawal_credentials: vec![withdrawal_credentials], amounts: smallvec![amount], + signatures: vec![signature], }; apply_deposits(state, 1, core::iter::once(combined_deposit), NullSlotReport)?; @@ -315,6 +316,7 @@ pub fn process_deposit_data( pubkey, withdrawal_credentials, amounts: smallvec![amount], + signatures: vec![signature], }; apply_deposits(state, 1, core::iter::once(combined_deposit), NullSlotReport)?; @@ -343,6 +345,7 @@ fn apply_deposits( pubkey, withdrawal_credentials, amounts, + .. } => { let public_key_bytes = pubkey.to_bytes(); let first_amount = amounts[0]; diff --git a/transition_functions/src/unphased/block_processing.rs b/transition_functions/src/unphased/block_processing.rs index 13fa0b6b..338f55de 100644 --- a/transition_functions/src/unphased/block_processing.rs +++ b/transition_functions/src/unphased/block_processing.rs @@ -1,5 +1,5 @@ use anyhow::{ensure, Result}; -use bls::CachedPublicKey; +use bls::{CachedPublicKey, SignatureBytes}; use helper_functions::{ accessors::{ attestation_epoch, get_beacon_proposer_index, get_current_epoch, get_randao_mix, @@ -45,11 +45,13 @@ pub enum CombinedDeposit { pubkey: CachedPublicKey, withdrawal_credentials: H256, amounts: GweiVec, + signatures: Vec, }, TopUp { validator_index: ValidatorIndex, withdrawal_credentials: Vec, amounts: GweiVec, + signatures: Vec, }, } @@ -373,23 +375,6 @@ pub fn validate_deposits( config: &Config, state: &impl BeaconState

, deposits: impl IntoIterator, -) -> Result> { - validate_deposits_internal(config, state, deposits, true) -} - -pub fn validate_deposits_without_verifying_merkle_branch( - config: &Config, - state: &impl BeaconState

, - deposits: impl IntoIterator, -) -> Result> { - validate_deposits_internal(config, state, deposits, false) -} - -fn validate_deposits_internal( - config: &Config, - state: &impl BeaconState

, - deposits: impl IntoIterator, - verify_merkle_branch: bool, ) -> Result> { let deposits_by_pubkey = (0..) .zip(deposits) @@ -436,29 +421,33 @@ fn validate_deposits_internal( let mut combined_deposits = deposits_by_pubkey .into_par_iter() .map(|(existing_validator_index, cached_public_key, deposits)| { - if verify_merkle_branch { - for (position, deposit) in deposits.iter().copied() { - // > Verify the Merkle branch - verify_deposit_merkle_branch( - state, - state.eth1_deposit_index() + position, - deposit, - )?; - } + for (position, deposit) in deposits.iter().copied() { + // > Verify the Merkle branch + verify_deposit_merkle_branch( + state, + state.eth1_deposit_index() + position, + deposit, + )?; } let (first_position, _) = deposits[0]; if let Some(validator_index) = existing_validator_index { - let (amounts, withdrawal_credentials) = deposits + let ((amounts, withdrawal_credentials), signatures) = deposits .into_iter() - .map(|(_, deposit)| (deposit.data.amount, deposit.data.withdrawal_credentials)) + .map(|(_, deposit)| { + ( + (deposit.data.amount, deposit.data.withdrawal_credentials), + deposit.data.signature, + ) + }) .unzip(); let combined_deposit = CombinedDeposit::TopUp { validator_index, withdrawal_credentials, amounts, + signatures, }; return Ok(Some((first_position, combined_deposit))); @@ -483,19 +472,23 @@ fn validate_deposits_internal( Ok(first_valid.map(|(position, deposit)| { let DepositData { + pubkey, withdrawal_credentials, amount: first_amount, - .. + signature: first_signature, } = deposit.data; - let amounts = core::iter::once(first_amount) - .chain(deposits.map(|(_, deposit)| deposit.data.amount)) + let (amounts, signatures) = core::iter::once((first_amount, first_signature)) + .chain( + deposits.map(|(_, deposit)| (deposit.data.amount, deposit.data.signature)), + ) .collect(); let combined_deposit = CombinedDeposit::NewValidator { - pubkey: cached_public_key, + pubkey: pubkey.into(), withdrawal_credentials, amounts, + signatures, }; (position, combined_deposit) diff --git a/types/src/collections.rs b/types/src/collections.rs index cf4b79a2..cc2668f8 100644 --- a/types/src/collections.rs +++ b/types/src/collections.rs @@ -12,7 +12,7 @@ use ssz::{PersistentList, PersistentVector, UnhashedBundleSize}; use crate::{ altair::primitives::ParticipationFlags, capella::containers::HistoricalSummary, - electra::containers::{PendingBalanceDeposit, PendingConsolidation, PendingPartialWithdrawal}, + electra::containers::{PendingConsolidation, PendingDeposit, PendingPartialWithdrawal}, phase0::{ containers::{Eth1Data, PendingAttestation, Validator}, primitives::{Gwei, H256}, @@ -53,8 +53,7 @@ pub type InactivityScores

= pub type HistoricalSummaries

= PersistentList::HistoricalRootsLimit>; -pub type PendingBalanceDeposits

= - PersistentList::PendingBalanceDepositsLimit>; +pub type PendingDeposits

= PersistentList::PendingDepositsLimit>; pub type PendingPartialWithdrawals

= PersistentList::PendingPartialWithdrawalsLimit>; diff --git a/types/src/electra/beacon_state.rs b/types/src/electra/beacon_state.rs index 4dc46c74..c050c850 100644 --- a/types/src/electra/beacon_state.rs +++ b/types/src/electra/beacon_state.rs @@ -10,7 +10,7 @@ use crate::{ capella::primitives::WithdrawalIndex, collections::{ Balances, EpochParticipation, Eth1DataVotes, HistoricalRoots, HistoricalSummaries, - InactivityScores, PendingBalanceDeposits, PendingConsolidations, PendingPartialWithdrawals, + InactivityScores, PendingConsolidations, PendingDeposits, PendingPartialWithdrawals, RandaoMixes, RecentRoots, Slashings, Validators, }, deneb::containers::ExecutionPayloadHeader, @@ -101,7 +101,7 @@ pub struct BeaconState { pub consolidation_balance_to_consume: Gwei, #[serde(with = "serde_utils::string_or_native")] pub earliest_consolidation_epoch: Epoch, - pub pending_balance_deposits: PendingBalanceDeposits

, + pub pending_deposits: PendingDeposits

, pub pending_partial_withdrawals: PendingPartialWithdrawals

, pub pending_consolidations: PendingConsolidations

, diff --git a/types/src/electra/consts.rs b/types/src/electra/consts.rs index 24851ca0..d3efdef2 100644 --- a/types/src/electra/consts.rs +++ b/types/src/electra/consts.rs @@ -56,7 +56,7 @@ pub type FinalizedRootIndex = ConcatGeneralizedIndices< /// │ └─95 BeaconState.earliest_exit_epoch /// └─3──6─12┬24┬48┬─96 BeaconState.consolidation_balance_to_consume /// │ │ └─97 BeaconState.earliest_consolidation_epoch -/// │ └49┬─98 BeaconState.pending_balance_deposits +/// │ └49┬─98 BeaconState.pending_deposits /// │ └─99 BeaconState.pending_partial_withdrawals /// └──────100 BeaconState.pending_consolidations /// ``` diff --git a/types/src/electra/containers.rs b/types/src/electra/containers.rs index e0b0df7c..7742b5a7 100644 --- a/types/src/electra/containers.rs +++ b/types/src/electra/containers.rs @@ -197,11 +197,14 @@ pub struct LightClientUpdate { #[derive(Clone, Copy, PartialEq, Eq, Default, Debug, Deserialize, Serialize, Ssz)] #[serde(deny_unknown_fields)] -pub struct PendingBalanceDeposit { - #[serde(with = "serde_utils::string_or_native")] - pub index: ValidatorIndex, +pub struct PendingDeposit { + pub pubkey: PublicKeyBytes, + pub withdrawal_credentials: H256, #[serde(with = "serde_utils::string_or_native")] pub amount: Gwei, + pub signature: SignatureBytes, + #[serde(with = "serde_utils::string_or_native")] + pub slot: Slot, } #[derive(Clone, Copy, PartialEq, Eq, Default, Debug, Deserialize, Serialize, Ssz)] diff --git a/types/src/electra/spec_tests.rs b/types/src/electra/spec_tests.rs index ce8eebca..bc36d95e 100644 --- a/types/src/electra/spec_tests.rs +++ b/types/src/electra/spec_tests.rs @@ -255,9 +255,9 @@ tests_for_type! { } tests_for_type! { - PendingBalanceDeposit, - "consensus-spec-tests/tests/mainnet/electra/ssz_static/PendingBalanceDeposit/*/*", - "consensus-spec-tests/tests/minimal/electra/ssz_static/PendingBalanceDeposit/*/*", + PendingDeposit, + "consensus-spec-tests/tests/mainnet/electra/ssz_static/PendingDeposit/*/*", + "consensus-spec-tests/tests/minimal/electra/ssz_static/PendingDeposit/*/*", } tests_for_type! { diff --git a/types/src/preset.rs b/types/src/preset.rs index a9b30040..a03eacb1 100644 --- a/types/src/preset.rs +++ b/types/src/preset.rs @@ -36,7 +36,7 @@ use crate::{ eip7594::Cell, electra::containers::{ Attestation as ElectraAttestation, AttesterSlashing as ElectraAttesterSlashing, - ConsolidationRequest, DepositRequest, PendingBalanceDeposit, PendingConsolidation, + ConsolidationRequest, DepositRequest, PendingConsolidation, PendingDeposit, PendingPartialWithdrawal, WithdrawalRequest, }, phase0::{ @@ -169,11 +169,7 @@ pub trait Preset: Copy + Eq + Ord + Hash + Default + Debug + Send + Sync + 'stat + Debug + Send + Sync; - type PendingBalanceDepositsLimit: MerkleElements - + Eq - + Debug - + Send - + Sync; + type PendingDepositsLimit: MerkleElements + Eq + Debug + Send + Sync; type PendingConsolidationsLimit: MerkleElements + Eq + Debug + Send + Sync; type PendingPartialWithdrawalsLimit: MerkleElements + Eq @@ -234,6 +230,7 @@ pub trait Preset: Copy + Eq + Ord + Hash + Default + Debug + Send + Sync + 'stat // Electra const MAX_EFFECTIVE_BALANCE_ELECTRA: Gwei = 2_048_000_000_000; const MAX_PENDING_PARTIALS_PER_WITHDRAWALS_SWEEP: u64 = 8; + const MAX_PENDING_DEPOSITS_PER_EPOCH: u64 = 16; const MIN_ACTIVATION_BALANCE: Gwei = 32_000_000_000; const MIN_SLASHING_PENALTY_QUOTIENT_ELECTRA: NonZeroU64 = nonzero!(4096_u64); const WHISTLEBLOWER_REWARD_QUOTIENT_ELECTRA: NonZeroU64 = nonzero!(4096_u64); @@ -297,7 +294,7 @@ impl Preset for Mainnet { type MaxConsolidationRequestsPerPayload = U1; type MaxDepositRequestsPerPayload = U8192; type MaxWithdrawalRequestsPerPayload = U16; - type PendingBalanceDepositsLimit = U134217728; + type PendingDepositsLimit = U134217728; type PendingConsolidationsLimit = U262144; type PendingPartialWithdrawalsLimit = U134217728; @@ -363,7 +360,7 @@ impl Preset for Minimal { type MaxAttestationsElectra; type MaxAttesterSlashingsElectra; type MaxConsolidationRequestsPerPayload; - type PendingBalanceDepositsLimit; + type PendingDepositsLimit; } // Phase 0 @@ -410,7 +407,7 @@ impl Preset for Minimal { const MAX_VALIDATORS_PER_WITHDRAWALS_SWEEP: u64 = 16; // Electra - const MAX_PENDING_PARTIALS_PER_WITHDRAWALS_SWEEP: u64 = 1; + const MAX_PENDING_PARTIALS_PER_WITHDRAWALS_SWEEP: u64 = 2; } /// [Medalla preset](https://github.com/eth-clients/eth2-networks/blob/674f7a1d01d9c18345456eab76e3871b3df2126b/shared/medalla/config.yaml). @@ -463,7 +460,7 @@ impl Preset for Medalla { type MaxConsolidationRequestsPerPayload; type MaxDepositRequestsPerPayload; type MaxWithdrawalRequestsPerPayload; - type PendingBalanceDepositsLimit; + type PendingDepositsLimit; type PendingConsolidationsLimit; type PendingPartialWithdrawalsLimit; @@ -880,7 +877,7 @@ pub struct ElectraPreset { #[serde(with = "serde_utils::string_or_native")] min_slashing_penalty_quotient_electra: NonZeroU64, #[serde(with = "serde_utils::string_or_native")] - pending_balance_deposits_limit: u64, + pending_deposits_limit: u64, #[serde(with = "serde_utils::string_or_native")] pending_consolidations_limit: u64, #[serde(with = "serde_utils::string_or_native")] @@ -903,7 +900,7 @@ impl ElectraPreset { max_withdrawal_requests_per_payload: P::MaxWithdrawalRequestsPerPayload::U64, min_activation_balance: P::MIN_ACTIVATION_BALANCE, min_slashing_penalty_quotient_electra: P::MIN_SLASHING_PENALTY_QUOTIENT_ELECTRA, - pending_balance_deposits_limit: P::PendingBalanceDepositsLimit::U64, + pending_deposits_limit: P::PendingDepositsLimit::U64, pending_consolidations_limit: P::PendingConsolidationsLimit::U64, pending_partial_withdrawals_limit: P::PendingPartialWithdrawalsLimit::U64, whistleblower_reward_quotient_electra: P::WHISTLEBLOWER_REWARD_QUOTIENT_ELECTRA, diff --git a/types/src/traits.rs b/types/src/traits.rs index 5a7dcc28..996ea8db 100644 --- a/types/src/traits.rs +++ b/types/src/traits.rs @@ -51,7 +51,7 @@ use crate::{ }, collections::{ Balances, EpochParticipation, Eth1DataVotes, HistoricalRoots, InactivityScores, - PendingBalanceDeposits, PendingConsolidations, PendingPartialWithdrawals, RandaoMixes, + PendingConsolidations, PendingDeposits, PendingPartialWithdrawals, RandaoMixes, RecentRoots, Slashings, Validators, }, combined::{ @@ -599,7 +599,7 @@ pub trait PostElectraBeaconState: PostCapellaBeaconState

{ fn earliest_exit_epoch(&self) -> Epoch; fn consolidation_balance_to_consume(&self) -> Gwei; fn earliest_consolidation_epoch(&self) -> Epoch; - fn pending_balance_deposits(&self) -> &PendingBalanceDeposits

; + fn pending_deposits(&self) -> &PendingDeposits

; fn pending_partial_withdrawals(&self) -> &PendingPartialWithdrawals

; fn pending_consolidations(&self) -> &PendingConsolidations

; @@ -609,7 +609,7 @@ pub trait PostElectraBeaconState: PostCapellaBeaconState

{ fn earliest_exit_epoch_mut(&mut self) -> &mut Epoch; fn consolidation_balance_to_consume_mut(&mut self) -> &mut Gwei; fn earliest_consolidation_epoch_mut(&mut self) -> &mut Epoch; - fn pending_balance_deposits_mut(&mut self) -> &mut PendingBalanceDeposits

; + fn pending_deposits_mut(&mut self) -> &mut PendingDeposits

; fn pending_partial_withdrawals_mut(&mut self) -> &mut PendingPartialWithdrawals

; fn pending_consolidations_mut(&mut self) -> &mut PendingConsolidations

; } @@ -649,7 +649,7 @@ impl PostElectraBeaconState

for implementor { #[duplicate_item( field return_type; - [pending_balance_deposits] [PendingBalanceDeposits

]; + [pending_deposits] [PendingDeposits

]; [pending_partial_withdrawals] [PendingPartialWithdrawals

]; [pending_consolidations] [PendingConsolidations

]; )] @@ -665,7 +665,7 @@ impl PostElectraBeaconState

for implementor { [earliest_exit_epoch] [earliest_exit_epoch_mut] [Epoch]; [consolidation_balance_to_consume] [consolidation_balance_to_consume_mut] [Gwei]; [earliest_consolidation_epoch] [earliest_consolidation_epoch_mut] [Epoch]; - [pending_balance_deposits] [pending_balance_deposits_mut] [PendingBalanceDeposits

]; + [pending_deposits] [pending_deposits_mut] [PendingDeposits

]; [pending_partial_withdrawals] [pending_partial_withdrawals_mut] [PendingPartialWithdrawals

]; [pending_consolidations] [pending_consolidations_mut] [PendingConsolidations

]; )]