From 6f919e6f7dbb6879afcd2af51409184887748d49 Mon Sep 17 00:00:00 2001 From: Paul Hauner Date: Thu, 14 Mar 2019 11:53:50 +1100 Subject: [PATCH 1/5] Add first iteration on faster rewards processing. --- .../benches/bench_epoch_processing.rs | 19 +- eth2/state_processing/benches/benches.rs | 2 +- .../src/per_epoch_processing.rs | 151 ++++++-------- .../src/per_epoch_processing/attesters.rs | 195 ++++++++++++++++++ 4 files changed, 265 insertions(+), 102 deletions(-) create mode 100644 eth2/state_processing/src/per_epoch_processing/attesters.rs diff --git a/eth2/state_processing/benches/bench_epoch_processing.rs b/eth2/state_processing/benches/bench_epoch_processing.rs index e4981b200..93c6c7ebd 100644 --- a/eth2/state_processing/benches/bench_epoch_processing.rs +++ b/eth2/state_processing/benches/bench_epoch_processing.rs @@ -150,13 +150,15 @@ fn bench_epoch_processing(c: &mut Criterion, state: &BeaconState, spec: &ChainSp let state_clone = state.clone(); let spec_clone = spec.clone(); + let active_validator_indices = calculate_active_validator_indices(&state, &spec); c.bench( &format!("{}/epoch_processing", desc), Benchmark::new("calculate_attester_sets", move |b| { b.iter_batched( || state_clone.clone(), |mut state| { - calculate_attester_sets(&mut state, &spec_clone).unwrap(); + calculate_attester_sets(&mut state, &active_validator_indices, &spec_clone) + .unwrap(); state }, criterion::BatchSize::SmallInput, @@ -168,8 +170,8 @@ fn bench_epoch_processing(c: &mut Criterion, state: &BeaconState, spec: &ChainSp let state_clone = state.clone(); let spec_clone = spec.clone(); let previous_epoch = state.previous_epoch(&spec); - let attesters = calculate_attester_sets(&state, &spec).unwrap(); let active_validator_indices = calculate_active_validator_indices(&state, &spec); + let attesters = calculate_attester_sets(&state, &active_validator_indices, &spec).unwrap(); let current_total_balance = state.get_total_balance(&active_validator_indices[..], &spec); let previous_total_balance = state.get_total_balance( &get_active_validator_indices(&state.validator_registry, previous_epoch)[..], @@ -185,8 +187,8 @@ fn bench_epoch_processing(c: &mut Criterion, state: &BeaconState, spec: &ChainSp &mut state, current_total_balance, previous_total_balance, - attesters.previous_epoch_boundary.balance, - attesters.current_epoch_boundary.balance, + attesters.balances.previous_epoch_boundary, + attesters.balances.current_epoch_boundary, &spec_clone, ); state @@ -214,8 +216,8 @@ fn bench_epoch_processing(c: &mut Criterion, state: &BeaconState, spec: &ChainSp let mut state_clone = state.clone(); let spec_clone = spec.clone(); let previous_epoch = state.previous_epoch(&spec); - let attesters = calculate_attester_sets(&state, &spec).unwrap(); let active_validator_indices = calculate_active_validator_indices(&state, &spec); + let attesters = calculate_attester_sets(&state, &active_validator_indices, &spec).unwrap(); let previous_total_balance = state.get_total_balance( &get_active_validator_indices(&state.validator_registry, previous_epoch)[..], &spec, @@ -229,7 +231,6 @@ fn bench_epoch_processing(c: &mut Criterion, state: &BeaconState, spec: &ChainSp |mut state| { process_rewards_and_penalities( &mut state, - &active_validator_indices, &attesters, previous_total_balance, &winning_root_for_shards, @@ -264,8 +265,8 @@ fn bench_epoch_processing(c: &mut Criterion, state: &BeaconState, spec: &ChainSp let mut state_clone = state.clone(); let spec_clone = spec.clone(); let previous_epoch = state.previous_epoch(&spec); - let attesters = calculate_attester_sets(&state, &spec).unwrap(); let active_validator_indices = calculate_active_validator_indices(&state, &spec); + let attesters = calculate_attester_sets(&state, &active_validator_indices, &spec).unwrap(); let current_total_balance = state.get_total_balance(&active_validator_indices[..], spec); let previous_total_balance = state.get_total_balance( &get_active_validator_indices(&state.validator_registry, previous_epoch)[..], @@ -279,8 +280,8 @@ fn bench_epoch_processing(c: &mut Criterion, state: &BeaconState, spec: &ChainSp &mut state_clone, current_total_balance, previous_total_balance, - attesters.previous_epoch_boundary.balance, - attesters.current_epoch_boundary.balance, + attesters.balances.previous_epoch_boundary, + attesters.balances.current_epoch_boundary, spec, ); assert!( diff --git a/eth2/state_processing/benches/benches.rs b/eth2/state_processing/benches/benches.rs index ad8c4f714..c619e1ef7 100644 --- a/eth2/state_processing/benches/benches.rs +++ b/eth2/state_processing/benches/benches.rs @@ -18,8 +18,8 @@ pub fn state_processing(c: &mut Criterion) { Builder::from_env(Env::default().default_filter_or(LOG_LEVEL)).init(); } - bench_block_processing::bench_block_processing_n_validators(c, VALIDATOR_COUNT); bench_epoch_processing::bench_epoch_processing_n_validators(c, VALIDATOR_COUNT); + bench_block_processing::bench_block_processing_n_validators(c, VALIDATOR_COUNT); } criterion_group!(benches, state_processing); diff --git a/eth2/state_processing/src/per_epoch_processing.rs b/eth2/state_processing/src/per_epoch_processing.rs index 4abbe012c..4fe53dd6b 100644 --- a/eth2/state_processing/src/per_epoch_processing.rs +++ b/eth2/state_processing/src/per_epoch_processing.rs @@ -1,6 +1,5 @@ -use attester_sets::AttesterSets; +use attesters::Attesters; use errors::EpochProcessingError as Error; -use fnv::FnvHashMap; use fnv::FnvHashSet; use integer_sqrt::IntegerSquareRoot; use rayon::prelude::*; @@ -11,6 +10,7 @@ use types::{validator_registry::get_active_validator_indices, *}; use winning_root::{winning_root, WinningRoot}; pub mod attester_sets; +pub mod attesters; pub mod errors; pub mod inclusion_distance; pub mod tests; @@ -35,8 +35,6 @@ pub fn per_epoch_processing(state: &mut BeaconState, spec: &ChainSpec) -> Result state.build_epoch_cache(RelativeEpoch::Current, spec)?; state.build_epoch_cache(RelativeEpoch::Next, spec)?; - let attesters = calculate_attester_sets(&state, spec)?; - let active_validator_indices = calculate_active_validator_indices(&state, spec); let current_total_balance = state.get_total_balance(&active_validator_indices[..], spec); @@ -46,14 +44,16 @@ pub fn per_epoch_processing(state: &mut BeaconState, spec: &ChainSpec) -> Result spec, ); + let attesters = calculate_attester_sets(&state, &active_validator_indices, spec)?; + process_eth1_data(state, spec); process_justification( state, current_total_balance, previous_total_balance, - attesters.previous_epoch_boundary.balance, - attesters.current_epoch_boundary.balance, + attesters.balances.previous_epoch_boundary, + attesters.balances.current_epoch_boundary, spec, ); @@ -63,7 +63,6 @@ pub fn per_epoch_processing(state: &mut BeaconState, spec: &ChainSpec) -> Result // Rewards and Penalities process_rewards_and_penalities( state, - &active_validator_indices, &attesters, previous_total_balance, &winning_root_for_shards, @@ -107,9 +106,13 @@ pub fn calculate_active_validator_indices(state: &BeaconState, spec: &ChainSpec) /// Spec v0.4.0 pub fn calculate_attester_sets( state: &BeaconState, + active_validator_indices: &[usize], spec: &ChainSpec, -) -> Result { - AttesterSets::new(&state, spec) +) -> Result { + let mut attesters = Attesters::empty(state.validator_registry.len()); + attesters.process_active_validator_indices(&active_validator_indices); + attesters.process_attestations(&state, &state.latest_attestations, spec)?; + Ok(attesters) } /// Spec v0.4.0 @@ -283,22 +286,20 @@ pub fn process_crosslinks( /// Spec v0.4.0 pub fn process_rewards_and_penalities( state: &mut BeaconState, - active_validator_indices: &[usize], - attesters: &AttesterSets, + attesters: &Attesters, previous_total_balance: u64, winning_root_for_shards: &WinningRootHashSet, spec: &ChainSpec, ) -> Result<(), Error> { let next_epoch = state.next_epoch(spec); - let active_validator_indices: FnvHashSet = - FnvHashSet::from_iter(active_validator_indices.iter().cloned()); - + /* let previous_epoch_attestations: Vec<&PendingAttestation> = state .latest_attestations .par_iter() .filter(|a| a.data.slot.epoch(spec.slots_per_epoch) == state.previous_epoch(spec)) .collect(); + */ let base_reward_quotient = previous_total_balance.integer_sqrt() / spec.base_reward_quotient; @@ -309,6 +310,7 @@ pub fn process_rewards_and_penalities( return Err(Error::PreviousTotalBalanceIsZero); } + /* // Map is ValidatorIndex -> ProposerIndex let mut inclusion_slots: FnvHashMap = FnvHashMap::default(); for a in &previous_epoch_attestations { @@ -330,79 +332,55 @@ pub fn process_rewards_and_penalities( ); } } + */ // Justification and finalization let epochs_since_finality = next_epoch - state.finalized_epoch; - if epochs_since_finality <= 4 { - state.validator_balances = state - .validator_balances - .par_iter() - .enumerate() - .map(|(index, &balance)| { - let mut balance = balance; + state.validator_balances = state + .validator_balances + .par_iter() + .enumerate() + .map(|(index, &balance)| { + let mut balance = balance; + let status = &attesters.statuses[index]; + + if epochs_since_finality <= 4 { let base_reward = state.base_reward(index, base_reward_quotient, spec); // Expected FFG source - if attesters.previous_epoch.indices.contains(&index) { + if status.is_previous_epoch { safe_add_assign!( balance, - base_reward * attesters.previous_epoch.balance / previous_total_balance + base_reward * attesters.balances.previous_epoch / previous_total_balance ); - } else if active_validator_indices.contains(&index) { + } else if status.is_active { safe_sub_assign!(balance, base_reward); } // Expected FFG target - if attesters.previous_epoch_boundary.indices.contains(&index) { + if status.is_previous_epoch_boundary { safe_add_assign!( balance, - base_reward * attesters.previous_epoch_boundary.balance + base_reward * attesters.balances.previous_epoch_boundary / previous_total_balance ); - } else if active_validator_indices.contains(&index) { + } else if status.is_active { safe_sub_assign!(balance, base_reward); } // Expected beacon chain head - if attesters.previous_epoch_head.indices.contains(&index) { + if status.is_previous_epoch_head { safe_add_assign!( balance, - base_reward * attesters.previous_epoch_head.balance + base_reward * attesters.balances.previous_epoch_head / previous_total_balance ); - } else if active_validator_indices.contains(&index) { + } else if status.is_active { safe_sub_assign!(balance, base_reward); }; - - if attesters.previous_epoch.indices.contains(&index) { - let base_reward = state.base_reward(index, base_reward_quotient, spec); - - let (inclusion_distance, _) = inclusion_slots - .get(&index) - .expect("Inconsistent inclusion_slots."); - - if *inclusion_distance > 0 { - safe_add_assign!( - balance, - base_reward * spec.min_attestation_inclusion_delay - / inclusion_distance.as_u64() - ) - } - } - - balance - }) - .collect(); - } else { - state.validator_balances = state - .validator_balances - .par_iter() - .enumerate() - .map(|(index, &balance)| { - let mut balance = balance; - + } else { let inactivity_penalty = state.inactivity_penalty( index, epochs_since_finality, @@ -410,14 +388,14 @@ pub fn process_rewards_and_penalities( spec, ); - if active_validator_indices.contains(&index) { - if !attesters.previous_epoch.indices.contains(&index) { + if status.is_active { + if !status.is_previous_epoch { safe_sub_assign!(balance, inactivity_penalty); } - if !attesters.previous_epoch_boundary.indices.contains(&index) { + if !status.is_previous_epoch_boundary { safe_sub_assign!(balance, inactivity_penalty); } - if !attesters.previous_epoch_head.indices.contains(&index) { + if !status.is_previous_epoch_head { safe_sub_assign!(balance, inactivity_penalty); } @@ -426,42 +404,31 @@ pub fn process_rewards_and_penalities( safe_sub_assign!(balance, 2 * inactivity_penalty + base_reward); } } + } - if attesters.previous_epoch.indices.contains(&index) { - let base_reward = state.base_reward(index, base_reward_quotient, spec); - - let (inclusion_distance, _) = inclusion_slots - .get(&index) - .expect("Inconsistent inclusion_slots."); - - if *inclusion_distance > 0 { - safe_add_assign!( - balance, - base_reward * spec.min_attestation_inclusion_delay - / inclusion_distance.as_u64() - ) - } - } - - balance - }) - .collect(); - } + balance + }) + .collect(); // Attestation inclusion - // - for &index in &attesters.previous_epoch.indices { - let (_, proposer_index) = inclusion_slots - .get(&index) - .ok_or_else(|| Error::InclusionSlotsInconsistent(index))?; + for (index, _validator) in state.validator_registry.iter().enumerate() { + let status = &attesters.statuses[index]; - let base_reward = state.base_reward(*proposer_index, base_reward_quotient, spec); + if status.is_previous_epoch { + let proposer_index = status.inclusion_info.proposer_index; + let inclusion_distance = status.inclusion_info.distance; - safe_add_assign!( - state.validator_balances[*proposer_index], - base_reward / spec.attestation_inclusion_reward_quotient - ); + let base_reward = state.base_reward(proposer_index, base_reward_quotient, spec); + + if inclusion_distance > 0 && inclusion_distance < Slot::max_value() { + safe_add_assign!( + state.validator_balances[proposer_index], + base_reward * spec.min_attestation_inclusion_delay + / inclusion_distance.as_u64() + ) + } + } } //Crosslinks diff --git a/eth2/state_processing/src/per_epoch_processing/attesters.rs b/eth2/state_processing/src/per_epoch_processing/attesters.rs new file mode 100644 index 000000000..662ddceed --- /dev/null +++ b/eth2/state_processing/src/per_epoch_processing/attesters.rs @@ -0,0 +1,195 @@ +use types::*; + +macro_rules! set_self_if_other_is_true { + ($self_: ident, $other: ident, $var: ident) => { + $self_.$var = $other.$var & !$self_.$var; + }; +} + +#[derive(Clone)] +pub struct InclusionInfo { + pub slot: Slot, + pub distance: Slot, + pub proposer_index: usize, +} + +impl Default for InclusionInfo { + fn default() -> Self { + Self { + slot: Slot::max_value(), + distance: Slot::max_value(), + proposer_index: 0, + } + } +} + +impl InclusionInfo { + pub fn update(&mut self, other: &Self) { + if other.slot < self.slot { + self.slot = other.slot; + self.distance = other.distance; + self.proposer_index = other.proposer_index; + } + } +} + +#[derive(Default, Clone)] +pub struct AttesterStatus { + pub is_active: bool, + + pub is_current_epoch: bool, + pub is_current_epoch_boundary: bool, + pub is_previous_epoch: bool, + pub is_previous_epoch_boundary: bool, + pub is_previous_epoch_head: bool, + + pub inclusion_info: InclusionInfo, +} + +impl AttesterStatus { + pub fn update(&mut self, other: &Self) { + // Update all the bool fields, only updating `self` if `other` is true (never setting + // `self` to false). + set_self_if_other_is_true!(self, other, is_active); + set_self_if_other_is_true!(self, other, is_current_epoch); + set_self_if_other_is_true!(self, other, is_current_epoch_boundary); + set_self_if_other_is_true!(self, other, is_previous_epoch); + set_self_if_other_is_true!(self, other, is_previous_epoch_boundary); + set_self_if_other_is_true!(self, other, is_previous_epoch_head); + + self.inclusion_info.update(&other.inclusion_info); + } +} + +#[derive(Default, Clone)] +pub struct TotalBalances { + pub current_epoch: u64, + pub current_epoch_boundary: u64, + pub previous_epoch: u64, + pub previous_epoch_boundary: u64, + pub previous_epoch_head: u64, +} + +pub struct Attesters { + pub statuses: Vec, + pub balances: TotalBalances, +} + +impl Attesters { + pub fn empty(num_validators: usize) -> Self { + Self { + statuses: vec![AttesterStatus::default(); num_validators], + balances: TotalBalances::default(), + } + } + + pub fn process_active_validator_indices(&mut self, active_validator_indices: &[usize]) { + let status = AttesterStatus { + is_active: true, + ..AttesterStatus::default() + }; + + for &i in active_validator_indices { + self.statuses[i].update(&status); + } + } + + pub fn process_attestations( + &mut self, + state: &BeaconState, + attestations: &[PendingAttestation], + spec: &ChainSpec, + ) -> Result<(), BeaconStateError> { + for a in attestations { + let attesting_indices = + state.get_attestation_participants(&a.data, &a.aggregation_bitfield, spec)?; + let attesting_balance = state.get_total_balance(&attesting_indices, spec); + + let mut status = AttesterStatus::default(); + + // Profile this attestation, updating the total balances and generating an + // `AttesterStatus` object that applies to all participants in the attestation. + if is_from_epoch(a, state.current_epoch(spec), spec) { + self.balances.current_epoch += attesting_balance; + status.is_current_epoch = true; + + if has_common_epoch_boundary_root(a, state, state.current_epoch(spec), spec)? { + self.balances.current_epoch_boundary += attesting_balance; + status.is_current_epoch_boundary = true; + } + } else if is_from_epoch(a, state.previous_epoch(spec), spec) { + self.balances.previous_epoch += attesting_balance; + status.is_previous_epoch = true; + + // The inclusion slot and distance are only required for previous epoch attesters. + status.inclusion_info = InclusionInfo { + slot: a.inclusion_slot, + distance: inclusion_distance(a), + proposer_index: state.get_beacon_proposer_index(a.inclusion_slot, spec)?, + }; + + if has_common_epoch_boundary_root(a, state, state.previous_epoch(spec), spec)? { + self.balances.previous_epoch_boundary += attesting_balance; + status.is_previous_epoch_boundary = true; + } + + if has_common_beacon_block_root(a, state, spec)? { + self.balances.previous_epoch_head += attesting_balance; + status.is_previous_epoch_head = true; + } + } + + // Loop through the participating validator indices and update the status vec. + for validator_index in attesting_indices { + self.statuses[validator_index].update(&status); + } + } + + Ok(()) + } +} + +fn inclusion_distance(a: &PendingAttestation) -> Slot { + a.inclusion_slot - a.data.slot +} + +/// Returns `true` if some `PendingAttestation` is from the supplied `epoch`. +/// +/// Spec v0.4.0 +fn is_from_epoch(a: &PendingAttestation, epoch: Epoch, spec: &ChainSpec) -> bool { + a.data.slot.epoch(spec.slots_per_epoch) == epoch +} + +/// Returns `true` if a `PendingAttestation` and `BeaconState` share the same beacon block hash for +/// the first slot of the given epoch. +/// +/// Spec v0.4.0 +fn has_common_epoch_boundary_root( + a: &PendingAttestation, + state: &BeaconState, + epoch: Epoch, + spec: &ChainSpec, +) -> Result { + let slot = epoch.start_slot(spec.slots_per_epoch); + let state_boundary_root = *state + .get_block_root(slot, spec) + .ok_or_else(|| BeaconStateError::InsufficientBlockRoots)?; + + Ok(a.data.epoch_boundary_root == state_boundary_root) +} + +/// Returns `true` if a `PendingAttestation` and `BeaconState` share the same beacon block hash for +/// the current slot of the `PendingAttestation`. +/// +/// Spec v0.4.0 +fn has_common_beacon_block_root( + a: &PendingAttestation, + state: &BeaconState, + spec: &ChainSpec, +) -> Result { + let state_block_root = *state + .get_block_root(a.data.slot, spec) + .ok_or_else(|| BeaconStateError::InsufficientBlockRoots)?; + + Ok(a.data.beacon_block_root == state_block_root) +} From a319144835f89ccac00756879e158711a260d1b7 Mon Sep 17 00:00:00 2001 From: Paul Hauner Date: Thu, 14 Mar 2019 12:17:43 +1100 Subject: [PATCH 2/5] Improve crosslink reward processing --- .../benches/bench_epoch_processing.rs | 6 +- .../src/per_epoch_processing.rs | 105 +++--------------- .../src/per_epoch_processing/attesters.rs | 41 +++++++ 3 files changed, 60 insertions(+), 92 deletions(-) diff --git a/eth2/state_processing/benches/bench_epoch_processing.rs b/eth2/state_processing/benches/bench_epoch_processing.rs index 93c6c7ebd..ab4f61c00 100644 --- a/eth2/state_processing/benches/bench_epoch_processing.rs +++ b/eth2/state_processing/benches/bench_epoch_processing.rs @@ -227,11 +227,11 @@ fn bench_epoch_processing(c: &mut Criterion, state: &BeaconState, spec: &ChainSp &format!("{}/epoch_processing", desc), Benchmark::new("process_rewards_and_penalties", move |b| { b.iter_batched( - || state_clone.clone(), - |mut state| { + || (state_clone.clone(), attesters.clone()), + |(mut state, mut attesters)| { process_rewards_and_penalities( &mut state, - &attesters, + &mut attesters, previous_total_balance, &winning_root_for_shards, &spec_clone, diff --git a/eth2/state_processing/src/per_epoch_processing.rs b/eth2/state_processing/src/per_epoch_processing.rs index 4fe53dd6b..2377d7ded 100644 --- a/eth2/state_processing/src/per_epoch_processing.rs +++ b/eth2/state_processing/src/per_epoch_processing.rs @@ -1,11 +1,9 @@ use attesters::Attesters; use errors::EpochProcessingError as Error; -use fnv::FnvHashSet; use integer_sqrt::IntegerSquareRoot; use rayon::prelude::*; use ssz::TreeHash; use std::collections::HashMap; -use std::iter::FromIterator; use types::{validator_registry::get_active_validator_indices, *}; use winning_root::{winning_root, WinningRoot}; @@ -44,7 +42,7 @@ pub fn per_epoch_processing(state: &mut BeaconState, spec: &ChainSpec) -> Result spec, ); - let attesters = calculate_attester_sets(&state, &active_validator_indices, spec)?; + let mut attesters = calculate_attester_sets(&state, &active_validator_indices, spec)?; process_eth1_data(state, spec); @@ -63,7 +61,7 @@ pub fn per_epoch_processing(state: &mut BeaconState, spec: &ChainSpec) -> Result // Rewards and Penalities process_rewards_and_penalities( state, - &attesters, + &mut attesters, previous_total_balance, &winning_root_for_shards, spec, @@ -286,21 +284,13 @@ pub fn process_crosslinks( /// Spec v0.4.0 pub fn process_rewards_and_penalities( state: &mut BeaconState, - attesters: &Attesters, + attesters: &mut Attesters, previous_total_balance: u64, winning_root_for_shards: &WinningRootHashSet, spec: &ChainSpec, ) -> Result<(), Error> { let next_epoch = state.next_epoch(spec); - /* - let previous_epoch_attestations: Vec<&PendingAttestation> = state - .latest_attestations - .par_iter() - .filter(|a| a.data.slot.epoch(spec.slots_per_epoch) == state.previous_epoch(spec)) - .collect(); - */ - let base_reward_quotient = previous_total_balance.integer_sqrt() / spec.base_reward_quotient; if base_reward_quotient == 0 { @@ -310,29 +300,7 @@ pub fn process_rewards_and_penalities( return Err(Error::PreviousTotalBalanceIsZero); } - /* - // Map is ValidatorIndex -> ProposerIndex - let mut inclusion_slots: FnvHashMap = FnvHashMap::default(); - for a in &previous_epoch_attestations { - let participants = - state.get_attestation_participants(&a.data, &a.aggregation_bitfield, spec)?; - let inclusion_distance = (a.inclusion_slot - a.data.slot).as_u64(); - for participant in participants { - if let Some((existing_distance, _)) = inclusion_slots.get(&participant) { - if *existing_distance <= inclusion_distance { - continue; - } - } - let proposer_index = state - .get_beacon_proposer_index(a.data.slot, spec) - .map_err(|_| Error::UnableToDetermineProducer)?; - inclusion_slots.insert( - participant, - (Slot::from(inclusion_distance), proposer_index), - ); - } - } - */ + attesters.process_winning_roots(state, winning_root_for_shards, spec)?; // Justification and finalization @@ -345,10 +313,9 @@ pub fn process_rewards_and_penalities( .map(|(index, &balance)| { let mut balance = balance; let status = &attesters.statuses[index]; + let base_reward = state.base_reward(index, base_reward_quotient, spec); if epochs_since_finality <= 4 { - let base_reward = state.base_reward(index, base_reward_quotient, spec); - // Expected FFG source if status.is_previous_epoch { safe_add_assign!( @@ -406,6 +373,17 @@ pub fn process_rewards_and_penalities( } } + // Crosslinks + + if let Some(ref info) = status.winning_root_info { + safe_add_assign!( + balance, + base_reward * info.total_attesting_balance / info.total_committee_balance + ); + } else { + safe_sub_assign!(balance, base_reward); + } + balance }) .collect(); @@ -431,57 +409,6 @@ pub fn process_rewards_and_penalities( } } - //Crosslinks - - for slot in state.previous_epoch(spec).slot_iter(spec.slots_per_epoch) { - // Clone removes the borrow which becomes an issue when mutating `state.balances`. - let crosslink_committees_at_slot = - state.get_crosslink_committees_at_slot(slot, spec)?.clone(); - - for (crosslink_committee, shard) in crosslink_committees_at_slot { - let shard = shard as u64; - - // Note: I'm a little uncertain of the logic here -- I am waiting for spec v0.5.0 to - // clear it up. - // - // What happens here is: - // - // - If there was some crosslink root elected by the super-majority of this committee, - // then we reward all who voted for that root and penalize all that did not. - // - However, if there _was not_ some super-majority-voted crosslink root, then penalize - // all the validators. - // - // I'm not quite sure that the second case (no super-majority crosslink) is correct. - if let Some(winning_root) = winning_root_for_shards.get(&shard) { - // Hash set de-dedups and (hopefully) offers a speed improvement from faster - // lookups. - let attesting_validator_indices: FnvHashSet = - FnvHashSet::from_iter(winning_root.attesting_validator_indices.iter().cloned()); - - for &index in &crosslink_committee { - let base_reward = state.base_reward(index, base_reward_quotient, spec); - - let total_balance = state.get_total_balance(&crosslink_committee, spec); - - if attesting_validator_indices.contains(&index) { - safe_add_assign!( - state.validator_balances[index], - base_reward * winning_root.total_attesting_balance / total_balance - ); - } else { - safe_sub_assign!(state.validator_balances[index], base_reward); - } - } - } else { - for &index in &crosslink_committee { - let base_reward = state.base_reward(index, base_reward_quotient, spec); - - safe_sub_assign!(state.validator_balances[index], base_reward); - } - } - } - } - Ok(()) } diff --git a/eth2/state_processing/src/per_epoch_processing/attesters.rs b/eth2/state_processing/src/per_epoch_processing/attesters.rs index 662ddceed..ef26d338d 100644 --- a/eth2/state_processing/src/per_epoch_processing/attesters.rs +++ b/eth2/state_processing/src/per_epoch_processing/attesters.rs @@ -1,3 +1,4 @@ +use super::WinningRootHashSet; use types::*; macro_rules! set_self_if_other_is_true { @@ -6,6 +7,12 @@ macro_rules! set_self_if_other_is_true { }; } +#[derive(Default, Clone)] +pub struct WinningRootInfo { + pub total_committee_balance: u64, + pub total_attesting_balance: u64, +} + #[derive(Clone)] pub struct InclusionInfo { pub slot: Slot, @@ -44,6 +51,7 @@ pub struct AttesterStatus { pub is_previous_epoch_head: bool, pub inclusion_info: InclusionInfo, + pub winning_root_info: Option, } impl AttesterStatus { @@ -70,6 +78,7 @@ pub struct TotalBalances { pub previous_epoch_head: u64, } +#[derive(Clone)] pub struct Attesters { pub statuses: Vec, pub balances: TotalBalances, @@ -147,6 +156,38 @@ impl Attesters { Ok(()) } + + pub fn process_winning_roots( + &mut self, + state: &BeaconState, + winning_roots: &WinningRootHashSet, + spec: &ChainSpec, + ) -> Result<(), BeaconStateError> { + // Loop through each slot in the previous epoch. + for slot in state.previous_epoch(spec).slot_iter(spec.slots_per_epoch) { + let crosslink_committees_at_slot = + state.get_crosslink_committees_at_slot(slot, spec)?; + + // Loop through each committee in the slot. + for (crosslink_committee, shard) in crosslink_committees_at_slot { + // If there was some winning crosslink root for the committee's shard. + if let Some(winning_root) = winning_roots.get(&shard) { + let total_committee_balance = + state.get_total_balance(&crosslink_committee, spec); + for &validator_index in &winning_root.attesting_validator_indices { + // Take note of the balance information for the winning root, it will be + // used later to calculate rewards for that validator. + self.statuses[validator_index].winning_root_info = Some(WinningRootInfo { + total_committee_balance, + total_attesting_balance: winning_root.total_attesting_balance, + }) + } + } + } + } + + Ok(()) + } } fn inclusion_distance(a: &PendingAttestation) -> Slot { From 95599ddc66649511f5e75deddd03035d17c451f8 Mon Sep 17 00:00:00 2001 From: Paul Hauner Date: Thu, 14 Mar 2019 12:49:48 +1100 Subject: [PATCH 3/5] Update Attesters struct - Renames variables - Moves total balance calculation into struct --- .../benches/bench_epoch_processing.rs | 123 ++---------------- .../src/per_epoch_processing.rs | 67 ++++------ .../src/per_epoch_processing/attesters.rs | 90 +++++++------ 3 files changed, 88 insertions(+), 192 deletions(-) diff --git a/eth2/state_processing/benches/bench_epoch_processing.rs b/eth2/state_processing/benches/bench_epoch_processing.rs index ab4f61c00..d95f1c819 100644 --- a/eth2/state_processing/benches/bench_epoch_processing.rs +++ b/eth2/state_processing/benches/bench_epoch_processing.rs @@ -4,14 +4,13 @@ use ssz::TreeHash; use state_processing::{ per_epoch_processing, per_epoch_processing::{ - calculate_active_validator_indices, calculate_attester_sets, clean_attestations, - process_crosslinks, process_eth1_data, process_justification, - process_rewards_and_penalities, process_validator_registry, update_active_tree_index_roots, - update_latest_slashed_balances, + calculate_attester_sets, clean_attestations, process_crosslinks, process_eth1_data, + process_justification, process_rewards_and_penalities, process_validator_registry, + update_active_tree_index_roots, update_latest_slashed_balances, }, }; use types::test_utils::TestingBeaconStateBuilder; -use types::{validator_registry::get_active_validator_indices, *}; +use types::*; pub const BENCHING_SAMPLE_SIZE: usize = 10; pub const SMALL_BENCHING_SAMPLE_SIZE: usize = 10; @@ -73,64 +72,6 @@ pub fn bench_epoch_processing_n_validators(c: &mut Criterion, validator_count: u /// /// `desc` will be added to the title of each bench. fn bench_epoch_processing(c: &mut Criterion, state: &BeaconState, spec: &ChainSpec, desc: &str) { - let state_clone = state.clone(); - let spec_clone = spec.clone(); - c.bench( - &format!("{}/epoch_processing", desc), - Benchmark::new("calculate_active_validator_indices", move |b| { - b.iter_batched( - || state_clone.clone(), - |mut state| { - calculate_active_validator_indices(&mut state, &spec_clone); - state - }, - criterion::BatchSize::SmallInput, - ) - }) - .sample_size(BENCHING_SAMPLE_SIZE), - ); - - let state_clone = state.clone(); - let spec_clone = spec.clone(); - let active_validator_indices = calculate_active_validator_indices(&state, &spec); - c.bench( - &format!("{}/epoch_processing", desc), - Benchmark::new("calculate_current_total_balance", move |b| { - b.iter_batched( - || state_clone.clone(), - |state| { - state.get_total_balance(&active_validator_indices[..], &spec_clone); - state - }, - criterion::BatchSize::SmallInput, - ) - }) - .sample_size(BENCHING_SAMPLE_SIZE), - ); - - let state_clone = state.clone(); - let spec_clone = spec.clone(); - c.bench( - &format!("{}/epoch_processing", desc), - Benchmark::new("calculate_previous_total_balance", move |b| { - b.iter_batched( - || state_clone.clone(), - |state| { - state.get_total_balance( - &get_active_validator_indices( - &state.validator_registry, - state.previous_epoch(&spec_clone), - )[..], - &spec_clone, - ); - state - }, - criterion::BatchSize::SmallInput, - ) - }) - .sample_size(BENCHING_SAMPLE_SIZE), - ); - let state_clone = state.clone(); let spec_clone = spec.clone(); c.bench( @@ -150,15 +91,13 @@ fn bench_epoch_processing(c: &mut Criterion, state: &BeaconState, spec: &ChainSp let state_clone = state.clone(); let spec_clone = spec.clone(); - let active_validator_indices = calculate_active_validator_indices(&state, &spec); c.bench( &format!("{}/epoch_processing", desc), Benchmark::new("calculate_attester_sets", move |b| { b.iter_batched( || state_clone.clone(), |mut state| { - calculate_attester_sets(&mut state, &active_validator_indices, &spec_clone) - .unwrap(); + calculate_attester_sets(&mut state, &spec_clone).unwrap(); state }, criterion::BatchSize::SmallInput, @@ -169,14 +108,7 @@ fn bench_epoch_processing(c: &mut Criterion, state: &BeaconState, spec: &ChainSp let state_clone = state.clone(); let spec_clone = spec.clone(); - let previous_epoch = state.previous_epoch(&spec); - let active_validator_indices = calculate_active_validator_indices(&state, &spec); - let attesters = calculate_attester_sets(&state, &active_validator_indices, &spec).unwrap(); - let current_total_balance = state.get_total_balance(&active_validator_indices[..], &spec); - let previous_total_balance = state.get_total_balance( - &get_active_validator_indices(&state.validator_registry, previous_epoch)[..], - &spec, - ); + let attesters = calculate_attester_sets(&state, &spec).unwrap(); c.bench( &format!("{}/epoch_processing", desc), Benchmark::new("process_justification", move |b| { @@ -185,10 +117,10 @@ fn bench_epoch_processing(c: &mut Criterion, state: &BeaconState, spec: &ChainSp |mut state| { process_justification( &mut state, - current_total_balance, - previous_total_balance, - attesters.balances.previous_epoch_boundary, - attesters.balances.current_epoch_boundary, + attesters.balances.current_epoch_total, + attesters.balances.previous_epoch_total, + attesters.balances.previous_epoch_boundary_attesters, + attesters.balances.current_epoch_boundary_attesters, &spec_clone, ); state @@ -215,13 +147,7 @@ fn bench_epoch_processing(c: &mut Criterion, state: &BeaconState, spec: &ChainSp let mut state_clone = state.clone(); let spec_clone = spec.clone(); - let previous_epoch = state.previous_epoch(&spec); - let active_validator_indices = calculate_active_validator_indices(&state, &spec); - let attesters = calculate_attester_sets(&state, &active_validator_indices, &spec).unwrap(); - let previous_total_balance = state.get_total_balance( - &get_active_validator_indices(&state.validator_registry, previous_epoch)[..], - &spec, - ); + let attesters = calculate_attester_sets(&state, &spec).unwrap(); let winning_root_for_shards = process_crosslinks(&mut state_clone, &spec).unwrap(); c.bench( &format!("{}/epoch_processing", desc), @@ -232,7 +158,6 @@ fn bench_epoch_processing(c: &mut Criterion, state: &BeaconState, spec: &ChainSp process_rewards_and_penalities( &mut state, &mut attesters, - previous_total_balance, &winning_root_for_shards, &spec_clone, ) @@ -262,32 +187,8 @@ fn bench_epoch_processing(c: &mut Criterion, state: &BeaconState, spec: &ChainSp .sample_size(BENCHING_SAMPLE_SIZE), ); - let mut state_clone = state.clone(); + let state_clone = state.clone(); let spec_clone = spec.clone(); - let previous_epoch = state.previous_epoch(&spec); - let active_validator_indices = calculate_active_validator_indices(&state, &spec); - let attesters = calculate_attester_sets(&state, &active_validator_indices, &spec).unwrap(); - let current_total_balance = state.get_total_balance(&active_validator_indices[..], spec); - let previous_total_balance = state.get_total_balance( - &get_active_validator_indices(&state.validator_registry, previous_epoch)[..], - &spec, - ); - assert_eq!( - state_clone.finalized_epoch, state_clone.validator_registry_update_epoch, - "The last registry update should be at the last finalized epoch." - ); - process_justification( - &mut state_clone, - current_total_balance, - previous_total_balance, - attesters.balances.previous_epoch_boundary, - attesters.balances.current_epoch_boundary, - spec, - ); - assert!( - state_clone.finalized_epoch > state_clone.validator_registry_update_epoch, - "The state should have been finalized." - ); c.bench( &format!("{}/epoch_processing", desc), Benchmark::new("process_validator_registry", move |b| { diff --git a/eth2/state_processing/src/per_epoch_processing.rs b/eth2/state_processing/src/per_epoch_processing.rs index 2377d7ded..03135df66 100644 --- a/eth2/state_processing/src/per_epoch_processing.rs +++ b/eth2/state_processing/src/per_epoch_processing.rs @@ -26,32 +26,21 @@ pub type WinningRootHashSet = HashMap; /// /// Spec v0.4.0 pub fn per_epoch_processing(state: &mut BeaconState, spec: &ChainSpec) -> Result<(), Error> { - let previous_epoch = state.previous_epoch(spec); - // Ensure all of the caches are built. state.build_epoch_cache(RelativeEpoch::Previous, spec)?; state.build_epoch_cache(RelativeEpoch::Current, spec)?; state.build_epoch_cache(RelativeEpoch::Next, spec)?; - let active_validator_indices = calculate_active_validator_indices(&state, spec); - - let current_total_balance = state.get_total_balance(&active_validator_indices[..], spec); - - let previous_total_balance = state.get_total_balance( - &get_active_validator_indices(&state.validator_registry, previous_epoch)[..], - spec, - ); - - let mut attesters = calculate_attester_sets(&state, &active_validator_indices, spec)?; + let mut attesters = calculate_attester_sets(&state, spec)?; process_eth1_data(state, spec); process_justification( state, - current_total_balance, - previous_total_balance, - attesters.balances.previous_epoch_boundary, - attesters.balances.current_epoch_boundary, + attesters.balances.current_epoch_total, + attesters.balances.previous_epoch_total, + attesters.balances.previous_epoch_boundary_attesters, + attesters.balances.current_epoch_boundary_attesters, spec, ); @@ -59,13 +48,7 @@ pub fn per_epoch_processing(state: &mut BeaconState, spec: &ChainSpec) -> Result let winning_root_for_shards = process_crosslinks(state, spec)?; // Rewards and Penalities - process_rewards_and_penalities( - state, - &mut attesters, - previous_total_balance, - &winning_root_for_shards, - spec, - )?; + process_rewards_and_penalities(state, &mut attesters, &winning_root_for_shards, spec)?; // Ejections state.process_ejections(spec); @@ -104,12 +87,12 @@ pub fn calculate_active_validator_indices(state: &BeaconState, spec: &ChainSpec) /// Spec v0.4.0 pub fn calculate_attester_sets( state: &BeaconState, - active_validator_indices: &[usize], spec: &ChainSpec, ) -> Result { - let mut attesters = Attesters::empty(state.validator_registry.len()); - attesters.process_active_validator_indices(&active_validator_indices); + let mut attesters = Attesters::new(state, spec); + attesters.process_attestations(&state, &state.latest_attestations, spec)?; + Ok(attesters) } @@ -285,12 +268,13 @@ pub fn process_crosslinks( pub fn process_rewards_and_penalities( state: &mut BeaconState, attesters: &mut Attesters, - previous_total_balance: u64, winning_root_for_shards: &WinningRootHashSet, spec: &ChainSpec, ) -> Result<(), Error> { let next_epoch = state.next_epoch(spec); + let previous_total_balance = attesters.balances.previous_epoch_total; + let base_reward_quotient = previous_total_balance.integer_sqrt() / spec.base_reward_quotient; if base_reward_quotient == 0 { @@ -317,34 +301,35 @@ pub fn process_rewards_and_penalities( if epochs_since_finality <= 4 { // Expected FFG source - if status.is_previous_epoch { + if status.is_previous_epoch_attester { safe_add_assign!( balance, - base_reward * attesters.balances.previous_epoch / previous_total_balance + base_reward * attesters.balances.previous_epoch_attesters + / previous_total_balance ); - } else if status.is_active { + } else if status.is_active_in_previous_epoch { safe_sub_assign!(balance, base_reward); } // Expected FFG target - if status.is_previous_epoch_boundary { + if status.is_previous_epoch_boundary_attester { safe_add_assign!( balance, - base_reward * attesters.balances.previous_epoch_boundary + base_reward * attesters.balances.previous_epoch_boundary_attesters / previous_total_balance ); - } else if status.is_active { + } else if status.is_active_in_previous_epoch { safe_sub_assign!(balance, base_reward); } // Expected beacon chain head - if status.is_previous_epoch_head { + if status.is_previous_epoch_head_attester { safe_add_assign!( balance, - base_reward * attesters.balances.previous_epoch_head + base_reward * attesters.balances.previous_epoch_head_attesters / previous_total_balance ); - } else if status.is_active { + } else if status.is_active_in_previous_epoch { safe_sub_assign!(balance, base_reward); }; } else { @@ -355,14 +340,14 @@ pub fn process_rewards_and_penalities( spec, ); - if status.is_active { - if !status.is_previous_epoch { + if status.is_active_in_previous_epoch { + if !status.is_previous_epoch_attester { safe_sub_assign!(balance, inactivity_penalty); } - if !status.is_previous_epoch_boundary { + if !status.is_previous_epoch_boundary_attester { safe_sub_assign!(balance, inactivity_penalty); } - if !status.is_previous_epoch_head { + if !status.is_previous_epoch_head_attester { safe_sub_assign!(balance, inactivity_penalty); } @@ -393,7 +378,7 @@ pub fn process_rewards_and_penalities( for (index, _validator) in state.validator_registry.iter().enumerate() { let status = &attesters.statuses[index]; - if status.is_previous_epoch { + if status.is_previous_epoch_attester { let proposer_index = status.inclusion_info.proposer_index; let inclusion_distance = status.inclusion_info.distance; diff --git a/eth2/state_processing/src/per_epoch_processing/attesters.rs b/eth2/state_processing/src/per_epoch_processing/attesters.rs index ef26d338d..1ffbdf652 100644 --- a/eth2/state_processing/src/per_epoch_processing/attesters.rs +++ b/eth2/state_processing/src/per_epoch_processing/attesters.rs @@ -42,28 +42,31 @@ impl InclusionInfo { #[derive(Default, Clone)] pub struct AttesterStatus { - pub is_active: bool, + pub is_active_in_current_epoch: bool, + pub is_active_in_previous_epoch: bool, - pub is_current_epoch: bool, - pub is_current_epoch_boundary: bool, - pub is_previous_epoch: bool, - pub is_previous_epoch_boundary: bool, - pub is_previous_epoch_head: bool, + pub is_current_epoch_attester: bool, + pub is_current_epoch_boundary_attester: bool, + pub is_previous_epoch_attester: bool, + pub is_previous_epoch_boundary_attester: bool, + pub is_previous_epoch_head_attester: bool, pub inclusion_info: InclusionInfo, pub winning_root_info: Option, } impl AttesterStatus { + /// Note: does not update the winning root info. pub fn update(&mut self, other: &Self) { // Update all the bool fields, only updating `self` if `other` is true (never setting // `self` to false). - set_self_if_other_is_true!(self, other, is_active); - set_self_if_other_is_true!(self, other, is_current_epoch); - set_self_if_other_is_true!(self, other, is_current_epoch_boundary); - set_self_if_other_is_true!(self, other, is_previous_epoch); - set_self_if_other_is_true!(self, other, is_previous_epoch_boundary); - set_self_if_other_is_true!(self, other, is_previous_epoch_head); + set_self_if_other_is_true!(self, other, is_active_in_current_epoch); + set_self_if_other_is_true!(self, other, is_active_in_previous_epoch); + set_self_if_other_is_true!(self, other, is_current_epoch_attester); + set_self_if_other_is_true!(self, other, is_current_epoch_boundary_attester); + set_self_if_other_is_true!(self, other, is_previous_epoch_attester); + set_self_if_other_is_true!(self, other, is_previous_epoch_boundary_attester); + set_self_if_other_is_true!(self, other, is_previous_epoch_head_attester); self.inclusion_info.update(&other.inclusion_info); } @@ -71,11 +74,13 @@ impl AttesterStatus { #[derive(Default, Clone)] pub struct TotalBalances { - pub current_epoch: u64, - pub current_epoch_boundary: u64, - pub previous_epoch: u64, - pub previous_epoch_boundary: u64, - pub previous_epoch_head: u64, + pub current_epoch_total: u64, + pub previous_epoch_total: u64, + pub current_epoch_attesters: u64, + pub current_epoch_boundary_attesters: u64, + pub previous_epoch_attesters: u64, + pub previous_epoch_boundary_attesters: u64, + pub previous_epoch_head_attesters: u64, } #[derive(Clone)] @@ -85,22 +90,27 @@ pub struct Attesters { } impl Attesters { - pub fn empty(num_validators: usize) -> Self { - Self { - statuses: vec![AttesterStatus::default(); num_validators], - balances: TotalBalances::default(), - } - } + pub fn new(state: &BeaconState, spec: &ChainSpec) -> Self { + let mut statuses = Vec::with_capacity(state.validator_registry.len()); + let mut balances = TotalBalances::default(); - pub fn process_active_validator_indices(&mut self, active_validator_indices: &[usize]) { - let status = AttesterStatus { - is_active: true, - ..AttesterStatus::default() - }; + for (i, validator) in state.validator_registry.iter().enumerate() { + let mut status = AttesterStatus::default(); - for &i in active_validator_indices { - self.statuses[i].update(&status); + if validator.is_active_at(state.current_epoch(spec)) { + status.is_active_in_current_epoch = true; + balances.current_epoch_total += state.get_effective_balance(i, spec); + } + + if validator.is_active_at(state.previous_epoch(spec)) { + status.is_active_in_previous_epoch = true; + balances.previous_epoch_total += state.get_effective_balance(i, spec); + } + + statuses.push(status); } + + Self { statuses, balances } } pub fn process_attestations( @@ -119,16 +129,16 @@ impl Attesters { // Profile this attestation, updating the total balances and generating an // `AttesterStatus` object that applies to all participants in the attestation. if is_from_epoch(a, state.current_epoch(spec), spec) { - self.balances.current_epoch += attesting_balance; - status.is_current_epoch = true; + self.balances.current_epoch_attesters += attesting_balance; + status.is_current_epoch_attester = true; if has_common_epoch_boundary_root(a, state, state.current_epoch(spec), spec)? { - self.balances.current_epoch_boundary += attesting_balance; - status.is_current_epoch_boundary = true; + self.balances.current_epoch_boundary_attesters += attesting_balance; + status.is_current_epoch_boundary_attester = true; } } else if is_from_epoch(a, state.previous_epoch(spec), spec) { - self.balances.previous_epoch += attesting_balance; - status.is_previous_epoch = true; + self.balances.previous_epoch_attesters += attesting_balance; + status.is_previous_epoch_attester = true; // The inclusion slot and distance are only required for previous epoch attesters. status.inclusion_info = InclusionInfo { @@ -138,13 +148,13 @@ impl Attesters { }; if has_common_epoch_boundary_root(a, state, state.previous_epoch(spec), spec)? { - self.balances.previous_epoch_boundary += attesting_balance; - status.is_previous_epoch_boundary = true; + self.balances.previous_epoch_boundary_attesters += attesting_balance; + status.is_previous_epoch_boundary_attester = true; } if has_common_beacon_block_root(a, state, spec)? { - self.balances.previous_epoch_head += attesting_balance; - status.is_previous_epoch_head = true; + self.balances.previous_epoch_head_attesters += attesting_balance; + status.is_previous_epoch_head_attester = true; } } From 10aee6214c4658322353de9b92df4e8194503c76 Mon Sep 17 00:00:00 2001 From: Paul Hauner Date: Thu, 14 Mar 2019 14:59:00 +1100 Subject: [PATCH 4/5] Tidy per_epoch_processing Mainly renaming variables and files for readability. --- .../benches/bench_epoch_processing.rs | 19 ++---- .../src/per_epoch_processing.rs | 66 ++++++++----------- .../{attesters.rs => validator_statuses.rs} | 37 ++++++----- 3 files changed, 57 insertions(+), 65 deletions(-) rename eth2/state_processing/src/per_epoch_processing/{attesters.rs => validator_statuses.rs} (89%) diff --git a/eth2/state_processing/benches/bench_epoch_processing.rs b/eth2/state_processing/benches/bench_epoch_processing.rs index d95f1c819..49b4f4371 100644 --- a/eth2/state_processing/benches/bench_epoch_processing.rs +++ b/eth2/state_processing/benches/bench_epoch_processing.rs @@ -4,7 +4,7 @@ use ssz::TreeHash; use state_processing::{ per_epoch_processing, per_epoch_processing::{ - calculate_attester_sets, clean_attestations, process_crosslinks, process_eth1_data, + clean_attestations, initialize_validator_statuses, process_crosslinks, process_eth1_data, process_justification, process_rewards_and_penalities, process_validator_registry, update_active_tree_index_roots, update_latest_slashed_balances, }, @@ -93,11 +93,11 @@ fn bench_epoch_processing(c: &mut Criterion, state: &BeaconState, spec: &ChainSp let spec_clone = spec.clone(); c.bench( &format!("{}/epoch_processing", desc), - Benchmark::new("calculate_attester_sets", move |b| { + Benchmark::new("initialize_validator_statuses", move |b| { b.iter_batched( || state_clone.clone(), |mut state| { - calculate_attester_sets(&mut state, &spec_clone).unwrap(); + initialize_validator_statuses(&mut state, &spec_clone).unwrap(); state }, criterion::BatchSize::SmallInput, @@ -108,21 +108,14 @@ fn bench_epoch_processing(c: &mut Criterion, state: &BeaconState, spec: &ChainSp let state_clone = state.clone(); let spec_clone = spec.clone(); - let attesters = calculate_attester_sets(&state, &spec).unwrap(); + let attesters = initialize_validator_statuses(&state, &spec).unwrap(); c.bench( &format!("{}/epoch_processing", desc), Benchmark::new("process_justification", move |b| { b.iter_batched( || state_clone.clone(), |mut state| { - process_justification( - &mut state, - attesters.balances.current_epoch_total, - attesters.balances.previous_epoch_total, - attesters.balances.previous_epoch_boundary_attesters, - attesters.balances.current_epoch_boundary_attesters, - &spec_clone, - ); + process_justification(&mut state, &attesters.total_balances, &spec_clone); state }, criterion::BatchSize::SmallInput, @@ -147,7 +140,7 @@ fn bench_epoch_processing(c: &mut Criterion, state: &BeaconState, spec: &ChainSp let mut state_clone = state.clone(); let spec_clone = spec.clone(); - let attesters = calculate_attester_sets(&state, &spec).unwrap(); + let attesters = initialize_validator_statuses(&state, &spec).unwrap(); let winning_root_for_shards = process_crosslinks(&mut state_clone, &spec).unwrap(); c.bench( &format!("{}/epoch_processing", desc), diff --git a/eth2/state_processing/src/per_epoch_processing.rs b/eth2/state_processing/src/per_epoch_processing.rs index 03135df66..044d32eae 100644 --- a/eth2/state_processing/src/per_epoch_processing.rs +++ b/eth2/state_processing/src/per_epoch_processing.rs @@ -1,17 +1,17 @@ -use attesters::Attesters; use errors::EpochProcessingError as Error; use integer_sqrt::IntegerSquareRoot; use rayon::prelude::*; use ssz::TreeHash; use std::collections::HashMap; use types::{validator_registry::get_active_validator_indices, *}; +use validator_statuses::{TotalBalances, ValidatorStatuses}; use winning_root::{winning_root, WinningRoot}; pub mod attester_sets; -pub mod attesters; pub mod errors; pub mod inclusion_distance; pub mod tests; +pub mod validator_statuses; pub mod winning_root; /// Maps a shard to a winning root. @@ -31,24 +31,17 @@ pub fn per_epoch_processing(state: &mut BeaconState, spec: &ChainSpec) -> Result state.build_epoch_cache(RelativeEpoch::Current, spec)?; state.build_epoch_cache(RelativeEpoch::Next, spec)?; - let mut attesters = calculate_attester_sets(&state, spec)?; + let mut statuses = initialize_validator_statuses(&state, spec)?; process_eth1_data(state, spec); - process_justification( - state, - attesters.balances.current_epoch_total, - attesters.balances.previous_epoch_total, - attesters.balances.previous_epoch_boundary_attesters, - attesters.balances.current_epoch_boundary_attesters, - spec, - ); + process_justification(state, &statuses.total_balances, spec); // Crosslinks let winning_root_for_shards = process_crosslinks(state, spec)?; // Rewards and Penalities - process_rewards_and_penalities(state, &mut attesters, &winning_root_for_shards, spec)?; + process_rewards_and_penalities(state, &mut statuses, &winning_root_for_shards, spec)?; // Ejections state.process_ejections(spec); @@ -85,15 +78,15 @@ pub fn calculate_active_validator_indices(state: &BeaconState, spec: &ChainSpec) /// - etc. /// /// Spec v0.4.0 -pub fn calculate_attester_sets( +pub fn initialize_validator_statuses( state: &BeaconState, spec: &ChainSpec, -) -> Result { - let mut attesters = Attesters::new(state, spec); +) -> Result { + let mut statuses = ValidatorStatuses::new(state, spec); - attesters.process_attestations(&state, &state.latest_attestations, spec)?; + statuses.process_attestations(&state, &state.latest_attestations, spec)?; - Ok(attesters) + Ok(statuses) } /// Spec v0.4.0 @@ -121,10 +114,7 @@ pub fn process_eth1_data(state: &mut BeaconState, spec: &ChainSpec) { /// Spec v0.4.0 pub fn process_justification( state: &mut BeaconState, - current_total_balance: u64, - previous_total_balance: u64, - previous_epoch_boundary_attesting_balance: u64, - current_epoch_boundary_attesting_balance: u64, + total_balances: &TotalBalances, spec: &ChainSpec, ) { let previous_epoch = state.previous_epoch(spec); @@ -137,7 +127,8 @@ pub fn process_justification( // // - Set the 2nd bit of the bitfield. // - Set the previous epoch to be justified. - if (3 * previous_epoch_boundary_attesting_balance) >= (2 * previous_total_balance) { + if (3 * total_balances.previous_epoch_boundary_attesters) >= (2 * total_balances.previous_epoch) + { state.justification_bitfield |= 2; new_justified_epoch = previous_epoch; } @@ -145,7 +136,7 @@ pub fn process_justification( // // - Set the 1st bit of the bitfield. // - Set the current epoch to be justified. - if (3 * current_epoch_boundary_attesting_balance) >= (2 * current_total_balance) { + if (3 * total_balances.current_epoch_boundary_attesters) >= (2 * total_balances.current_epoch) { state.justification_bitfield |= 1; new_justified_epoch = current_epoch; } @@ -267,25 +258,26 @@ pub fn process_crosslinks( /// Spec v0.4.0 pub fn process_rewards_and_penalities( state: &mut BeaconState, - attesters: &mut Attesters, + statuses: &mut ValidatorStatuses, winning_root_for_shards: &WinningRootHashSet, spec: &ChainSpec, ) -> Result<(), Error> { let next_epoch = state.next_epoch(spec); - let previous_total_balance = attesters.balances.previous_epoch_total; + statuses.process_winning_roots(state, winning_root_for_shards, spec)?; - let base_reward_quotient = previous_total_balance.integer_sqrt() / spec.base_reward_quotient; + let total_balances = &statuses.total_balances; + + let base_reward_quotient = + total_balances.previous_epoch.integer_sqrt() / spec.base_reward_quotient; if base_reward_quotient == 0 { return Err(Error::BaseRewardQuotientIsZero); } - if previous_total_balance == 0 { + if total_balances.previous_epoch == 0 { return Err(Error::PreviousTotalBalanceIsZero); } - attesters.process_winning_roots(state, winning_root_for_shards, spec)?; - // Justification and finalization let epochs_since_finality = next_epoch - state.finalized_epoch; @@ -296,7 +288,7 @@ pub fn process_rewards_and_penalities( .enumerate() .map(|(index, &balance)| { let mut balance = balance; - let status = &attesters.statuses[index]; + let status = &statuses.get(index); let base_reward = state.base_reward(index, base_reward_quotient, spec); if epochs_since_finality <= 4 { @@ -304,8 +296,8 @@ pub fn process_rewards_and_penalities( if status.is_previous_epoch_attester { safe_add_assign!( balance, - base_reward * attesters.balances.previous_epoch_attesters - / previous_total_balance + base_reward * total_balances.previous_epoch_attesters + / total_balances.previous_epoch ); } else if status.is_active_in_previous_epoch { safe_sub_assign!(balance, base_reward); @@ -315,8 +307,8 @@ pub fn process_rewards_and_penalities( if status.is_previous_epoch_boundary_attester { safe_add_assign!( balance, - base_reward * attesters.balances.previous_epoch_boundary_attesters - / previous_total_balance + base_reward * total_balances.previous_epoch_boundary_attesters + / total_balances.previous_epoch ); } else if status.is_active_in_previous_epoch { safe_sub_assign!(balance, base_reward); @@ -326,8 +318,8 @@ pub fn process_rewards_and_penalities( if status.is_previous_epoch_head_attester { safe_add_assign!( balance, - base_reward * attesters.balances.previous_epoch_head_attesters - / previous_total_balance + base_reward * total_balances.previous_epoch_head_attesters + / total_balances.previous_epoch ); } else if status.is_active_in_previous_epoch { safe_sub_assign!(balance, base_reward); @@ -376,7 +368,7 @@ pub fn process_rewards_and_penalities( // Attestation inclusion for (index, _validator) in state.validator_registry.iter().enumerate() { - let status = &attesters.statuses[index]; + let status = &statuses.get(index); if status.is_previous_epoch_attester { let proposer_index = status.inclusion_info.proposer_index; diff --git a/eth2/state_processing/src/per_epoch_processing/attesters.rs b/eth2/state_processing/src/per_epoch_processing/validator_statuses.rs similarity index 89% rename from eth2/state_processing/src/per_epoch_processing/attesters.rs rename to eth2/state_processing/src/per_epoch_processing/validator_statuses.rs index 1ffbdf652..70eeaf82a 100644 --- a/eth2/state_processing/src/per_epoch_processing/attesters.rs +++ b/eth2/state_processing/src/per_epoch_processing/validator_statuses.rs @@ -74,8 +74,8 @@ impl AttesterStatus { #[derive(Default, Clone)] pub struct TotalBalances { - pub current_epoch_total: u64, - pub previous_epoch_total: u64, + pub current_epoch: u64, + pub previous_epoch: u64, pub current_epoch_attesters: u64, pub current_epoch_boundary_attesters: u64, pub previous_epoch_attesters: u64, @@ -84,33 +84,40 @@ pub struct TotalBalances { } #[derive(Clone)] -pub struct Attesters { - pub statuses: Vec, - pub balances: TotalBalances, +pub struct ValidatorStatuses { + statuses: Vec, + pub total_balances: TotalBalances, } -impl Attesters { +impl ValidatorStatuses { pub fn new(state: &BeaconState, spec: &ChainSpec) -> Self { let mut statuses = Vec::with_capacity(state.validator_registry.len()); - let mut balances = TotalBalances::default(); + let mut total_balances = TotalBalances::default(); for (i, validator) in state.validator_registry.iter().enumerate() { let mut status = AttesterStatus::default(); if validator.is_active_at(state.current_epoch(spec)) { status.is_active_in_current_epoch = true; - balances.current_epoch_total += state.get_effective_balance(i, spec); + total_balances.current_epoch += state.get_effective_balance(i, spec); } if validator.is_active_at(state.previous_epoch(spec)) { status.is_active_in_previous_epoch = true; - balances.previous_epoch_total += state.get_effective_balance(i, spec); + total_balances.previous_epoch += state.get_effective_balance(i, spec); } statuses.push(status); } - Self { statuses, balances } + Self { + statuses, + total_balances, + } + } + + pub fn get(&self, i: usize) -> &AttesterStatus { + &self.statuses[i] } pub fn process_attestations( @@ -129,15 +136,15 @@ impl Attesters { // Profile this attestation, updating the total balances and generating an // `AttesterStatus` object that applies to all participants in the attestation. if is_from_epoch(a, state.current_epoch(spec), spec) { - self.balances.current_epoch_attesters += attesting_balance; + self.total_balances.current_epoch_attesters += attesting_balance; status.is_current_epoch_attester = true; if has_common_epoch_boundary_root(a, state, state.current_epoch(spec), spec)? { - self.balances.current_epoch_boundary_attesters += attesting_balance; + self.total_balances.current_epoch_boundary_attesters += attesting_balance; status.is_current_epoch_boundary_attester = true; } } else if is_from_epoch(a, state.previous_epoch(spec), spec) { - self.balances.previous_epoch_attesters += attesting_balance; + self.total_balances.previous_epoch_attesters += attesting_balance; status.is_previous_epoch_attester = true; // The inclusion slot and distance are only required for previous epoch attesters. @@ -148,12 +155,12 @@ impl Attesters { }; if has_common_epoch_boundary_root(a, state, state.previous_epoch(spec), spec)? { - self.balances.previous_epoch_boundary_attesters += attesting_balance; + self.total_balances.previous_epoch_boundary_attesters += attesting_balance; status.is_previous_epoch_boundary_attester = true; } if has_common_beacon_block_root(a, state, spec)? { - self.balances.previous_epoch_head_attesters += attesting_balance; + self.total_balances.previous_epoch_head_attesters += attesting_balance; status.is_previous_epoch_head_attester = true; } } From 1c1c15a122e1c589649a06cb27b227b4d5321230 Mon Sep 17 00:00:00 2001 From: Paul Hauner Date: Thu, 14 Mar 2019 16:00:22 +1100 Subject: [PATCH 5/5] Tidy per epoch processing - Add comments to ValidatorStatuses - Add some checks to guard against a bad statuses list - Remove unused attester_sets.rs file. --- .../src/per_epoch_processing.rs | 16 ++- .../src/per_epoch_processing/attester_sets.rs | 133 ------------------ .../src/per_epoch_processing/errors.rs | 1 + .../validator_statuses.rs | 80 ++++++++++- 4 files changed, 87 insertions(+), 143 deletions(-) delete mode 100644 eth2/state_processing/src/per_epoch_processing/attester_sets.rs diff --git a/eth2/state_processing/src/per_epoch_processing.rs b/eth2/state_processing/src/per_epoch_processing.rs index 044d32eae..8c4b8e88b 100644 --- a/eth2/state_processing/src/per_epoch_processing.rs +++ b/eth2/state_processing/src/per_epoch_processing.rs @@ -7,7 +7,6 @@ use types::{validator_registry::get_active_validator_indices, *}; use validator_statuses::{TotalBalances, ValidatorStatuses}; use winning_root::{winning_root, WinningRoot}; -pub mod attester_sets; pub mod errors; pub mod inclusion_distance; pub mod tests; @@ -271,12 +270,18 @@ pub fn process_rewards_and_penalities( let base_reward_quotient = total_balances.previous_epoch.integer_sqrt() / spec.base_reward_quotient; + // Guard against a divide-by-zero during the validator balance update. if base_reward_quotient == 0 { return Err(Error::BaseRewardQuotientIsZero); } + // Guard against a divide-by-zero during the validator balance update. if total_balances.previous_epoch == 0 { return Err(Error::PreviousTotalBalanceIsZero); } + // Guard against an out-of-bounds during the validator balance update. + if statuses.statuses.len() != state.validator_balances.len() { + return Err(Error::ValidatorStatusesInconsistent); + } // Justification and finalization @@ -288,7 +293,7 @@ pub fn process_rewards_and_penalities( .enumerate() .map(|(index, &balance)| { let mut balance = balance; - let status = &statuses.get(index); + let status = &statuses.statuses[index]; let base_reward = state.base_reward(index, base_reward_quotient, spec); if epochs_since_finality <= 4 { @@ -367,8 +372,13 @@ pub fn process_rewards_and_penalities( // Attestation inclusion + // Guard against an out-of-bounds during the attester inclusion balance update. + if statuses.statuses.len() != state.validator_registry.len() { + return Err(Error::ValidatorStatusesInconsistent); + } + for (index, _validator) in state.validator_registry.iter().enumerate() { - let status = &statuses.get(index); + let status = &statuses.statuses[index]; if status.is_previous_epoch_attester { let proposer_index = status.inclusion_info.proposer_index; diff --git a/eth2/state_processing/src/per_epoch_processing/attester_sets.rs b/eth2/state_processing/src/per_epoch_processing/attester_sets.rs deleted file mode 100644 index 03f49c1d3..000000000 --- a/eth2/state_processing/src/per_epoch_processing/attester_sets.rs +++ /dev/null @@ -1,133 +0,0 @@ -use fnv::FnvHashSet; -use types::*; - -/// A set of validator indices, along with the total balance of all those attesters. -#[derive(Default)] -pub struct Attesters { - /// A set of validator indices. - pub indices: FnvHashSet, - /// The total balance of all validators in `self.indices`. - pub balance: u64, -} - -impl Attesters { - /// Add the given indices to the set, incrementing the sets balance by the provided balance. - fn add(&mut self, additional_indices: &[usize], additional_balance: u64) { - self.indices.reserve(additional_indices.len()); - for i in additional_indices { - self.indices.insert(*i); - } - self.balance = self.balance.saturating_add(additional_balance); - } -} - -/// A collection of `Attester` objects, representing set of attesters that are rewarded/penalized -/// during an epoch transition. -pub struct AttesterSets { - /// All validators who attested during the state's current epoch. - pub current_epoch: Attesters, - /// All validators who attested that the beacon block root of the first slot of the state's - /// current epoch is the same as the one stored in this state. - /// - /// In short validators who agreed with the state about the first slot of the current epoch. - pub current_epoch_boundary: Attesters, - /// All validators who attested during the state's previous epoch. - pub previous_epoch: Attesters, - /// All validators who attested that the beacon block root of the first slot of the state's - /// previous epoch is the same as the one stored in this state. - /// - /// In short, validators who agreed with the state about the first slot of the previous epoch. - pub previous_epoch_boundary: Attesters, - /// All validators who attested that the beacon block root at the pending attestation's slot is - /// the same as the one stored in this state. - /// - /// In short, validators who agreed with the state about the current beacon block root when - /// they attested. - pub previous_epoch_head: Attesters, -} - -impl AttesterSets { - /// Loop through all attestations in the state and instantiate a complete `AttesterSets` struct. - /// - /// Spec v0.4.0 - pub fn new(state: &BeaconState, spec: &ChainSpec) -> Result { - let mut current_epoch = Attesters::default(); - let mut current_epoch_boundary = Attesters::default(); - let mut previous_epoch = Attesters::default(); - let mut previous_epoch_boundary = Attesters::default(); - let mut previous_epoch_head = Attesters::default(); - - for a in &state.latest_attestations { - let attesting_indices = - state.get_attestation_participants(&a.data, &a.aggregation_bitfield, spec)?; - let attesting_balance = state.get_total_balance(&attesting_indices, spec); - - if is_from_epoch(a, state.current_epoch(spec), spec) { - current_epoch.add(&attesting_indices, attesting_balance); - - if has_common_epoch_boundary_root(a, state, state.current_epoch(spec), spec)? { - current_epoch_boundary.add(&attesting_indices, attesting_balance); - } - } else if is_from_epoch(a, state.previous_epoch(spec), spec) { - previous_epoch.add(&attesting_indices, attesting_balance); - - if has_common_epoch_boundary_root(a, state, state.previous_epoch(spec), spec)? { - previous_epoch_boundary.add(&attesting_indices, attesting_balance); - } - - if has_common_beacon_block_root(a, state, spec)? { - previous_epoch_head.add(&attesting_indices, attesting_balance); - } - } - } - - Ok(Self { - current_epoch, - current_epoch_boundary, - previous_epoch, - previous_epoch_boundary, - previous_epoch_head, - }) - } -} - -/// Returns `true` if some `PendingAttestation` is from the supplied `epoch`. -/// -/// Spec v0.4.0 -fn is_from_epoch(a: &PendingAttestation, epoch: Epoch, spec: &ChainSpec) -> bool { - a.data.slot.epoch(spec.slots_per_epoch) == epoch -} - -/// Returns `true` if a `PendingAttestation` and `BeaconState` share the same beacon block hash for -/// the first slot of the given epoch. -/// -/// Spec v0.4.0 -fn has_common_epoch_boundary_root( - a: &PendingAttestation, - state: &BeaconState, - epoch: Epoch, - spec: &ChainSpec, -) -> Result { - let slot = epoch.start_slot(spec.slots_per_epoch); - let state_boundary_root = *state - .get_block_root(slot, spec) - .ok_or_else(|| BeaconStateError::InsufficientBlockRoots)?; - - Ok(a.data.epoch_boundary_root == state_boundary_root) -} - -/// Returns `true` if a `PendingAttestation` and `BeaconState` share the same beacon block hash for -/// the current slot of the `PendingAttestation`. -/// -/// Spec v0.4.0 -fn has_common_beacon_block_root( - a: &PendingAttestation, - state: &BeaconState, - spec: &ChainSpec, -) -> Result { - let state_block_root = *state - .get_block_root(a.data.slot, spec) - .ok_or_else(|| BeaconStateError::InsufficientBlockRoots)?; - - Ok(a.data.beacon_block_root == state_block_root) -} diff --git a/eth2/state_processing/src/per_epoch_processing/errors.rs b/eth2/state_processing/src/per_epoch_processing/errors.rs index c60e00cae..94fc0cca5 100644 --- a/eth2/state_processing/src/per_epoch_processing/errors.rs +++ b/eth2/state_processing/src/per_epoch_processing/errors.rs @@ -8,6 +8,7 @@ pub enum EpochProcessingError { NoRandaoSeed, PreviousTotalBalanceIsZero, InclusionDistanceZero, + ValidatorStatusesInconsistent, /// Unable to get the inclusion distance for a validator that should have an inclusion /// distance. This indicates an internal inconsistency. /// diff --git a/eth2/state_processing/src/per_epoch_processing/validator_statuses.rs b/eth2/state_processing/src/per_epoch_processing/validator_statuses.rs index 70eeaf82a..f76900f3b 100644 --- a/eth2/state_processing/src/per_epoch_processing/validator_statuses.rs +++ b/eth2/state_processing/src/per_epoch_processing/validator_statuses.rs @@ -1,26 +1,40 @@ use super::WinningRootHashSet; use types::*; +/// Sets the boolean `var` on `self` to be true if it is true on `other`. Otherwise leaves `self` +/// as is. macro_rules! set_self_if_other_is_true { ($self_: ident, $other: ident, $var: ident) => { - $self_.$var = $other.$var & !$self_.$var; + if $other.$var { + $self_.$var = true; + } }; } +/// The information required to reward some validator for their participation in a "winning" +/// crosslink root. #[derive(Default, Clone)] pub struct WinningRootInfo { + /// The total balance of the crosslink committee. pub total_committee_balance: u64, + /// The total balance of the crosslink committee that attested for the "winning" root. pub total_attesting_balance: u64, } +/// The information required to reward a block producer for including an attestation in a block. #[derive(Clone)] pub struct InclusionInfo { + /// The earliest slot a validator had an attestation included in the previous epoch. pub slot: Slot, + /// The distance between the attestation slot and the slot that attestation was included in a + /// block. pub distance: Slot, + /// The index of the proposer at the slot where the attestation was included. pub proposer_index: usize, } impl Default for InclusionInfo { + /// Defaults to `slot` and `distance` at their maximum values and `proposer_index` at zero. fn default() -> Self { Self { slot: Slot::max_value(), @@ -31,6 +45,8 @@ impl Default for InclusionInfo { } impl InclusionInfo { + /// Tests if some `other` `InclusionInfo` has a lower inclusion slot than `self`. If so, + /// replaces `self` with `other`. pub fn update(&mut self, other: &Self) { if other.slot < self.slot { self.slot = other.slot; @@ -40,23 +56,43 @@ impl InclusionInfo { } } +/// Information required to reward some validator during the current and previous epoch. #[derive(Default, Clone)] pub struct AttesterStatus { + /// True if the validator was active in the state's _current_ epoch. pub is_active_in_current_epoch: bool, + /// True if the validator was active in the state's _previous_ epoch. pub is_active_in_previous_epoch: bool, + /// True if the validator had an attestation included in the _current_ epoch. pub is_current_epoch_attester: bool, + /// True if the validator's beacon block root attestation for the first slot of the _current_ + /// epoch matches the block root known to the state. pub is_current_epoch_boundary_attester: bool, + /// True if the validator had an attestation included in the _previous_ epoch. pub is_previous_epoch_attester: bool, + /// True if the validator's beacon block root attestation for the first slot of the _previous_ + /// epoch matches the block root known to the state. pub is_previous_epoch_boundary_attester: bool, + /// True if the validator's beacon block root attestation in the _previous_ epoch at the + /// attestation's slot (`attestation_data.slot`) matches the block root known to the state. pub is_previous_epoch_head_attester: bool, + /// Information used to reward the block producer of this validators earliest-included + /// attestation. pub inclusion_info: InclusionInfo, + /// Information used to reward/penalize the validator if they voted in the super-majority for + /// some shard block. pub winning_root_info: Option, } impl AttesterStatus { - /// Note: does not update the winning root info. + /// Accepts some `other` `AttesterStatus` and updates `self` if required. + /// + /// Will never set one of the `bool` fields to `false`, it will only set it to `true` if other + /// contains a `true` field. + /// + /// Note: does not update the winning root info, this is done manually. pub fn update(&mut self, other: &Self) { // Update all the bool fields, only updating `self` if `other` is true (never setting // `self` to false). @@ -72,24 +108,46 @@ impl AttesterStatus { } } +/// The total effective balances for different sets of validators during the previous and current +/// epochs. #[derive(Default, Clone)] pub struct TotalBalances { + /// The total effective balance of all active validators during the _current_ epoch. pub current_epoch: u64, + /// The total effective balance of all active validators during the _previous_ epoch. pub previous_epoch: u64, + /// The total effective balance of all validators who attested during the _current_ epoch. pub current_epoch_attesters: u64, + /// The total effective balance of all validators who attested during the _current_ epoch and + /// agreed with the state about the beacon block at the first slot of the _current_ epoch. pub current_epoch_boundary_attesters: u64, + /// The total effective balance of all validators who attested during the _previous_ epoch. pub previous_epoch_attesters: u64, + /// The total effective balance of all validators who attested during the _previous_ epoch and + /// agreed with the state about the beacon block at the first slot of the _previous_ epoch. pub previous_epoch_boundary_attesters: u64, + /// The total effective balance of all validators who attested during the _previous_ epoch and + /// agreed with the state about the beacon block at the time of attestation. pub previous_epoch_head_attesters: u64, } +/// Summarised information about validator participation in the _previous and _current_ epochs of +/// some `BeaconState`. #[derive(Clone)] pub struct ValidatorStatuses { - statuses: Vec, + /// Information about each individual validator from the state's validator registy. + pub statuses: Vec, + /// Summed balances for various sets of validators. pub total_balances: TotalBalances, } impl ValidatorStatuses { + /// Initializes a new instance, determining: + /// + /// - Active validators + /// - Total balances for the current and previous epochs. + /// + /// Spec v0.4.0 pub fn new(state: &BeaconState, spec: &ChainSpec) -> Self { let mut statuses = Vec::with_capacity(state.validator_registry.len()); let mut total_balances = TotalBalances::default(); @@ -116,10 +174,10 @@ impl ValidatorStatuses { } } - pub fn get(&self, i: usize) -> &AttesterStatus { - &self.statuses[i] - } - + /// Process some attestations from the given `state` updating the `statuses` and + /// `total_balances` fields. + /// + /// Spec v0.4.0 pub fn process_attestations( &mut self, state: &BeaconState, @@ -174,6 +232,10 @@ impl ValidatorStatuses { Ok(()) } + /// Update the `statuses` for each validator based upon whether or not they attested to the + /// "winning" shard block root for the previous epoch. + /// + /// Spec v0.4.0 pub fn process_winning_roots( &mut self, state: &BeaconState, @@ -207,6 +269,10 @@ impl ValidatorStatuses { } } +/// Returns the distance between when the attestation was created and when it was included in a +/// block. +/// +/// Spec v0.4.0 fn inclusion_distance(a: &PendingAttestation) -> Slot { a.inclusion_slot - a.data.slot }