diff --git a/Cargo.lock b/Cargo.lock index 49d10a9457..6dbc4af741 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2886,6 +2886,7 @@ dependencies = [ "serde_json", "serde_yaml", "state_processing", + "store", "tree_hash", "types", "validator_dir", @@ -3657,6 +3658,7 @@ dependencies = [ "eth2_ssz", "itertools", "parking_lot", + "rayon", "serde", "tree_hash", "triomphe", diff --git a/beacon_node/store/src/chunked_vector.rs b/beacon_node/store/src/chunked_vector.rs index 9c248ec4f3..c0b8572d43 100644 --- a/beacon_node/store/src/chunked_vector.rs +++ b/beacon_node/store/src/chunked_vector.rs @@ -21,9 +21,6 @@ use tree_hash::TreeHash; use typenum::Unsigned; use types::VList; -#[cfg(feature = "milhouse")] -use types::milhouse::ImmList; - /// Description of how a `BeaconState` field is updated during state processing. /// /// When storing a state, this allows us to efficiently store only those entries @@ -301,7 +298,7 @@ field!( T::SlotsPerHistoricalRoot, DBColumn::BeaconBlockRoots, |_| OncePerNSlots { n: 1 }, - |state: &BeaconState<_>, index, _| safe_modulo_index(state.block_roots(), index) + |state: &BeaconState<_>, index, _| safe_modulo_index_vect(state.block_roots(), index) ); field!( @@ -311,7 +308,7 @@ field!( T::SlotsPerHistoricalRoot, DBColumn::BeaconStateRoots, |_| OncePerNSlots { n: 1 }, - |state: &BeaconState<_>, index, _| safe_modulo_index(state.state_roots(), index) + |state: &BeaconState<_>, index, _| safe_modulo_index_vect(state.state_roots(), index) ); field!( @@ -323,7 +320,7 @@ field!( |_| OncePerNSlots { n: T::SlotsPerHistoricalRoot::to_u64() }, - |state: &BeaconState<_>, index, _| safe_modulo_index(state.historical_roots(), index) + |state: &BeaconState<_>, index, _| safe_modulo_index_list(state.historical_roots(), index) ); field!( @@ -333,7 +330,7 @@ field!( T::EpochsPerHistoricalVector, DBColumn::BeaconRandaoMixes, |_| OncePerEpoch { lag: 1 }, - |state: &BeaconState<_>, index, _| safe_modulo_index(state.randao_mixes(), index) + |state: &BeaconState<_>, index, _| safe_modulo_index_vect(state.randao_mixes(), index) ); pub fn store_updated_vector, E: EthSpec, S: KeyValueStore>( @@ -572,7 +569,25 @@ fn safe_modulo_index(values: &[T], index: u64) -> Result } #[cfg(feature = "milhouse")] -fn safe_modulo_index, T: Copy>(values: &V, index: u64) -> Result { +fn safe_modulo_index_list( + values: &VList, + index: u64, +) -> Result { + if values.is_empty() { + Err(ChunkError::ZeroLengthVector) + } else { + values + .get(index as usize % values.len()) + .copied() + .ok_or(ChunkError::OutOfBounds) + } +} + +#[cfg(feature = "milhouse")] +fn safe_modulo_index_vect( + values: &FixedVector, + index: u64, +) -> Result { if values.is_empty() { Err(ChunkError::ZeroLengthVector) } else { diff --git a/beacon_node/store/src/partial_beacon_state.rs b/beacon_node/store/src/partial_beacon_state.rs index aae54c4f19..160f94aef2 100644 --- a/beacon_node/store/src/partial_beacon_state.rs +++ b/beacon_node/store/src/partial_beacon_state.rs @@ -79,7 +79,7 @@ where // Inactivity #[superstruct(only(Altair, Merge))] - pub inactivity_scores: VariableList, + pub inactivity_scores: VList, // Light-client sync committees #[superstruct(only(Altair, Merge))] @@ -266,17 +266,9 @@ impl PartialBeaconState { let current_epoch = self.slot().epoch(T::slots_per_epoch()); let len = randao_mixes.len(); - #[cfg(feature = "milhouse")] - { - use milhouse::interface::MutList; - randao_mixes - .replace(current_epoch.as_usize() % len, *self.latest_randao_value())?; - } - - #[cfg(not(feature = "milhouse"))] - { - randao_mixes[current_epoch.as_usize() % len] = *self.latest_randao_value(); - } + *randao_mixes + .get_mut(current_epoch.as_usize() % len) + .unwrap() = *self.latest_randao_value(); *self.randao_mixes_mut() = Some(randao_mixes) } diff --git a/consensus/state_processing/src/common/initiate_validator_exit.rs b/consensus/state_processing/src/common/initiate_validator_exit.rs index fbf4d037ab..a2bb47ebe0 100644 --- a/consensus/state_processing/src/common/initiate_validator_exit.rs +++ b/consensus/state_processing/src/common/initiate_validator_exit.rs @@ -32,13 +32,11 @@ pub fn initiate_validator_exit( .exit_cache_mut() .record_validator_exit(exit_queue_epoch)?; - let mut validators = state.validators_mut(); - let validator = validators.get_validator_mut(index)?; - + // FIXME(sproul): could avoid this second lookup with some clever borrowing + let mut validator = state.get_validator_mut(index)?; validator.exit_epoch = exit_queue_epoch; validator.withdrawable_epoch = exit_queue_epoch.safe_add(spec.min_validator_withdrawability_delay)?; - drop(validators); Ok(()) } diff --git a/consensus/state_processing/src/common/mod.rs b/consensus/state_processing/src/common/mod.rs index a1e4f673b8..334a293ed5 100644 --- a/consensus/state_processing/src/common/mod.rs +++ b/consensus/state_processing/src/common/mod.rs @@ -16,7 +16,7 @@ pub use initiate_validator_exit::initiate_validator_exit; pub use slash_validator::slash_validator; use safe_arith::SafeArith; -use types::{BeaconState, BeaconStateError, EthSpec, GetBalanceMut}; +use types::{BeaconState, BeaconStateError, EthSpec}; /// Increase the balance of a validator, erroring upon overflow, as per the spec. pub fn increase_balance( @@ -24,10 +24,7 @@ pub fn increase_balance( index: usize, delta: u64, ) -> Result<(), BeaconStateError> { - state - .balances_mut() - .get_balance_mut(index)? - .safe_add_assign(delta)?; + state.get_balance_mut(index)?.safe_add_assign(delta)?; Ok(()) } @@ -37,8 +34,7 @@ pub fn decrease_balance( index: usize, delta: u64, ) -> Result<(), BeaconStateError> { - let mut balances = state.balances_mut(); - let balance = balances.get_balance_mut(index)?; + let balance = state.get_balance_mut(index)?; *balance = balance.saturating_sub(delta); Ok(()) } diff --git a/consensus/state_processing/src/common/slash_validator.rs b/consensus/state_processing/src/common/slash_validator.rs index 5a03ea0322..e9d94a1062 100644 --- a/consensus/state_processing/src/common/slash_validator.rs +++ b/consensus/state_processing/src/common/slash_validator.rs @@ -17,16 +17,13 @@ pub fn slash_validator( initiate_validator_exit(state, slashed_index, spec)?; - let mut validators = state.validators_mut(); - let validator = validators.get_validator_mut(slashed_index)?; + let validator = state.get_validator_mut(slashed_index)?; validator.slashed = true; validator.withdrawable_epoch = cmp::max( validator.withdrawable_epoch, epoch.safe_add(T::EpochsPerSlashingsVector::to_u64())?, ); let validator_effective_balance = validator.effective_balance; - drop(validators); - state.set_slashings( epoch, state diff --git a/consensus/state_processing/src/genesis.rs b/consensus/state_processing/src/genesis.rs index 44c008276e..6680c615db 100644 --- a/consensus/state_processing/src/genesis.rs +++ b/consensus/state_processing/src/genesis.rs @@ -96,9 +96,10 @@ pub fn process_activations( state: &mut BeaconState, spec: &ChainSpec, ) -> Result<(), Error> { - let (mut validators, balances) = state.validators_and_balances_mut(); - for index in 0..validators.len() { - let validator = validators.get_validator_mut(index)?; + let (validators, balances) = state.validators_and_balances_mut(); + let mut validators_iter = validators.iter_cow(); + while let Some((index, validator)) = validators_iter.next_cow() { + let validator = validator.to_mut(); let balance = balances .get(index) .copied() diff --git a/consensus/state_processing/src/per_epoch_processing/altair/participation_cache.rs b/consensus/state_processing/src/per_epoch_processing/altair/participation_cache.rs index 503dadfc70..47105cff15 100644 --- a/consensus/state_processing/src/per_epoch_processing/altair/participation_cache.rs +++ b/consensus/state_processing/src/per_epoch_processing/altair/participation_cache.rs @@ -19,6 +19,7 @@ use types::{ TIMELY_TARGET_FLAG_INDEX, }, BeaconState, BeaconStateError, ChainSpec, Epoch, EthSpec, ParticipationFlags, RelativeEpoch, + Validator, }; #[derive(Debug, PartialEq)] @@ -120,12 +121,10 @@ impl SingleEpochParticipationCache { fn process_active_validator( &mut self, val_index: usize, + validator: &Validator, state: &BeaconState, relative_epoch: RelativeEpoch, ) -> Result<(), BeaconStateError> { - let val_balance = state.get_effective_balance(val_index)?; - let validator = state.get_validator(val_index)?; - // Sanity check to ensure the validator is active. let epoch = relative_epoch.into_epoch(state.current_epoch()); if !validator.is_active_at(epoch) { @@ -141,7 +140,8 @@ impl SingleEpochParticipationCache { .ok_or(BeaconStateError::ParticipationOutOfBounds(val_index))?; // All active validators increase the total active balance. - self.total_active_balance.safe_add_assign(val_balance)?; + self.total_active_balance + .safe_add_assign(validator.effective_balance)?; // Only unslashed validators may proceed. if validator.slashed { @@ -156,7 +156,7 @@ impl SingleEpochParticipationCache { // are set for `val_index`. for (flag, balance) in self.total_flag_balances.iter_mut().enumerate() { if epoch_participation.has_flag(flag)? { - balance.safe_add_assign(val_balance)?; + balance.safe_add_assign(validator.effective_balance)?; } } @@ -223,6 +223,7 @@ impl ParticipationCache { if val.is_active_at(current_epoch) { current_epoch_participation.process_active_validator( val_index, + val, state, RelativeEpoch::Current, )?; @@ -231,6 +232,7 @@ impl ParticipationCache { if val.is_active_at(previous_epoch) { previous_epoch_participation.process_active_validator( val_index, + val, state, RelativeEpoch::Previous, )?; diff --git a/consensus/state_processing/src/per_epoch_processing/altair/rewards_and_penalties.rs b/consensus/state_processing/src/per_epoch_processing/altair/rewards_and_penalties.rs index b1c17851d1..defc1d572c 100644 --- a/consensus/state_processing/src/per_epoch_processing/altair/rewards_and_penalties.rs +++ b/consensus/state_processing/src/per_epoch_processing/altair/rewards_and_penalties.rs @@ -69,6 +69,7 @@ pub fn get_flag_index_deltas( let active_increments = total_active_balance.safe_div(spec.effective_balance_increment)?; for &index in participation_cache.eligible_validator_indices() { + // FIXME(sproul): compute base reward in participation cache let base_reward = get_base_reward(state, index, total_active_balance, spec)?; let mut delta = Delta::default(); diff --git a/consensus/state_processing/src/per_epoch_processing/effective_balance_updates.rs b/consensus/state_processing/src/per_epoch_processing/effective_balance_updates.rs index 5378bf47fb..c87871f1d8 100644 --- a/consensus/state_processing/src/per_epoch_processing/effective_balance_updates.rs +++ b/consensus/state_processing/src/per_epoch_processing/effective_balance_updates.rs @@ -2,7 +2,7 @@ use super::errors::EpochProcessingError; use safe_arith::SafeArith; use types::beacon_state::BeaconState; use types::chain_spec::ChainSpec; -use types::{BeaconStateError, EthSpec, GetValidatorMut}; +use types::{BeaconStateError, EthSpec}; pub fn process_effective_balance_updates( state: &mut BeaconState, @@ -13,9 +13,9 @@ pub fn process_effective_balance_updates( .safe_div(spec.hysteresis_quotient)?; let downward_threshold = hysteresis_increment.safe_mul(spec.hysteresis_downward_multiplier)?; let upward_threshold = hysteresis_increment.safe_mul(spec.hysteresis_upward_multiplier)?; - let (mut validators, balances) = state.validators_and_balances_mut(); - for index in 0..validators.len() { - let validator = validators.get_validator_mut(index)?; + let (validators, balances) = state.validators_and_balances_mut(); + let mut validators_iter = validators.iter_cow(); + while let Some((index, validator)) = validators_iter.next_cow() { let balance = balances .get(index) .copied() @@ -24,7 +24,7 @@ pub fn process_effective_balance_updates( if balance.safe_add(downward_threshold)? < validator.effective_balance || validator.effective_balance.safe_add(upward_threshold)? < balance { - validator.effective_balance = std::cmp::min( + validator.to_mut().effective_balance = std::cmp::min( balance.safe_sub(balance.safe_rem(spec.effective_balance_increment)?)?, spec.max_effective_balance, ); diff --git a/consensus/state_processing/src/per_epoch_processing/registry_updates.rs b/consensus/state_processing/src/per_epoch_processing/registry_updates.rs index 93daa7def2..4fd2d68586 100644 --- a/consensus/state_processing/src/per_epoch_processing/registry_updates.rs +++ b/consensus/state_processing/src/per_epoch_processing/registry_updates.rs @@ -1,7 +1,7 @@ use crate::{common::initiate_validator_exit, per_epoch_processing::Error}; use itertools::Itertools; use safe_arith::SafeArith; -use types::{BeaconState, ChainSpec, EthSpec, GetValidatorMut, Validator}; +use types::{BeaconState, ChainSpec, EthSpec, Validator}; /// Performs a validator registry update, if required. /// @@ -30,13 +30,11 @@ pub fn process_registry_updates( .collect(); for index in indices_to_update { - let mut validators = state.validators_mut(); - let validator = validators.get_validator_mut(index)?; + let validator = state.get_validator_mut(index)?; if validator.is_eligible_for_activation_queue(spec) { validator.activation_eligibility_epoch = current_epoch.safe_add(1)?; } if is_ejectable(validator) { - drop(validators); initiate_validator_exit(state, index, spec)?; } } @@ -54,9 +52,8 @@ pub fn process_registry_updates( // Dequeue validators for activation up to churn limit let churn_limit = state.get_churn_limit(spec)? as usize; let delayed_activation_epoch = state.compute_activation_exit_epoch(current_epoch, spec)?; - let mut validators = state.validators_mut(); for index in activation_queue.into_iter().take(churn_limit) { - validators.get_validator_mut(index)?.activation_epoch = delayed_activation_epoch; + state.get_validator_mut(index)?.activation_epoch = delayed_activation_epoch; } Ok(()) diff --git a/consensus/state_processing/src/per_epoch_processing/slashings.rs b/consensus/state_processing/src/per_epoch_processing/slashings.rs index 69807a5e5f..e1632280f4 100644 --- a/consensus/state_processing/src/per_epoch_processing/slashings.rs +++ b/consensus/state_processing/src/per_epoch_processing/slashings.rs @@ -1,6 +1,6 @@ use crate::per_epoch_processing::Error; use safe_arith::{SafeArith, SafeArithIter}; -use types::{BeaconState, ChainSpec, EthSpec, GetBalanceMut, GetValidatorMut, Unsigned}; +use types::{BeaconState, BeaconStateError, ChainSpec, EthSpec, Unsigned}; /// Process slashings. pub fn process_slashings( @@ -16,9 +16,9 @@ pub fn process_slashings( total_balance, ); - let (validators, mut balances) = state.validators_and_balances_mut(); - for index in 0..validators.len() { - let validator = validators.get_validator(index)?; + let (validators, balances) = state.validators_and_balances_mut(); + let mut validators_iter = validators.iter_cow(); + while let Some((index, validator)) = validators_iter.next_cow() { if validator.slashed && epoch.safe_add(T::EpochsPerSlashingsVector::to_u64().safe_div(2)?)? == validator.withdrawable_epoch @@ -33,7 +33,9 @@ pub fn process_slashings( .safe_mul(increment)?; // Equivalent to `decrease_balance(state, index, penalty)`, but avoids borrowing `state`. - let balance = balances.get_balance_mut(index)?; + let balance = balances + .get_mut(index) + .ok_or(BeaconStateError::BalancesOutOfBounds(index))?; *balance = balance.saturating_sub(penalty); } } diff --git a/consensus/state_processing/src/upgrade/altair.rs b/consensus/state_processing/src/upgrade/altair.rs index b32afc5ee3..94c65e270a 100644 --- a/consensus/state_processing/src/upgrade/altair.rs +++ b/consensus/state_processing/src/upgrade/altair.rs @@ -51,7 +51,7 @@ pub fn upgrade_to_altair( let default_epoch_participation = VariableList::new(vec![ParticipationFlags::default(); pre.validators.len()])?; - let inactivity_scores = VariableList::new(vec![0; pre.validators.len()])?; + let inactivity_scores = VList::new(vec![0; pre.validators.len()])?; let temp_sync_committee = Arc::new(SyncCommittee::temporary()?); diff --git a/consensus/types/src/beacon_state.rs b/consensus/types/src/beacon_state.rs index ab02257e82..69d0a5ca39 100644 --- a/consensus/types/src/beacon_state.rs +++ b/consensus/types/src/beacon_state.rs @@ -47,28 +47,6 @@ mod tests; #[cfg(not(feature = "milhouse"))] mod tree_hash_cache; -#[cfg(feature = "milhouse")] -mod type_aliases { - use super::*; - - pub type ListMut<'a, T, N> = Interface<'a, T, List>; - pub type VectMut<'a, T, N> = Interface<'a, T, FixedVector>; - pub type ValidatorsMut<'a, N> = ListMut<'a, Validator, N>; - pub type BalancesMut<'a, N> = ListMut<'a, u64, N>; -} - -#[cfg(not(feature = "milhouse"))] -mod type_aliases { - use super::*; - - pub type ListMut<'a, T, N> = &'a mut VList; - pub type VectMut<'a, T, N> = &'a mut FixedVector; - pub type ValidatorsMut<'a, N> = &'a mut VList; - pub type BalancesMut<'a, N> = ListMut<'a, u64, N>; -} - -pub use type_aliases::*; - pub const CACHED_EPOCHS: usize = 3; const MAX_RANDOM_BYTE: u64 = (1 << 8) - 1; @@ -248,10 +226,8 @@ where // History pub latest_block_header: BeaconBlockHeader, - #[superstruct(getter(rename = "block_roots_raw"))] #[test_random(default)] pub block_roots: FixedVector, - #[superstruct(getter(rename = "state_roots_raw"))] #[test_random(default)] pub state_roots: FixedVector, #[test_random(default)] @@ -266,22 +242,18 @@ where pub eth1_deposit_index: u64, // Registry - #[superstruct(getter(rename = "validators_raw"))] #[test_random(default)] pub validators: VList, // FIXME(sproul): serde quoting // #[serde(with = "ssz_types::serde_utils::quoted_u64_var_list")] - #[superstruct(getter(rename = "balances_raw"))] #[test_random(default)] pub balances: VList, // Randomness - #[superstruct(getter(rename = "randao_mixes_raw"))] #[test_random(default)] pub randao_mixes: FixedVector, // Slashings - #[superstruct(getter(rename = "slashings_raw"))] #[test_random(default)] // FIXME(sproul): serde quoting // #[serde(with = "ssz_types::serde_utils::quoted_u64_fixed_vec")] @@ -312,9 +284,11 @@ where pub finalized_checkpoint: Checkpoint, // Inactivity - #[serde(with = "ssz_types::serde_utils::quoted_u64_var_list")] + // FIXME(sproul): quoting + // #[serde(with = "ssz_types::serde_utils::quoted_u64_var_list")] #[superstruct(only(Altair, Merge))] - pub inactivity_scores: VariableList, + #[test_random(default)] + pub inactivity_scores: VList, // Light-client sync committees #[superstruct(only(Altair, Merge))] @@ -990,7 +964,7 @@ impl BeaconState { /// Fill `randao_mixes` with pub fn fill_randao_mixes_with(&mut self, index_root: Hash256) { - *self.randao_mixes_raw_mut() = FixedVector::from_elem(index_root); + *self.randao_mixes_mut() = FixedVector::from_elem(index_root); } /// Safely obtains the index for `randao_mixes` @@ -1139,116 +1113,25 @@ impl BeaconState { Ok(()) } - pub fn validators(&self) -> &VList { - self.validators_raw() - } - - pub fn validators_mut(&mut self) -> ValidatorsMut { - #[cfg(not(feature = "milhouse"))] - { - self.validators_raw_mut() - } - #[cfg(feature = "milhouse")] - { - self.validators_raw_mut().as_mut() - } - } - - pub fn balances(&self) -> &VList { - self.balances_raw() - } - - pub fn balances_mut(&mut self) -> BalancesMut { - #[cfg(not(feature = "milhouse"))] - { - self.balances_raw_mut() - } - #[cfg(feature = "milhouse")] - { - self.balances_raw_mut().as_mut() - } - } - /// Convenience accessor for validators and balances simultaneously. pub fn validators_and_balances_mut( &mut self, ) -> ( - ValidatorsMut, - BalancesMut, + &mut VList, + &mut VList, ) { - #[cfg(not(feature = "milhouse"))] match self { 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), } - - #[cfg(feature = "milhouse")] - match self { - BeaconState::Base(state) => (state.validators.as_mut(), state.balances.as_mut()), - BeaconState::Altair(state) => (state.validators.as_mut(), state.balances.as_mut()), - BeaconState::Merge(state) => (state.validators.as_mut(), state.balances.as_mut()), - } } - pub fn block_roots(&self) -> &FixedVector { - self.block_roots_raw() - } - - pub fn block_roots_mut(&mut self) -> VectMut { - #[cfg(not(feature = "milhouse"))] - { - self.block_roots_raw_mut() - } - #[cfg(feature = "milhouse")] - { - self.block_roots_raw_mut().as_mut() - } - } - - pub fn state_roots(&self) -> &FixedVector { - self.state_roots_raw() - } - - pub fn state_roots_mut(&mut self) -> VectMut { - #[cfg(not(feature = "milhouse"))] - { - self.state_roots_raw_mut() - } - #[cfg(feature = "milhouse")] - { - self.state_roots_raw_mut().as_mut() - } - } - - pub fn randao_mixes(&self) -> &FixedVector { - self.randao_mixes_raw() - } - - pub fn randao_mixes_mut(&mut self) -> VectMut { - #[cfg(not(feature = "milhouse"))] - { - self.randao_mixes_raw_mut() - } - #[cfg(feature = "milhouse")] - { - self.randao_mixes_raw_mut().as_mut() - } - } - - pub fn slashings(&self) -> &FixedVector { - self.slashings_raw() - } - - pub fn slashings_mut(&mut self) -> VectMut { - #[cfg(not(feature = "milhouse"))] - { - self.slashings_raw_mut() - } - #[cfg(feature = "milhouse")] - { - self.slashings_raw_mut().as_mut() - } + /// Get a mutable reference to the balance of a single validator. + pub fn get_balance_mut(&mut self, validator_index: usize) -> Result<&mut u64, Error> { + self.balances_mut() + .get_mut(validator_index) + .ok_or(Error::BalancesOutOfBounds(validator_index)) } /// Generate a seed for the given `epoch`. @@ -1293,14 +1176,22 @@ impl BeaconState { .ok_or(Error::UnknownValidator(validator_index)) } - /* FIXME(sproul): lens? /// Safe mutator for the `validators` list. pub fn get_validator_mut(&mut self, validator_index: usize) -> Result<&mut Validator, Error> { self.validators_mut() .get_mut(validator_index) .ok_or(Error::UnknownValidator(validator_index)) } - */ + + /// Safe copy-on-write accessor for the `validators` list. + pub fn get_validator_cow( + &mut self, + validator_index: usize, + ) -> Result, Error> { + self.validators_mut() + .get_cow(validator_index) + .ok_or(Error::UnknownValidator(validator_index)) + } /// Return the effective balance for a validator with the given `validator_index`. pub fn get_effective_balance(&self, validator_index: usize) -> Result { @@ -1387,6 +1278,28 @@ impl BeaconState { )) } + pub fn compute_total_active_balance( + &self, + epoch: Epoch, + spec: &ChainSpec, + ) -> Result { + if epoch != self.current_epoch() && epoch != self.next_epoch()? { + return Err(Error::EpochOutOfBounds); + } + + let mut total_active_balance = 0; + + for validator in self.validators() { + if validator.is_active_at(epoch) { + total_active_balance.safe_add_assign(validator.effective_balance)?; + } + } + Ok(std::cmp::max( + total_active_balance, + spec.effective_balance_increment, + )) + } + /// Implementation of `get_total_active_balance`, matching the spec. /// /// Requires the total active balance cache to be initialised, which is initialised whenever @@ -1410,16 +1323,9 @@ impl BeaconState { } /// Build the total active balance cache. - /// - /// This function requires the current committee cache to be already built. It is called - /// automatically when `build_committee_cache` is called for the current epoch. fn build_total_active_balance_cache(&mut self, spec: &ChainSpec) -> Result<(), Error> { - // Order is irrelevant, so use the cached indices. let current_epoch = self.current_epoch(); - let total_active_balance = self.get_total_balance( - self.get_cached_active_validator_indices(RelativeEpoch::Current)?, - spec, - )?; + let total_active_balance = self.compute_total_active_balance(current_epoch, spec)?; *self.total_active_balance_mut() = Some((current_epoch, total_active_balance)); Ok(()) } @@ -1578,10 +1484,9 @@ impl BeaconState { // not yet been advanced. let new_current_epoch = self.next_epoch()?; if curr_cache.is_initialized_at(new_current_epoch) { - *self.total_active_balance_mut() = Some(( - new_current_epoch, - self.get_total_balance(curr_cache.active_validator_indices(), spec)?, - )); + let total_active_balance = + self.compute_total_active_balance(new_current_epoch, spec)?; + *self.total_active_balance_mut() = Some((new_current_epoch, total_active_balance)); } // If the cache is not initialized, then the previous cached value for the total balance is // wrong, so delete it. @@ -1671,6 +1576,38 @@ impl BeaconState { *self.pubkey_cache_mut() = PubkeyCache::default() } + /// Check if the `BeaconState` has any pending mutations. + pub fn has_pending_mutations(&self) -> bool { + // FIXME(sproul): check this more thoroughly + self.block_roots().has_pending_updates() + || self.state_roots().has_pending_updates() + || self.historical_roots().has_pending_updates() + || self.eth1_data_votes().has_pending_updates() + || self.validators().has_pending_updates() + || self.balances().has_pending_updates() + || self.randao_mixes().has_pending_updates() + || self.slashings().has_pending_updates() + || self + .inactivity_scores() + .map_or(false, VList::has_pending_updates) + } + + pub fn apply_pending_mutations(&mut self) -> Result<(), Error> { + self.block_roots_mut().apply_updates()?; + self.state_roots_mut().apply_updates()?; + self.historical_roots_mut().apply_updates()?; + self.eth1_data_votes_mut().apply_updates()?; + self.validators_mut().apply_updates()?; + self.balances_mut().apply_updates()?; + self.randao_mixes_mut().apply_updates()?; + self.slashings_mut().apply_updates()?; + + if let Ok(inactivity_scores) = self.inactivity_scores_mut() { + inactivity_scores.apply_updates()?; + } + Ok(()) + } + /// Initialize but don't fill the tree hash cache, if it isn't already initialized. pub fn initialize_tree_hash_cache(&mut self) { #[cfg(not(feature = "milhouse"))] @@ -1700,7 +1637,10 @@ impl BeaconState { } } #[cfg(feature = "milhouse")] - Ok(self.tree_hash_root()) + { + self.apply_pending_mutations()?; + Ok(self.tree_hash_root()) + } } /// Compute the tree hash root of the validators using the tree hash cache. @@ -1724,7 +1664,10 @@ impl BeaconState { } } #[cfg(feature = "milhouse")] - Ok(self.validators().tree_hash_root()) + { + self.validators_mut().apply_updates()?; + Ok(self.validators().tree_hash_root()) + } } /// Completely drops the tree hash cache, replacing it with a new, empty cache. diff --git a/consensus/types/src/lib.rs b/consensus/types/src/lib.rs index 695ecbc101..ef0fcf804c 100644 --- a/consensus/types/src/lib.rs +++ b/consensus/types/src/lib.rs @@ -66,7 +66,6 @@ pub mod voluntary_exit; pub mod slot_epoch_macros; pub mod config_and_preset; pub mod fork_context; -pub mod mixin; pub mod participation_flags; pub mod participation_list; pub mod preset; @@ -123,7 +122,6 @@ pub use crate::free_attestation::FreeAttestation; pub use crate::graffiti::{Graffiti, GRAFFITI_BYTES_LEN}; pub use crate::historical_batch::HistoricalBatch; pub use crate::indexed_attestation::IndexedAttestation; -pub use crate::mixin::{GetBalanceMut, GetValidatorMut}; pub use crate::participation_flags::ParticipationFlags; pub use crate::participation_list::ParticipationList; pub use crate::pending_attestation::PendingAttestation; diff --git a/consensus/types/src/mixin.rs b/consensus/types/src/mixin.rs deleted file mode 100644 index 41f2203740..0000000000 --- a/consensus/types/src/mixin.rs +++ /dev/null @@ -1,36 +0,0 @@ -use crate::beacon_state::{BalancesMut, Error, ValidatorsMut}; -use crate::{Unsigned, Validator}; - -pub trait GetValidatorMut { - fn get_validator(&self, index: usize) -> Result<&Validator, Error>; - - fn get_validator_mut(&mut self, index: usize) -> Result<&mut Validator, Error>; -} - -impl<'a, N: Unsigned> GetValidatorMut for ValidatorsMut<'a, N> { - fn get_validator(&self, index: usize) -> Result<&Validator, Error> { - self.get(index).ok_or(Error::UnknownValidator(index)) - } - - fn get_validator_mut(&mut self, index: usize) -> Result<&mut Validator, Error> { - self.get_mut(index).ok_or(Error::UnknownValidator(index)) - } -} - -pub trait GetBalanceMut { - fn get_balance(&self, index: usize) -> Result; - - fn get_balance_mut(&mut self, index: usize) -> Result<&mut u64, Error>; -} - -impl<'a, N: Unsigned> GetBalanceMut for BalancesMut<'a, N> { - fn get_balance(&self, index: usize) -> Result { - self.get(index) - .copied() - .ok_or(Error::BalancesOutOfBounds(index)) - } - - fn get_balance_mut(&mut self, index: usize) -> Result<&mut u64, Error> { - self.get_mut(index).ok_or(Error::BalancesOutOfBounds(index)) - } -} diff --git a/lcli/src/replace_state_pubkeys.rs b/lcli/src/replace_state_pubkeys.rs index bba391aca8..97ef94e32a 100644 --- a/lcli/src/replace_state_pubkeys.rs +++ b/lcli/src/replace_state_pubkeys.rs @@ -42,7 +42,7 @@ pub fn run(testnet_dir: PathBuf, matches: &ArgMatches) -> Result<(), let mut deposit_tree = DepositDataTree::create(&[], 0, DEPOSIT_TREE_DEPTH); let mut deposit_root = Hash256::zero(); - let mut validators = state.validators_mut(); + let validators = state.validators_mut(); for index in 0..validators.len() { let (secret, _) = recover_validator_secret_from_mnemonic(seed.as_bytes(), index as u32, KeyType::Voting)