From eda8ec8c558ac589a2e2dc21961bad0d44ab39c0 Mon Sep 17 00:00:00 2001 From: Michael Sproul Date: Tue, 7 May 2019 18:21:25 +1000 Subject: [PATCH] spec: registry updates v0.6.1 --- .../per_epoch_processing/registry_updates.rs | 70 ++++++++ .../update_registry_and_shuffling_data.rs | 150 ------------------ 2 files changed, 70 insertions(+), 150 deletions(-) create mode 100644 eth2/state_processing/src/per_epoch_processing/registry_updates.rs delete mode 100644 eth2/state_processing/src/per_epoch_processing/update_registry_and_shuffling_data.rs diff --git a/eth2/state_processing/src/per_epoch_processing/registry_updates.rs b/eth2/state_processing/src/per_epoch_processing/registry_updates.rs new file mode 100644 index 000000000..97e5d81da --- /dev/null +++ b/eth2/state_processing/src/per_epoch_processing/registry_updates.rs @@ -0,0 +1,70 @@ +use super::super::common::initiate_validator_exit; +use super::Error; +use itertools::{Either, Itertools}; +use types::*; + +/// Peforms a validator registry update, if required. +/// +/// Spec v0.6.1 +pub fn process_registry_updates( + state: &mut BeaconState, + current_total_balance: u64, + spec: &ChainSpec, +) -> Result<(), Error> { + // Process activation eligibility and ejections. + // Collect eligible and exiting validators (we need to avoid mutating the state while iterating). + // We assume it's safe to re-order the change in eligibility and `initiate_validator_exit`. + // Rest assured exiting validators will still be exited in the same order as in the spec. + let current_epoch = state.current_epoch(spec); + let is_eligible = |validator: &Validator| { + validator.activation_eligibility_epoch == spec.far_future_epoch + && validator.effective_balance >= spec.max_effective_balance + }; + let is_exiting_validator = |validator: &Validator| { + validator.is_active_at(current_epoch) + && validator.effective_balance <= spec.ejection_balance + }; + let (eligible_validators, exiting_validators): (Vec<_>, Vec<_>) = state + .validator_registry + .iter() + .enumerate() + .filter(|(_, validator)| is_eligible(validator) || is_exiting_validator(validator)) + .partition_map(|(index, validator)| { + if is_eligible(validator) { + Either::Left(index) + } else { + Either::Right(index) + } + }); + for index in eligible_validators { + state.validator_registry[index].activation_eligibility_epoch = current_epoch; + } + for index in exiting_validators { + initiate_validator_exit(state, index, spec)?; + } + + // Queue validators eligible for activation and not dequeued for activation prior to finalized epoch + let activation_queue = state + .validator_registry + .iter() + .enumerate() + .filter(|(_, validator)| { + validator.activation_eligibility_epoch != spec.far_future_epoch + && validator.activation_epoch + >= state.get_delayed_activation_exit_epoch(state.finalized_epoch, spec) + }) + .sorted_by_key(|(_, validator)| validator.activation_eligibility_epoch) + .map(|(index, _)| index) + .collect_vec(); + + let churn_limit = state.get_churn_limit(spec)? as usize; + let delayed_activation_epoch = state.get_delayed_activation_exit_epoch(current_epoch, spec); + for index in activation_queue.into_iter().take(churn_limit) { + let validator = &mut state.validator_registry[index]; + if validator.activation_epoch == spec.far_future_epoch { + validator.activation_epoch = delayed_activation_epoch; + } + } + + Ok(()) +} diff --git a/eth2/state_processing/src/per_epoch_processing/update_registry_and_shuffling_data.rs b/eth2/state_processing/src/per_epoch_processing/update_registry_and_shuffling_data.rs deleted file mode 100644 index d290d2987..000000000 --- a/eth2/state_processing/src/per_epoch_processing/update_registry_and_shuffling_data.rs +++ /dev/null @@ -1,150 +0,0 @@ -use super::super::common::exit_validator; -use super::Error; -use types::*; - -/// Peforms a validator registry update, if required. -/// -/// Spec v0.5.1 -pub fn update_registry_and_shuffling_data( - state: &mut BeaconState, - current_total_balance: u64, - spec: &ChainSpec, -) -> Result<(), Error> { - // First set previous shuffling data to current shuffling data. - state.previous_shuffling_epoch = state.current_shuffling_epoch; - state.previous_shuffling_start_shard = state.previous_shuffling_start_shard; - state.previous_shuffling_seed = state.previous_shuffling_seed; - - let current_epoch = state.current_epoch(spec); - let next_epoch = current_epoch + 1; - - // Check we should update, and if so, update. - if should_update_validator_registry(state, spec)? { - update_validator_registry(state, current_total_balance, spec)?; - - // If we update the registry, update the shuffling data and shards as well. - state.current_shuffling_epoch = next_epoch; - state.current_shuffling_start_shard = { - let active_validators = - state.get_cached_active_validator_indices(RelativeEpoch::Current, spec)?; - let epoch_committee_count = spec.get_epoch_committee_count(active_validators.len()); - - (state.current_shuffling_start_shard + epoch_committee_count) % spec.shard_count - }; - state.current_shuffling_seed = state.generate_seed(state.current_shuffling_epoch, spec)?; - } else { - // If processing at least on crosslink keeps failing, the reshuffle every power of two, but - // don't update the current_shuffling_start_shard. - let epochs_since_last_update = current_epoch - state.validator_registry_update_epoch; - - if epochs_since_last_update > 1 && epochs_since_last_update.is_power_of_two() { - state.current_shuffling_epoch = next_epoch; - state.current_shuffling_seed = - state.generate_seed(state.current_shuffling_epoch, spec)?; - } - } - - Ok(()) -} - -/// Returns `true` if the validator registry should be updated during an epoch processing. -/// -/// Spec v0.5.1 -pub fn should_update_validator_registry( - state: &BeaconState, - spec: &ChainSpec, -) -> Result { - if state.finalized_epoch <= state.validator_registry_update_epoch { - return Ok(false); - } - - let num_active_validators = state - .get_cached_active_validator_indices(RelativeEpoch::Current, spec)? - .len(); - let current_epoch_committee_count = spec.get_epoch_committee_count(num_active_validators); - - for shard in (0..current_epoch_committee_count) - .map(|i| (state.current_shuffling_start_shard + i as u64) % spec.shard_count) - { - if state.latest_crosslinks[shard as usize].epoch <= state.validator_registry_update_epoch { - return Ok(false); - } - } - - Ok(true) -} - -/// Update validator registry, activating/exiting validators if possible. -/// -/// Note: Utilizes the cache and will fail if the appropriate cache is not initialized. -/// -/// Spec v0.5.1 -pub fn update_validator_registry( - state: &mut BeaconState, - current_total_balance: u64, - spec: &ChainSpec, -) -> Result<(), Error> { - let current_epoch = state.current_epoch(spec); - - let max_balance_churn = std::cmp::max( - spec.max_deposit_amount, - current_total_balance / (2 * spec.max_balance_churn_quotient), - ); - - // Activate validators within the allowable balance churn. - let mut balance_churn = 0; - for index in 0..state.validator_registry.len() { - let not_activated = - state.validator_registry[index].activation_epoch == spec.far_future_epoch; - let has_enough_balance = state.validator_balances[index] >= spec.max_deposit_amount; - - if not_activated && has_enough_balance { - // Check the balance churn would be within the allowance. - balance_churn += state.get_effective_balance(index, spec)?; - if balance_churn > max_balance_churn { - break; - } - - activate_validator(state, index, false, spec); - } - } - - // Exit validators within the allowable balance churn. - let mut balance_churn = 0; - for index in 0..state.validator_registry.len() { - let not_exited = state.validator_registry[index].exit_epoch == spec.far_future_epoch; - let has_initiated_exit = state.validator_registry[index].initiated_exit; - - if not_exited && has_initiated_exit { - // Check the balance churn would be within the allowance. - balance_churn += state.get_effective_balance(index, spec)?; - if balance_churn > max_balance_churn { - break; - } - - exit_validator(state, index, spec)?; - } - } - - state.validator_registry_update_epoch = current_epoch; - - Ok(()) -} - -/// Activate the validator of the given ``index``. -/// -/// Spec v0.5.1 -pub fn activate_validator( - state: &mut BeaconState, - validator_index: usize, - is_genesis: bool, - spec: &ChainSpec, -) { - let current_epoch = state.current_epoch(spec); - - state.validator_registry[validator_index].activation_epoch = if is_genesis { - spec.genesis_epoch - } else { - state.get_delayed_activation_exit_epoch(current_epoch, spec) - } -}