diff --git a/eth2/types/src/beacon_state.rs b/eth2/types/src/beacon_state.rs index a1dd8983c..2463f4701 100644 --- a/eth2/types/src/beacon_state.rs +++ b/eth2/types/src/beacon_state.rs @@ -537,8 +537,8 @@ impl BeaconState { let cache = self.cache(relative_epoch)?; let (committee_slot_index, committee_index) = cache - .shard_committee_index_map - .get(&attestation_data.shard) + .shard_committee_indices + .get(attestation_data.shard as usize) .ok_or_else(|| Error::ShardOutOfBounds)?; let (committee, shard) = &cache.committees[*committee_slot_index][*committee_index]; @@ -787,7 +787,6 @@ impl BeaconState { &self, slot: Slot, registry_change: bool, - spec: &ChainSpec, ) -> Result>, Error> { let (_committees_per_epoch, seed, shuffling_epoch, _shuffling_start_shard) = @@ -810,7 +809,6 @@ impl BeaconState { ) -> Result>, Error> { let active_validator_indices = get_active_validator_indices(&self.validator_registry, epoch); - if active_validator_indices.is_empty() { error!("get_shuffling: no validators."); return Err(Error::InsufficientValidators); @@ -912,22 +910,18 @@ impl BeaconState { } } - /// Return the list of ``(committee, shard)`` tuples for the ``slot``. + /// Return the ordered list of shards tuples for the `slot`. /// /// Note: There are two possible shufflings for crosslink committees for a /// `slot` in the next epoch: with and without a `registry_change` /// - /// Note: does not utilize the cache, `get_crosslink_committees_at_slot` is an equivalent - /// function which uses the cache. - /// /// Spec v0.4.0 - pub(crate) fn calculate_crosslink_committees_at_slot( + pub(crate) fn get_shards_for_slot( &self, slot: Slot, registry_change: bool, - shuffling: Vec>, spec: &ChainSpec, - ) -> Result, u64)>, Error> { + ) -> Result, Error> { let (committees_per_epoch, _seed, _shuffling_epoch, shuffling_start_shard) = self.get_committee_params_at_slot(slot, registry_change, spec)?; @@ -936,15 +930,12 @@ impl BeaconState { let slot_start_shard = (shuffling_start_shard + committees_per_slot * offset) % spec.shard_count; - let mut crosslinks_at_slot = vec![]; + let mut shards_at_slot = vec![]; for i in 0..committees_per_slot { - let tuple = ( - shuffling[(committees_per_slot * offset + i) as usize].clone(), - (slot_start_shard + i) % spec.shard_count, - ); - crosslinks_at_slot.push(tuple) + shards_at_slot.push((slot_start_shard + i) % spec.shard_count) } - Ok(crosslinks_at_slot) + + Ok(shards_at_slot) } /// Returns the `slot`, `shard` and `committee_index` for which a validator must produce an @@ -962,10 +953,10 @@ impl BeaconState { ) -> Result, Error> { let cache = self.cache(RelativeEpoch::Current)?; - Ok(cache - .attestation_duty_map - .get(&(validator_index as u64)) - .and_then(|tuple| Some(*tuple))) + Ok(*cache + .attestation_duties + .get(validator_index) + .ok_or_else(|| Error::UnknownValidator)?) } /// Process the slashings. diff --git a/eth2/types/src/beacon_state/epoch_cache.rs b/eth2/types/src/beacon_state/epoch_cache.rs index e6bacd351..f9bc0d2e7 100644 --- a/eth2/types/src/beacon_state/epoch_cache.rs +++ b/eth2/types/src/beacon_state/epoch_cache.rs @@ -1,8 +1,6 @@ -use super::{AttestationDutyMap, BeaconState, CrosslinkCommittees, Error, ShardCommitteeIndexMap}; +use super::{AttestationDuty, BeaconState, CrosslinkCommittees, Error}; use crate::{ChainSpec, Epoch}; -use log::trace; use serde_derive::Serialize; -use std::collections::HashMap; #[derive(Debug, PartialEq, Clone, Serialize)] pub struct EpochCache { @@ -11,21 +9,23 @@ pub struct EpochCache { /// The crosslink committees for an epoch. pub committees: Vec, /// Maps validator index to a slot, shard and committee index for attestation. - pub attestation_duty_map: AttestationDutyMap, + pub attestation_duties: Vec>, /// Maps a shard to an index of `self.committees`. - pub shard_committee_index_map: ShardCommitteeIndexMap, + pub shard_committee_indices: Vec<(usize, usize)>, } impl EpochCache { + /// Return a new, completely empty cache. pub fn empty() -> EpochCache { EpochCache { initialized: false, committees: vec![], - attestation_duty_map: AttestationDutyMap::new(), - shard_committee_index_map: ShardCommitteeIndexMap::new(), + attestation_duties: vec![], + shard_committee_indices: vec![], } } + /// Return a new, fully initialized cache. pub fn initialized( state: &BeaconState, epoch: Epoch, @@ -33,42 +33,36 @@ impl EpochCache { ) -> Result { let mut epoch_committees: Vec = Vec::with_capacity(spec.slots_per_epoch as usize); - let mut shard_committee_index_map: ShardCommitteeIndexMap = HashMap::new(); - let shuffling = + let mut attestation_duties = vec![None; state.validator_registry.len()]; + + let mut shard_committee_indices = vec![(0, 0); spec.shard_count as usize]; + + let mut shuffling = state.get_shuffling_for_slot(epoch.start_slot(spec.slots_per_epoch), false, spec)?; - let mut attestation_duty_map: AttestationDutyMap = HashMap::with_capacity(shuffling.len()); + for (epoch_committees_index, slot) in epoch.slot_iter(spec.slots_per_epoch).enumerate() { + let mut slot_committees: Vec<(Vec, u64)> = vec![]; - for (epoch_committeess_index, slot) in epoch.slot_iter(spec.slots_per_epoch).enumerate() { - let slot_committees = state.calculate_crosslink_committees_at_slot( - slot, - false, - shuffling.clone(), - spec, - )?; + let shards = state.get_shards_for_slot(slot, false, spec)?; + for shard in shards { + let committee = shuffling.remove(0); + slot_committees.push((committee, shard)); + } for (slot_committees_index, (committee, shard)) in slot_committees.iter().enumerate() { - // Empty committees are not permitted. if committee.is_empty() { return Err(Error::InsufficientValidators); } - trace!( - "shard: {}, epoch_i: {}, slot_i: {}", - shard, - epoch_committeess_index, - slot_committees_index - ); - - shard_committee_index_map - .insert(*shard, (epoch_committeess_index, slot_committees_index)); + // Store the slot and committee index for this shard. + shard_committee_indices[*shard as usize] = + (epoch_committees_index, slot_committees_index); + // For each validator, store their attestation duties. for (committee_index, validator_index) in committee.iter().enumerate() { - attestation_duty_map.insert( - *validator_index as u64, - (slot, *shard, committee_index as u64), - ); + attestation_duties[*validator_index] = + Some((slot, *shard, committee_index as u64)) } } @@ -78,8 +72,8 @@ impl EpochCache { Ok(EpochCache { initialized: true, committees: epoch_committees, - attestation_duty_map, - shard_committee_index_map, + attestation_duties, + shard_committee_indices, }) } } diff --git a/eth2/types/src/validator_registry.rs b/eth2/types/src/validator_registry.rs index 7b55e78cb..db35ae993 100644 --- a/eth2/types/src/validator_registry.rs +++ b/eth2/types/src/validator_registry.rs @@ -7,17 +7,17 @@ use crate::Epoch; /// /// Spec v0.4.0 pub fn get_active_validator_indices(validators: &[Validator], epoch: Epoch) -> Vec { - validators - .iter() - .enumerate() - .filter_map(|(index, validator)| { - if validator.is_active_at(epoch) { - Some(index) - } else { - None - } - }) - .collect::>() + let mut active = Vec::with_capacity(validators.len()); + + for (index, validator) in validators.iter().enumerate() { + if validator.is_active_at(epoch) { + active.push(index) + } + } + + active.shrink_to_fit(); + + active } #[cfg(test)]