Merge branch 'faster-rewards' into sane-case
This commit is contained in:
		
						commit
						ed49023b87
					
				| @ -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, | ||||
|         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, | ||||
|     }, | ||||
| }; | ||||
| 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( | ||||
| @ -152,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, | ||||
| @ -167,28 +108,14 @@ 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 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 = 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, | ||||
|                         current_total_balance, | ||||
|                         previous_total_balance, | ||||
|                         attesters.previous_epoch_boundary.balance, | ||||
|                         attesters.current_epoch_boundary.balance, | ||||
|                         &spec_clone, | ||||
|                     ); | ||||
|                     process_justification(&mut state, &attesters.total_balances, &spec_clone); | ||||
|                     state | ||||
|                 }, | ||||
|                 criterion::BatchSize::SmallInput, | ||||
| @ -213,25 +140,17 @@ 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 previous_total_balance = state.get_total_balance( | ||||
|         &get_active_validator_indices(&state.validator_registry, previous_epoch)[..], | ||||
|         &spec, | ||||
|     ); | ||||
|     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), | ||||
|         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, | ||||
|                         &active_validator_indices, | ||||
|                         &attesters, | ||||
|                         previous_total_balance, | ||||
|                         &mut attesters, | ||||
|                         &winning_root_for_shards, | ||||
|                         &spec_clone, | ||||
|                     ) | ||||
| @ -261,32 +180,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 attesters = calculate_attester_sets(&state, &spec).unwrap(); | ||||
|     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, | ||||
|     ); | ||||
|     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.previous_epoch_boundary.balance, | ||||
|         attesters.current_epoch_boundary.balance, | ||||
|         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| { | ||||
|  | ||||
| @ -15,8 +15,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); | ||||
|  | ||||
| @ -1,19 +1,16 @@ | ||||
| use attester_sets::AttesterSets; | ||||
| use errors::EpochProcessingError as Error; | ||||
| use fnv::FnvHashMap; | ||||
| 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 validator_statuses::{TotalBalances, ValidatorStatuses}; | ||||
| use winning_root::{winning_root, WinningRoot}; | ||||
| 
 | ||||
| pub mod attester_sets; | ||||
| 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.
 | ||||
| @ -28,47 +25,22 @@ pub type WinningRootHashSet = HashMap<u64, WinningRoot>; | ||||
| ///
 | ||||
| /// 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 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); | ||||
| 
 | ||||
|     let previous_total_balance = state.get_total_balance( | ||||
|         &get_active_validator_indices(&state.validator_registry, previous_epoch)[..], | ||||
|         spec, | ||||
|     ); | ||||
|     let mut statuses = initialize_validator_statuses(&state, 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, | ||||
|         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, | ||||
|         &active_validator_indices, | ||||
|         &attesters, | ||||
|         previous_total_balance, | ||||
|         &winning_root_for_shards, | ||||
|         spec, | ||||
|     )?; | ||||
|     process_rewards_and_penalities(state, &mut statuses, &winning_root_for_shards, spec)?; | ||||
| 
 | ||||
|     // Ejections
 | ||||
|     state.process_ejections(spec); | ||||
| @ -105,11 +77,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<AttesterSets, BeaconStateError> { | ||||
|     AttesterSets::new(&state, spec) | ||||
| ) -> Result<ValidatorStatuses, BeaconStateError> { | ||||
|     let mut statuses = ValidatorStatuses::new(state, spec); | ||||
| 
 | ||||
|     statuses.process_attestations(&state, &state.latest_attestations, spec)?; | ||||
| 
 | ||||
|     Ok(statuses) | ||||
| } | ||||
| 
 | ||||
| /// Spec v0.4.0
 | ||||
| @ -137,10 +113,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); | ||||
| @ -153,7 +126,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; | ||||
|     } | ||||
| @ -161,7 +135,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; | ||||
|     } | ||||
| @ -283,126 +257,79 @@ pub fn process_crosslinks( | ||||
| /// Spec v0.4.0
 | ||||
| pub fn process_rewards_and_penalities( | ||||
|     state: &mut BeaconState, | ||||
|     active_validator_indices: &[usize], | ||||
|     attesters: &AttesterSets, | ||||
|     previous_total_balance: u64, | ||||
|     statuses: &mut ValidatorStatuses, | ||||
|     winning_root_for_shards: &WinningRootHashSet, | ||||
|     spec: &ChainSpec, | ||||
| ) -> Result<(), Error> { | ||||
|     let next_epoch = state.next_epoch(spec); | ||||
| 
 | ||||
|     let active_validator_indices: FnvHashSet<usize> = | ||||
|         FnvHashSet::from_iter(active_validator_indices.iter().cloned()); | ||||
|     statuses.process_winning_roots(state, winning_root_for_shards, 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 total_balances = &statuses.total_balances; | ||||
| 
 | ||||
|     let base_reward_quotient = previous_total_balance.integer_sqrt() / spec.base_reward_quotient; | ||||
|     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); | ||||
|     } | ||||
|     if previous_total_balance == 0 { | ||||
|     // Guard against a divide-by-zero during the validator balance update.
 | ||||
|     if total_balances.previous_epoch == 0 { | ||||
|         return Err(Error::PreviousTotalBalanceIsZero); | ||||
|     } | ||||
| 
 | ||||
|     // Map is ValidatorIndex -> ProposerIndex
 | ||||
|     let mut inclusion_slots: FnvHashMap<usize, (Slot, usize)> = 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), | ||||
|             ); | ||||
|         } | ||||
|     // 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
 | ||||
| 
 | ||||
|     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; | ||||
|                 let base_reward = state.base_reward(index, base_reward_quotient, spec); | ||||
|     state.validator_balances = state | ||||
|         .validator_balances | ||||
|         .par_iter() | ||||
|         .enumerate() | ||||
|         .map(|(index, &balance)| { | ||||
|             let mut balance = balance; | ||||
|             let status = &statuses.statuses[index]; | ||||
|             let base_reward = state.base_reward(index, base_reward_quotient, spec); | ||||
| 
 | ||||
|             if epochs_since_finality <= 4 { | ||||
|                 // Expected FFG source
 | ||||
|                 if attesters.previous_epoch.indices.contains(&index) { | ||||
|                 if status.is_previous_epoch_attester { | ||||
|                     safe_add_assign!( | ||||
|                         balance, | ||||
|                         base_reward * attesters.previous_epoch.balance / previous_total_balance | ||||
|                         base_reward * total_balances.previous_epoch_attesters | ||||
|                             / total_balances.previous_epoch | ||||
|                     ); | ||||
|                 } else if active_validator_indices.contains(&index) { | ||||
|                 } else if status.is_active_in_previous_epoch { | ||||
|                     safe_sub_assign!(balance, base_reward); | ||||
|                 } | ||||
| 
 | ||||
|                 // Expected FFG target
 | ||||
|                 if attesters.previous_epoch_boundary.indices.contains(&index) { | ||||
|                 if status.is_previous_epoch_boundary_attester { | ||||
|                     safe_add_assign!( | ||||
|                         balance, | ||||
|                         base_reward * attesters.previous_epoch_boundary.balance | ||||
|                             / previous_total_balance | ||||
|                         base_reward * total_balances.previous_epoch_boundary_attesters | ||||
|                             / total_balances.previous_epoch | ||||
|                     ); | ||||
|                 } else if active_validator_indices.contains(&index) { | ||||
|                 } else if status.is_active_in_previous_epoch { | ||||
|                     safe_sub_assign!(balance, base_reward); | ||||
|                 } | ||||
| 
 | ||||
|                 // Expected beacon chain head
 | ||||
|                 if attesters.previous_epoch_head.indices.contains(&index) { | ||||
|                 if status.is_previous_epoch_head_attester { | ||||
|                     safe_add_assign!( | ||||
|                         balance, | ||||
|                         base_reward * attesters.previous_epoch_head.balance | ||||
|                             / previous_total_balance | ||||
|                         base_reward * total_balances.previous_epoch_head_attesters | ||||
|                             / total_balances.previous_epoch | ||||
|                     ); | ||||
|                 } else if active_validator_indices.contains(&index) { | ||||
|                 } else if status.is_active_in_previous_epoch { | ||||
|                     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 +337,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_in_previous_epoch { | ||||
|                     if !status.is_previous_epoch_attester { | ||||
|                         safe_sub_assign!(balance, inactivity_penalty); | ||||
|                     } | ||||
|                     if !attesters.previous_epoch_boundary.indices.contains(&index) { | ||||
|                     if !status.is_previous_epoch_boundary_attester { | ||||
|                         safe_sub_assign!(balance, inactivity_penalty); | ||||
|                     } | ||||
|                     if !attesters.previous_epoch_head.indices.contains(&index) { | ||||
|                     if !status.is_previous_epoch_head_attester { | ||||
|                         safe_sub_assign!(balance, inactivity_penalty); | ||||
|                     } | ||||
| 
 | ||||
| @ -426,91 +353,45 @@ 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); | ||||
|             // Crosslinks
 | ||||
| 
 | ||||
|                     let (inclusion_distance, _) = inclusion_slots | ||||
|                         .get(&index) | ||||
|                         .expect("Inconsistent inclusion_slots."); | ||||
|             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); | ||||
|             } | ||||
| 
 | ||||
|                     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))?; | ||||
| 
 | ||||
|         let base_reward = state.base_reward(*proposer_index, base_reward_quotient, spec); | ||||
| 
 | ||||
|         safe_add_assign!( | ||||
|             state.validator_balances[*proposer_index], | ||||
|             base_reward / spec.attestation_inclusion_reward_quotient | ||||
|         ); | ||||
|     // Guard against an out-of-bounds during the attester inclusion balance update.
 | ||||
|     if statuses.statuses.len() != state.validator_registry.len() { | ||||
|         return Err(Error::ValidatorStatusesInconsistent); | ||||
|     } | ||||
| 
 | ||||
|     //Crosslinks
 | ||||
|     for (index, _validator) in state.validator_registry.iter().enumerate() { | ||||
|         let status = &statuses.statuses[index]; | ||||
| 
 | ||||
|     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(); | ||||
|         if status.is_previous_epoch_attester { | ||||
|             let proposer_index = status.inclusion_info.proposer_index; | ||||
|             let inclusion_distance = status.inclusion_info.distance; | ||||
| 
 | ||||
|         for (crosslink_committee, shard) in crosslink_committees_at_slot { | ||||
|             let shard = shard as u64; | ||||
|             let base_reward = state.base_reward(proposer_index, base_reward_quotient, spec); | ||||
| 
 | ||||
|             // 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<usize> = | ||||
|                     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); | ||||
|                 } | ||||
|             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() | ||||
|                 ) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @ -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<usize>, | ||||
|     /// 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<Self, BeaconStateError> { | ||||
|         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<bool, BeaconStateError> { | ||||
|     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<bool, BeaconStateError> { | ||||
|     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) | ||||
| } | ||||
| @ -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.
 | ||||
|     ///
 | ||||
|  | ||||
| @ -0,0 +1,319 @@ | ||||
| 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) => { | ||||
|         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(), | ||||
|             distance: Slot::max_value(), | ||||
|             proposer_index: 0, | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 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; | ||||
|             self.distance = other.distance; | ||||
|             self.proposer_index = other.proposer_index; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /// 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<WinningRootInfo>, | ||||
| } | ||||
| 
 | ||||
| impl AttesterStatus { | ||||
|     /// 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).
 | ||||
|         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); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /// 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 { | ||||
|     /// Information about each individual validator from the state's validator registy.
 | ||||
|     pub statuses: Vec<AttesterStatus>, | ||||
|     /// 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(); | ||||
| 
 | ||||
|         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; | ||||
|                 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; | ||||
|                 total_balances.previous_epoch += state.get_effective_balance(i, spec); | ||||
|             } | ||||
| 
 | ||||
|             statuses.push(status); | ||||
|         } | ||||
| 
 | ||||
|         Self { | ||||
|             statuses, | ||||
|             total_balances, | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /// 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, | ||||
|         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.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.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.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.
 | ||||
|                 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.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.total_balances.previous_epoch_head_attesters += attesting_balance; | ||||
|                     status.is_previous_epoch_head_attester = 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(()) | ||||
|     } | ||||
| 
 | ||||
|     /// 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, | ||||
|         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(()) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /// 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 | ||||
| } | ||||
| 
 | ||||
| /// 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<bool, BeaconStateError> { | ||||
|     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<bool, BeaconStateError> { | ||||
|     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) | ||||
| } | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user