Add progress on epoch caching

This commit is contained in:
Paul Hauner 2019-05-16 16:54:11 +10:00
parent 0885d56b36
commit 944ac73ef9
No known key found for this signature in database
GPG Key ID: 5E2CFF9B75FA63DF
18 changed files with 230 additions and 250 deletions

View File

@ -23,7 +23,7 @@ mod exit_cache;
mod pubkey_cache; mod pubkey_cache;
mod tests; mod tests;
pub const CACHED_EPOCHS: usize = 4; pub const CACHED_EPOCHS: usize = 3;
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub enum Error { pub enum Error {
@ -49,6 +49,7 @@ pub enum Error {
}, },
PreviousEpochCacheUninitialized, PreviousEpochCacheUninitialized,
CurrentEpochCacheUnintialized, CurrentEpochCacheUnintialized,
EpochCacheUnintialized(RelativeEpoch),
EpochCacheError(EpochCacheError), EpochCacheError(EpochCacheError),
TreeHashCacheError(TreeHashCacheError), TreeHashCacheError(TreeHashCacheError),
} }
@ -117,13 +118,7 @@ where
#[ssz(skip_deserializing)] #[ssz(skip_deserializing)]
#[tree_hash(skip_hashing)] #[tree_hash(skip_hashing)]
#[test_random(default)] #[test_random(default)]
pub previous_epoch_cache: EpochCache, pub epoch_caches: [EpochCache; CACHED_EPOCHS],
#[serde(default)]
#[ssz(skip_serializing)]
#[ssz(skip_deserializing)]
#[tree_hash(skip_hashing)]
#[test_random(default)]
pub current_epoch_cache: EpochCache,
#[serde(default)] #[serde(default)]
#[ssz(skip_serializing)] #[ssz(skip_serializing)]
#[ssz(skip_deserializing)] #[ssz(skip_deserializing)]
@ -214,8 +209,11 @@ impl<T: EthSpec> BeaconState<T> {
/* /*
* Caching (not in spec) * Caching (not in spec)
*/ */
previous_epoch_cache: EpochCache::default(), epoch_caches: [
current_epoch_cache: EpochCache::default(), EpochCache::default(),
EpochCache::default(),
EpochCache::default(),
],
pubkey_cache: PubkeyCache::default(), pubkey_cache: PubkeyCache::default(),
tree_hash_cache: TreeHashCache::default(), tree_hash_cache: TreeHashCache::default(),
exit_cache: ExitCache::default(), exit_cache: ExitCache::default(),
@ -254,8 +252,8 @@ impl<T: EthSpec> BeaconState<T> {
/// The epoch corresponding to `self.slot`. /// The epoch corresponding to `self.slot`.
/// ///
/// Spec v0.6.1 /// Spec v0.6.1
pub fn current_epoch(&self, spec: &ChainSpec) -> Epoch { pub fn current_epoch(&self) -> Epoch {
self.slot.epoch(spec.slots_per_epoch) self.slot.epoch(T::slots_per_epoch())
} }
/// The epoch prior to `self.current_epoch()`. /// The epoch prior to `self.current_epoch()`.
@ -263,9 +261,9 @@ impl<T: EthSpec> BeaconState<T> {
/// If the current epoch is the genesis epoch, the genesis_epoch is returned. /// If the current epoch is the genesis epoch, the genesis_epoch is returned.
/// ///
/// Spec v0.6.1 /// Spec v0.6.1
pub fn previous_epoch(&self, spec: &ChainSpec) -> Epoch { pub fn previous_epoch(&self) -> Epoch {
let current_epoch = self.current_epoch(spec); let current_epoch = self.current_epoch();
if current_epoch > spec.genesis_epoch { if current_epoch > T::genesis_epoch() {
current_epoch - 1 current_epoch - 1
} else { } else {
current_epoch current_epoch
@ -275,8 +273,8 @@ impl<T: EthSpec> BeaconState<T> {
/// The epoch following `self.current_epoch()`. /// The epoch following `self.current_epoch()`.
/// ///
/// Spec v0.6.1 /// Spec v0.6.1
pub fn next_epoch(&self, spec: &ChainSpec) -> Epoch { pub fn next_epoch(&self) -> Epoch {
self.current_epoch(spec) + 1 self.current_epoch() + 1
} }
/// Return the number of committees at ``epoch``. /// Return the number of committees at ``epoch``.
@ -301,13 +299,13 @@ impl<T: EthSpec> BeaconState<T> {
/// ///
/// Spec v0.6.1 /// Spec v0.6.1
pub fn get_epoch_start_shard(&self, epoch: Epoch, spec: &ChainSpec) -> Result<u64, Error> { pub fn get_epoch_start_shard(&self, epoch: Epoch, spec: &ChainSpec) -> Result<u64, Error> {
if epoch > self.current_epoch(spec) + 1 { if epoch > self.current_epoch() + 1 {
return Err(Error::EpochOutOfBounds); return Err(Error::EpochOutOfBounds);
} }
let shard_count = T::ShardCount::to_u64(); let shard_count = T::ShardCount::to_u64();
let mut check_epoch = self.current_epoch(spec) + 1; let mut check_epoch = self.current_epoch() + 1;
let mut shard = (self.latest_start_shard let mut shard = (self.latest_start_shard
+ self.get_shard_delta(self.current_epoch(spec), spec)) + self.get_shard_delta(self.current_epoch(), spec))
% shard_count; % shard_count;
while check_epoch > epoch { while check_epoch > epoch {
check_epoch -= 1; check_epoch -= 1;
@ -458,8 +456,8 @@ impl<T: EthSpec> BeaconState<T> {
/// Safely obtains the index for `latest_randao_mixes` /// Safely obtains the index for `latest_randao_mixes`
/// ///
/// Spec v0.5.1 /// Spec v0.5.1
fn get_randao_mix_index(&self, epoch: Epoch, spec: &ChainSpec) -> Result<usize, Error> { fn get_randao_mix_index(&self, epoch: Epoch) -> Result<usize, Error> {
let current_epoch = self.current_epoch(spec); let current_epoch = self.current_epoch();
let len = T::LatestRandaoMixesLength::to_u64(); let len = T::LatestRandaoMixesLength::to_u64();
if (current_epoch - len < epoch) & (epoch <= current_epoch) { if (current_epoch - len < epoch) & (epoch <= current_epoch) {
@ -486,7 +484,7 @@ impl<T: EthSpec> BeaconState<T> {
let signature_hash = Hash256::from_slice(&hash(&ssz_encode(signature))); let signature_hash = Hash256::from_slice(&hash(&ssz_encode(signature)));
self.latest_randao_mixes[i] = *self.get_randao_mix(epoch, spec)? ^ signature_hash; self.latest_randao_mixes[i] = *self.get_randao_mix(epoch)? ^ signature_hash;
Ok(()) Ok(())
} }
@ -494,21 +492,16 @@ impl<T: EthSpec> BeaconState<T> {
/// Return the randao mix at a recent ``epoch``. /// Return the randao mix at a recent ``epoch``.
/// ///
/// Spec v0.5.1 /// Spec v0.5.1
pub fn get_randao_mix(&self, epoch: Epoch, spec: &ChainSpec) -> Result<&Hash256, Error> { pub fn get_randao_mix(&self, epoch: Epoch) -> Result<&Hash256, Error> {
let i = self.get_randao_mix_index(epoch, spec)?; let i = self.get_randao_mix_index(epoch)?;
Ok(&self.latest_randao_mixes[i]) Ok(&self.latest_randao_mixes[i])
} }
/// Set the randao mix at a recent ``epoch``. /// Set the randao mix at a recent ``epoch``.
/// ///
/// Spec v0.5.1 /// Spec v0.5.1
pub fn set_randao_mix( pub fn set_randao_mix(&mut self, epoch: Epoch, mix: Hash256) -> Result<(), Error> {
&mut self, let i = self.get_randao_mix_index(epoch)?;
epoch: Epoch,
mix: Hash256,
spec: &ChainSpec,
) -> Result<(), Error> {
let i = self.get_randao_mix_index(epoch, spec)?;
self.latest_randao_mixes[i] = mix; self.latest_randao_mixes[i] = mix;
Ok(()) Ok(())
} }
@ -517,7 +510,7 @@ impl<T: EthSpec> BeaconState<T> {
/// ///
/// Spec v0.6.1 /// Spec v0.6.1
fn get_active_index_root_index(&self, epoch: Epoch, spec: &ChainSpec) -> Result<usize, Error> { fn get_active_index_root_index(&self, epoch: Epoch, spec: &ChainSpec) -> Result<usize, Error> {
let current_epoch = self.current_epoch(spec); let current_epoch = self.current_epoch();
if current_epoch - self.latest_active_index_roots.len() as u64 + spec.activation_exit_delay if current_epoch - self.latest_active_index_roots.len() as u64 + spec.activation_exit_delay
< epoch < epoch
@ -627,9 +620,9 @@ impl<T: EthSpec> BeaconState<T> {
epoch: Epoch, epoch: Epoch,
spec: &ChainSpec, spec: &ChainSpec,
) -> Result<&[PendingAttestation], Error> { ) -> Result<&[PendingAttestation], Error> {
if epoch == self.current_epoch(spec) { if epoch == self.current_epoch() {
Ok(&self.current_epoch_attestations) Ok(&self.current_epoch_attestations)
} else if epoch == self.previous_epoch(spec) { } else if epoch == self.previous_epoch() {
Ok(&self.previous_epoch_attestations) Ok(&self.previous_epoch_attestations)
} else { } else {
Err(Error::EpochOutOfBounds) Err(Error::EpochOutOfBounds)
@ -659,7 +652,7 @@ impl<T: EthSpec> BeaconState<T> {
/// Spec v0.5.1 /// Spec v0.5.1
pub fn generate_seed(&self, epoch: Epoch, spec: &ChainSpec) -> Result<Hash256, Error> { pub fn generate_seed(&self, epoch: Epoch, spec: &ChainSpec) -> Result<Hash256, Error> {
let mut input = self let mut input = self
.get_randao_mix(epoch - spec.min_seed_lookahead, spec)? .get_randao_mix(epoch - spec.min_seed_lookahead)?
.as_bytes() .as_bytes()
.to_vec(); .to_vec();
@ -699,7 +692,7 @@ impl<T: EthSpec> BeaconState<T> {
pub fn get_churn_limit(&self, spec: &ChainSpec) -> Result<u64, Error> { pub fn get_churn_limit(&self, spec: &ChainSpec) -> Result<u64, Error> {
Ok(std::cmp::max( Ok(std::cmp::max(
spec.min_per_epoch_churn_limit, spec.min_per_epoch_churn_limit,
self.cache(RelativeEpoch::Current, spec)? self.cache(self.current_epoch(), spec)?
.active_validator_indices .active_validator_indices
.len() as u64 .len() as u64
/ spec.churn_limit_quotient, / spec.churn_limit_quotient,
@ -719,7 +712,7 @@ impl<T: EthSpec> BeaconState<T> {
validator_index: usize, validator_index: usize,
spec: &ChainSpec, spec: &ChainSpec,
) -> Result<&Option<AttestationDuty>, Error> { ) -> Result<&Option<AttestationDuty>, Error> {
let cache = self.cache(RelativeEpoch::Current, spec)?; let cache = self.cache(self.current_epoch(), spec)?;
Ok(cache Ok(cache
.attestation_duties .attestation_duties
@ -743,8 +736,9 @@ impl<T: EthSpec> BeaconState<T> {
/// Build all the caches, if they need to be built. /// Build all the caches, if they need to be built.
pub fn build_all_caches(&mut self, spec: &ChainSpec) -> Result<(), Error> { pub fn build_all_caches(&mut self, spec: &ChainSpec) -> Result<(), Error> {
self.build_previous_epoch_cache(spec)?; self.build_epoch_cache(RelativeEpoch::Previous, spec)?;
self.build_current_epoch_cache(spec)?; self.build_epoch_cache(RelativeEpoch::Current, spec)?;
self.build_epoch_cache(RelativeEpoch::Next, spec)?;
self.update_pubkey_cache()?; self.update_pubkey_cache()?;
self.update_tree_hash_cache()?; self.update_tree_hash_cache()?;
self.exit_cache self.exit_cache
@ -754,9 +748,14 @@ impl<T: EthSpec> BeaconState<T> {
} }
/// Build an epoch cache, unless it is has already been built. /// Build an epoch cache, unless it is has already been built.
pub fn build_previous_epoch_cache(&mut self, spec: &ChainSpec) -> Result<(), Error> { pub fn build_epoch_cache(
if self.caches[cache_index].initialized_epoch == Some(self.slot.epoch(spec.slots_per_epoch)) &mut self,
{ relative_epoch: RelativeEpoch,
spec: &ChainSpec,
) -> Result<(), Error> {
let i = Self::cache_index(relative_epoch);
if self.epoch_caches[i].is_initialized_at(self.previous_epoch()) {
Ok(()) Ok(())
} else { } else {
self.force_build_epoch_cache(relative_epoch, spec) self.force_build_epoch_cache(relative_epoch, spec)
@ -764,22 +763,14 @@ impl<T: EthSpec> BeaconState<T> {
} }
/// Always builds the previous epoch cache, even if it is already initialized. /// Always builds the previous epoch cache, even if it is already initialized.
pub fn force_build_previous_epoch_cache(&mut self, spec: &ChainSpec) -> Result<(), Error> { pub fn force_build_epoch_cache(
let epoch = self.previous_epoch(spec); &mut self,
self.previous_epoch_cache = EpochCache::initialized( relative_epoch: RelativeEpoch,
&self, spec: &ChainSpec,
epoch, ) -> Result<(), Error> {
self.generate_seed(epoch, spec)?, let epoch = relative_epoch.into_epoch(self.current_epoch());
self.get_epoch_start_shard(epoch, spec)?,
spec,
)?;
Ok(())
}
/// Always builds the current epoch cache, even if it is already initialized. self.epoch_caches[Self::cache_index(relative_epoch)] = EpochCache::initialized(
pub fn force_build_current_epoch_cache(&mut self, spec: &ChainSpec) -> Result<(), Error> {
let epoch = self.current_epoch(spec);
self.current_epoch_cache = EpochCache::initialized(
&self, &self,
epoch, epoch,
self.generate_seed(epoch, spec)?, self.generate_seed(epoch, spec)?,
@ -792,10 +783,39 @@ impl<T: EthSpec> BeaconState<T> {
/// Advances the cache for this state into the next epoch. /// Advances the cache for this state into the next epoch.
/// ///
/// This should be used if the `slot` of this state is advanced beyond an epoch boundary. /// This should be used if the `slot` of this state is advanced beyond an epoch boundary.
pub fn advance_caches(&mut self) { ///
self.previous_epoch_cache = /// Note: whilst this function will preserve already-built caches, it will not build any.
std::mem::replace(&mut self.current_epoch_cache, EpochCache::default()); pub fn advance_caches(&mut self, spec: &ChainSpec) {
self.force_build_current_epoch_cache(); let previous = Self::cache_index(RelativeEpoch::Previous);
let current = Self::cache_index(RelativeEpoch::Previous);
let next = Self::cache_index(RelativeEpoch::Previous);
let caches = &mut self.epoch_caches[..];
caches.rotate_left(1);
caches[next] = EpochCache::default();
}
fn cache_index(relative_epoch: RelativeEpoch) -> usize {
match relative_epoch {
RelativeEpoch::Previous => 0,
RelativeEpoch::Current => 1,
RelativeEpoch::Next => 2,
}
}
/// Returns the cache for some `RelativeEpoch`. Returns an error if the cache has not been
/// initialized.
fn cache(&self, epoch: Epoch, spec: &ChainSpec) -> Result<&EpochCache, Error> {
let relative_epoch = RelativeEpoch::from_epoch(self.current_epoch(), epoch)
.map_err(|e| Error::EpochOutOfBounds)?;
let cache = &self.epoch_caches[Self::cache_index(relative_epoch)];
if cache.is_initialized_at(epoch) {
Ok(cache)
} else {
Err(Error::EpochCacheUnintialized(relative_epoch))
}
} }
// FIXME(sproul): drop_previous/current_epoch_cache // FIXME(sproul): drop_previous/current_epoch_cache

View File

@ -14,6 +14,20 @@ pub trait EthSpec:
fn spec() -> ChainSpec; fn spec() -> ChainSpec;
/// Returns the `SLOTS_PER_EPOCH` constant for this specification.
///
/// Spec v0.6.1
fn slots_per_epoch() -> u64 {
Self::spec().slots_per_epoch
}
/// Returns the `SLOTS_PER_EPOCH` constant for this specification.
///
/// Spec v0.6.1
fn genesis_epoch() -> Epoch {
Self::spec().genesis_epoch
}
/// Returns the `SHARD_COUNT` constant for this specification. /// Returns the `SHARD_COUNT` constant for this specification.
/// ///
/// Spec v0.5.1 /// Spec v0.5.1

View File

@ -1,6 +1,5 @@
use super::BeaconState; use super::BeaconState;
use crate::*; use crate::*;
use honey_badger_split::SplitExt;
use serde_derive::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
use swap_or_not_shuffle::shuffle_list; use swap_or_not_shuffle::shuffle_list;
@ -17,8 +16,12 @@ mod tests;
pub struct EpochCache { pub struct EpochCache {
/// `Some(epoch)` if the cache is initialized, where `epoch` is the cache it holds. /// `Some(epoch)` if the cache is initialized, where `epoch` is the cache it holds.
pub initialized_epoch: Option<Epoch>, pub initialized_epoch: Option<Epoch>,
/// All crosslink committees for an epoch. /// All crosslink committees.
pub epoch_crosslink_committees: EpochCrosslinkCommittees, pub crosslink_committees: Vec<CrosslinkCommittee>,
/// Maps a shard to `self.epoch_crosslink_committees`.
pub shard_crosslink_committees: Vec<Option<usize>>,
/// Maps a slot to `self.epoch_crosslink_committees`.
pub slot_crosslink_committees: Vec<Option<usize>>,
/// Maps validator index to a slot, shard and committee index for attestation. /// Maps validator index to a slot, shard and committee index for attestation.
pub attestation_duties: Vec<Option<AttestationDuty>>, pub attestation_duties: Vec<Option<AttestationDuty>>,
/// Indices of all active validators in the epoch /// Indices of all active validators in the epoch
@ -33,28 +36,33 @@ impl EpochCache {
seed: Hash256, seed: Hash256,
epoch_start_shard: u64, epoch_start_shard: u64,
spec: &ChainSpec, spec: &ChainSpec,
) -> Result<EpochCache, Error> { ) -> Result<EpochCache, BeaconStateError> {
if epoch != state.previous_epoch(spec) && epoch != state.current_epoch(spec) { if epoch != state.previous_epoch() && epoch != state.current_epoch() {
return Err(Error::EpochOutOfBounds); return Err(BeaconStateError::EpochOutOfBounds);
} }
let active_validator_indices = let active_validator_indices =
get_active_validator_indices(&state.validator_registry, epoch); get_active_validator_indices(&state.validator_registry, epoch);
let epoch_crosslink_committees = EpochCrosslinkCommittees::new( let epoch_committee_count = state.get_epoch_committee_count(epoch, spec);
epoch,
active_validator_indices.clone(),
seed,
epoch_start_shard,
state.get_epoch_committee_count(epoch, spec),
spec,
);
// Loop through all the validators in the committees and create the following map: let crosslink_committees = compute_epoch_commitees(
// epoch,
// `attestation_duties`: maps `ValidatorIndex` to `AttestationDuty`. state,
active_validator_indices.clone(),
epoch_committee_count,
spec,
)?;
let mut shard_crosslink_committees = vec![None; T::shard_count()];
let mut slot_crosslink_committees = vec![None; spec.slots_per_epoch as usize];
let mut attestation_duties = vec![None; state.validator_registry.len()]; let mut attestation_duties = vec![None; state.validator_registry.len()];
for crosslink_committee in epoch_crosslink_committees.crosslink_committees.iter() {
for (i, crosslink_committee) in crosslink_committees.iter().enumerate() {
shard_crosslink_committees[crosslink_committee.shard as usize] = Some(i);
slot_crosslink_committees[crosslink_committee.slot.as_usize()] = Some(i);
// Loop through each validator in the committee and store its attestation duties.
for (committee_index, validator_index) in for (committee_index, validator_index) in
crosslink_committee.committee.iter().enumerate() crosslink_committee.committee.iter().enumerate()
{ {
@ -70,20 +78,16 @@ impl EpochCache {
Ok(EpochCache { Ok(EpochCache {
initialized_epoch: Some(epoch), initialized_epoch: Some(epoch),
epoch_crosslink_committees, crosslink_committees,
attestation_duties, attestation_duties,
shard_crosslink_committees,
slot_crosslink_committees,
active_validator_indices, active_validator_indices,
}) })
} }
/// Return a vec of `CrosslinkCommittee` for a given slot. pub fn is_initialized_at(&self, epoch: Epoch) -> bool {
pub fn get_crosslink_committees_at_slot( Some(epoch) == self.initialized_epoch
&self,
slot: Slot,
spec: &ChainSpec,
) -> Option<&Vec<CrosslinkCommittee>> {
self.epoch_crosslink_committees
.get_crosslink_committees_at_slot(slot, spec)
} }
/// Return `Some(CrosslinkCommittee)` if the given shard has a committee during the given /// Return `Some(CrosslinkCommittee)` if the given shard has a committee during the given
@ -93,12 +97,11 @@ impl EpochCache {
shard: Shard, shard: Shard,
spec: &ChainSpec, spec: &ChainSpec,
) -> Option<&CrosslinkCommittee> { ) -> Option<&CrosslinkCommittee> {
if shard > self.shard_committee_indices.len() as u64 { if shard > self.shard_crosslink_committees.len() as u64 {
None None
} else { } else {
let (slot, committee) = self.shard_committee_indices[shard as usize]?; let i = self.shard_crosslink_committees[shard as usize]?;
let slot_committees = self.get_crosslink_committees_at_slot(slot, spec)?; Some(&self.crosslink_committees[i])
slot_committees.get(committee)
} }
} }
} }
@ -121,16 +124,52 @@ pub fn get_active_validator_indices(validators: &[Validator], epoch: Epoch) -> V
active active
} }
/// Contains all `CrosslinkCommittees` for an epoch. pub fn compute_epoch_commitees<T: EthSpec>(
#[derive(Debug, Default, PartialEq, Clone, Serialize, Deserialize)]
pub struct EpochCrosslinkCommittees {
/// The epoch the committees are present in.
epoch: Epoch, epoch: Epoch,
/// Committees indexed by the `index` parameter of `compute_committee` from the spec. state: &BeaconState<T>,
/// active_validator_indices: Vec<usize>,
/// The length of the vector is equal to the number of committees in the epoch epoch_committee_count: u64,
/// i.e. `state.get_epoch_committee_count(self.epoch)` spec: &ChainSpec,
pub crosslink_committees: Vec<CrosslinkCommittee>, ) -> Result<Vec<CrosslinkCommittee>, BeaconStateError> {
let seed = state.generate_seed(epoch, spec)?;
// The shuffler fails on a empty list, so if there are no active validator indices, simply
// return an empty list.
let shuffled_active_validator_indices = if active_validator_indices.is_empty() {
vec![]
} else {
shuffle_list(
active_validator_indices,
spec.shuffle_round_count,
&seed[..],
false,
)
.ok_or_else(|| Error::UnableToShuffle)?
};
let committee_size = shuffled_active_validator_indices.len() / epoch_committee_count as usize;
let epoch_start_shard = state.get_epoch_start_shard(epoch, spec)?;
Ok(shuffled_active_validator_indices
.chunks(committee_size)
.enumerate()
.map(|(index, committee)| {
let shard = (epoch_start_shard + index as u64) % spec.shard_count;
let slot = crosslink_committee_slot(
shard,
epoch,
epoch_start_shard,
epoch_committee_count,
spec,
);
CrosslinkCommittee {
slot,
shard,
committee: committee.to_vec(),
}
})
.collect())
} }
fn crosslink_committee_slot( fn crosslink_committee_slot(
@ -144,74 +183,3 @@ fn crosslink_committee_slot(
let offset = (shard + spec.shard_count - epoch_start_shard) % spec.shard_count; let offset = (shard + spec.shard_count - epoch_start_shard) % spec.shard_count;
epoch.start_slot(spec.slots_per_epoch) + offset / (epoch_committee_count / spec.slots_per_epoch) epoch.start_slot(spec.slots_per_epoch) + offset / (epoch_committee_count / spec.slots_per_epoch)
} }
impl EpochCrosslinkCommittees {
fn new(
epoch: Epoch,
active_validator_indices: Vec<usize>,
seed: Hash256,
epoch_start_shard: u64,
epoch_committee_count: u64,
spec: &ChainSpec,
) -> Self {
// The shuffler fails on a empty list, so if there are no active validator indices, simply
// return an empty list.
let shuffled_active_validator_indices = if active_validator_indices.is_empty() {
vec![]
} else {
shuffle_list(
active_validator_indices,
spec.shuffle_round_count,
&seed[..],
false,
)
.ok_or_else(|| Error::UnableToShuffle)?
};
let committee_size =
shuffled_active_validator_indices.len() / epoch_committee_count as usize;
let crosslink_committees = shuffled_active_validator_indices
.into_iter()
.chunks(committee_size)
.enumerate()
.map(|(index, committee)| {
let shard = (epoch_start_start_shard + index) % spec.shard_count;
let slot = crosslink_committee_slot(
shard,
epoch,
epoch_start_shard,
epoch_committee_count,
spec,
);
CrosslinkCommittee {
slot,
shard,
committee: committee.to_vec(),
}
})
.collect();
Ok(Self {
epoch,
crosslink_committees,
})
}
/// Return a vec of `CrosslinkCommittee` for a given slot.
fn get_crosslink_committees_at_slot(
&self,
slot: Slot,
spec: &ChainSpec,
) -> Option<&Vec<CrosslinkCommittee>> {
let epoch_start_slot = self.epoch.start_slot(spec.slots_per_epoch);
let epoch_end_slot = self.epoch.end_slot(spec.slots_per_epoch);
if (epoch_start_slot <= slot) && (slot <= epoch_end_slot) {
let index = slot - epoch_start_slot;
self.crosslink_committees.get(index.as_usize())
} else {
None
}
}
}

View File

@ -1,4 +1,4 @@
#![cfg(all(not(test), test))] #![cfg(test)]
use super::*; use super::*;
use crate::beacon_state::FewValidatorsEthSpec; use crate::beacon_state::FewValidatorsEthSpec;

View File

@ -6,6 +6,7 @@ use crate::test_utils::*;
ssz_tests!(FoundationBeaconState); ssz_tests!(FoundationBeaconState);
cached_tree_hash_tests!(FoundationBeaconState); cached_tree_hash_tests!(FoundationBeaconState);
/*
/// Test that /// Test that
/// ///
/// 1. Using the cache before it's built fails. /// 1. Using the cache before it's built fails.
@ -22,12 +23,14 @@ fn test_cache_initialization<'a, T: EthSpec>(
// Assuming the cache isn't already built, assert that a call to a cache-using function fails. // Assuming the cache isn't already built, assert that a call to a cache-using function fails.
assert_eq!( assert_eq!(
state.get_beacon_proposer_index(slot, relative_epoch, spec), state.get_attestation_duties(0, spec),
Err(BeaconStateError::EpochCacheUninitialized(relative_epoch)) Err(BeaconStateError::EpochCacheUninitialized(relative_epoch))
); );
// Build the cache. // Build the cache.
state.build_epoch_cache(relative_epoch, spec).unwrap(); state
.build_current_epoch_cache(relative_epoch, spec)
.unwrap();
// Assert a call to a cache-using function passes. // Assert a call to a cache-using function passes.
let _ = state let _ = state
@ -59,6 +62,7 @@ fn cache_initialization() {
test_cache_initialization(&mut state, RelativeEpoch::NextWithRegistryChange, &spec); test_cache_initialization(&mut state, RelativeEpoch::NextWithRegistryChange, &spec);
test_cache_initialization(&mut state, RelativeEpoch::NextWithoutRegistryChange, &spec); test_cache_initialization(&mut state, RelativeEpoch::NextWithoutRegistryChange, &spec);
} }
*/
#[test] #[test]
fn tree_hash_cache() { fn tree_hash_cache() {

View File

@ -4,7 +4,6 @@ use crate::*;
pub enum Error { pub enum Error {
EpochTooLow { base: Epoch, other: Epoch }, EpochTooLow { base: Epoch, other: Epoch },
EpochTooHigh { base: Epoch, other: Epoch }, EpochTooHigh { base: Epoch, other: Epoch },
AmbiguiousNextEpoch,
} }
/// Defines the epochs relative to some epoch. Most useful when referring to the committees prior /// Defines the epochs relative to some epoch. Most useful when referring to the committees prior
@ -17,28 +16,19 @@ pub enum RelativeEpoch {
Previous, Previous,
/// The current epoch. /// The current epoch.
Current, Current,
/// The next epoch if there _is_ a validator registry update. /// The next epoch.
/// Next,
/// If the validator registry is updated during an epoch transition, a new shuffling seed is
/// generated, this changes the attestation and proposal roles.
NextWithRegistryChange,
/// The next epoch if there _is not_ a validator registry update.
///
/// If the validator registry _is not_ updated during an epoch transition, the shuffling stays
/// the same.
NextWithoutRegistryChange,
} }
impl RelativeEpoch { impl RelativeEpoch {
/// Returns the `epoch` that `self` refers to, with respect to the `base` epoch. /// Returns the `epoch` that `self` refers to, with respect to the `base` epoch.
/// ///
/// Spec v0.5.1 /// Spec v0.6.1
pub fn into_epoch(self, base: Epoch) -> Epoch { pub fn into_epoch(self, base: Epoch) -> Epoch {
match self { match self {
RelativeEpoch::Previous => base - 1, RelativeEpoch::Previous => base - 1,
RelativeEpoch::Current => base, RelativeEpoch::Current => base,
RelativeEpoch::NextWithoutRegistryChange => base + 1, RelativeEpoch::Next => base + 1,
RelativeEpoch::NextWithRegistryChange => base + 1,
} }
} }
@ -48,17 +38,15 @@ impl RelativeEpoch {
/// Returns an error when: /// Returns an error when:
/// - `EpochTooLow` when `other` is more than 1 prior to `base`. /// - `EpochTooLow` when `other` is more than 1 prior to `base`.
/// - `EpochTooHigh` when `other` is more than 1 after `base`. /// - `EpochTooHigh` when `other` is more than 1 after `base`.
/// - `AmbiguiousNextEpoch` whenever `other` is one after `base`, because it's unknowable if
/// there will be a registry change.
/// ///
/// Spec v0.5.1 /// Spec v0.6.1
pub fn from_epoch(base: Epoch, other: Epoch) -> Result<Self, Error> { pub fn from_epoch(base: Epoch, other: Epoch) -> Result<Self, Error> {
if other == base - 1 { if other == base - 1 {
Ok(RelativeEpoch::Previous) Ok(RelativeEpoch::Previous)
} else if other == base { } else if other == base {
Ok(RelativeEpoch::Current) Ok(RelativeEpoch::Current)
} else if other == base + 1 { } else if other == base + 1 {
Err(Error::AmbiguiousNextEpoch) Ok(RelativeEpoch::Next)
} else if other < base { } else if other < base {
Err(Error::EpochTooLow { base, other }) Err(Error::EpochTooLow { base, other })
} else { } else {
@ -67,11 +55,8 @@ impl RelativeEpoch {
} }
/// Convenience function for `Self::from_epoch` where both slots are converted into epochs. /// Convenience function for `Self::from_epoch` where both slots are converted into epochs.
pub fn from_slot(base: Slot, other: Slot, spec: &ChainSpec) -> Result<Self, Error> { pub fn from_slot(base: Slot, other: Slot, slots_per_epoch: u64) -> Result<Self, Error> {
Self::from_epoch( Self::from_epoch(base.epoch(slots_per_epoch), other.epoch(slots_per_epoch))
base.epoch(spec.slots_per_epoch),
other.epoch(spec.slots_per_epoch),
)
} }
} }
@ -85,14 +70,7 @@ mod tests {
assert_eq!(RelativeEpoch::Current.into_epoch(base), base); assert_eq!(RelativeEpoch::Current.into_epoch(base), base);
assert_eq!(RelativeEpoch::Previous.into_epoch(base), base - 1); assert_eq!(RelativeEpoch::Previous.into_epoch(base), base - 1);
assert_eq!( assert_eq!(RelativeEpoch::Next.into_epoch(base), base + 1);
RelativeEpoch::NextWithRegistryChange.into_epoch(base),
base + 1
);
assert_eq!(
RelativeEpoch::NextWithoutRegistryChange.into_epoch(base),
base + 1
);
} }
#[test] #[test]
@ -109,26 +87,26 @@ mod tests {
); );
assert_eq!( assert_eq!(
RelativeEpoch::from_epoch(base, base + 1), RelativeEpoch::from_epoch(base, base + 1),
Err(RelativeEpochError::AmbiguiousNextEpoch) Ok(RelativeEpoch::Next)
); );
} }
#[test] #[test]
fn from_slot() { fn from_slot() {
let spec = ChainSpec::foundation(); let slots_per_epoch: u64 = 64;
let base = Epoch::new(10).start_slot(spec.slots_per_epoch); let base = Slot::new(10 * slots_per_epoch);
assert_eq!( assert_eq!(
RelativeEpoch::from_slot(base, base - 1, &spec), RelativeEpoch::from_slot(base, base - 1, slots_per_epoch),
Ok(RelativeEpoch::Previous) Ok(RelativeEpoch::Previous)
); );
assert_eq!( assert_eq!(
RelativeEpoch::from_slot(base, base, &spec), RelativeEpoch::from_slot(base, base, slots_per_epoch),
Ok(RelativeEpoch::Current) Ok(RelativeEpoch::Current)
); );
assert_eq!( assert_eq!(
RelativeEpoch::from_slot(base, base + spec.slots_per_epoch, &spec), RelativeEpoch::from_slot(base, base + slots_per_epoch, slots_per_epoch),
Err(RelativeEpochError::AmbiguiousNextEpoch) Ok(RelativeEpoch::Next)
); );
} }
} }

View File

@ -0,0 +1,21 @@
mod testing_attestation_builder;
mod testing_attestation_data_builder;
mod testing_attester_slashing_builder;
mod testing_beacon_block_builder;
mod testing_beacon_state_builder;
mod testing_deposit_builder;
mod testing_pending_attestation_builder;
mod testing_proposer_slashing_builder;
mod testing_transfer_builder;
mod testing_voluntary_exit_builder;
pub use testing_attestation_builder::*;
pub use testing_attestation_data_builder::*;
pub use testing_attester_slashing_builder::*;
pub use testing_beacon_block_builder::*;
pub use testing_beacon_state_builder::*;
pub use testing_deposit_builder::*;
pub use testing_pending_attestation_builder::*;
pub use testing_proposer_slashing_builder::*;
pub use testing_transfer_builder::*;
pub use testing_voluntary_exit_builder::*;

View File

@ -16,8 +16,8 @@ impl TestingAttestationDataBuilder {
slot: Slot, slot: Slot,
spec: &ChainSpec, spec: &ChainSpec,
) -> Self { ) -> Self {
let current_epoch = state.current_epoch(spec); let current_epoch = state.current_epoch();
let previous_epoch = state.previous_epoch(spec); let previous_epoch = state.previous_epoch();
let is_previous_epoch = let is_previous_epoch =
state.slot.epoch(spec.slots_per_epoch) != slot.epoch(spec.slots_per_epoch); state.slot.epoch(spec.slots_per_epoch) != slot.epoch(spec.slots_per_epoch);
@ -29,9 +29,9 @@ impl TestingAttestationDataBuilder {
}; };
let target_epoch = if is_previous_epoch { let target_epoch = if is_previous_epoch {
state.previous_epoch(spec) state.previous_epoch()
} else { } else {
state.current_epoch(spec) state.current_epoch()
}; };
let target_root = if is_previous_epoch { let target_root = if is_previous_epoch {

View File

@ -1,4 +1,4 @@
use super::{generate_deterministic_keypairs, KeypairsFile}; use super::super::{generate_deterministic_keypairs, KeypairsFile};
use crate::test_utils::TestingPendingAttestationBuilder; use crate::test_utils::TestingPendingAttestationBuilder;
use crate::*; use crate::*;
use bls::get_withdrawal_credentials; use bls::get_withdrawal_credentials;
@ -166,14 +166,7 @@ impl<T: EthSpec> TestingBeaconStateBuilder<T> {
/// Note: this performs the build when called. Ensure that no changes are made that would /// Note: this performs the build when called. Ensure that no changes are made that would
/// invalidate this cache. /// invalidate this cache.
pub fn build_caches(&mut self, spec: &ChainSpec) -> Result<(), BeaconStateError> { pub fn build_caches(&mut self, spec: &ChainSpec) -> Result<(), BeaconStateError> {
let state = &mut self.state; self.state.build_all_caches(spec);
state.build_epoch_cache(RelativeEpoch::Previous, &spec)?;
state.build_epoch_cache(RelativeEpoch::Current, &spec)?;
state.build_epoch_cache(RelativeEpoch::NextWithRegistryChange, &spec)?;
state.build_epoch_cache(RelativeEpoch::NextWithoutRegistryChange, &spec)?;
state.update_pubkey_cache()?;
Ok(()) Ok(())
} }
@ -218,8 +211,8 @@ impl<T: EthSpec> TestingBeaconStateBuilder<T> {
.build_epoch_cache(RelativeEpoch::Current, spec) .build_epoch_cache(RelativeEpoch::Current, spec)
.unwrap(); .unwrap();
let current_epoch = state.current_epoch(spec); let current_epoch = state.current_epoch();
let previous_epoch = state.previous_epoch(spec); let previous_epoch = state.previous_epoch();
let first_slot = previous_epoch.start_slot(spec.slots_per_epoch).as_u64(); let first_slot = previous_epoch.start_slot(spec.slots_per_epoch).as_u64();
let last_slot = current_epoch.end_slot(spec.slots_per_epoch).as_u64() let last_slot = current_epoch.end_slot(spec.slots_per_epoch).as_u64()
@ -246,7 +239,7 @@ impl<T: EthSpec> TestingBeaconStateBuilder<T> {
builder.add_committee_participation(signers); builder.add_committee_participation(signers);
let attestation = builder.build(); let attestation = builder.build();
if attestation.data.target_epoch < state.current_epoch(spec) { if attestation.data.target_epoch < state.current_epoch() {
state.previous_epoch_attestations.push(attestation) state.previous_epoch_attestations.push(attestation)
} else { } else {
state.current_epoch_attestations.push(attestation) state.current_epoch_attestations.push(attestation)

View File

@ -1,20 +1,12 @@
#[macro_use] #[macro_use]
mod macros; mod macros;
mod builders;
mod generate_deterministic_keypairs; mod generate_deterministic_keypairs;
mod keypairs_file; mod keypairs_file;
mod serde_utils; mod serde_utils;
mod test_random; mod test_random;
mod testing_attestation_builder;
mod testing_attestation_data_builder;
mod testing_attester_slashing_builder;
mod testing_beacon_block_builder;
mod testing_beacon_state_builder;
mod testing_deposit_builder;
mod testing_pending_attestation_builder;
mod testing_proposer_slashing_builder;
mod testing_transfer_builder;
mod testing_voluntary_exit_builder;
pub use builders::*;
pub use generate_deterministic_keypairs::generate_deterministic_keypair; pub use generate_deterministic_keypairs::generate_deterministic_keypair;
pub use generate_deterministic_keypairs::generate_deterministic_keypairs; pub use generate_deterministic_keypairs::generate_deterministic_keypairs;
pub use keypairs_file::KeypairsFile; pub use keypairs_file::KeypairsFile;
@ -24,13 +16,3 @@ pub use rand::{
}; };
pub use serde_utils::{fork_from_hex_str, u8_from_hex_str}; pub use serde_utils::{fork_from_hex_str, u8_from_hex_str};
pub use test_random::TestRandom; pub use test_random::TestRandom;
pub use testing_attestation_builder::TestingAttestationBuilder;
pub use testing_attestation_data_builder::TestingAttestationDataBuilder;
pub use testing_attester_slashing_builder::TestingAttesterSlashingBuilder;
pub use testing_beacon_block_builder::TestingBeaconBlockBuilder;
pub use testing_beacon_state_builder::{keypairs_path, TestingBeaconStateBuilder};
pub use testing_deposit_builder::TestingDepositBuilder;
pub use testing_pending_attestation_builder::TestingPendingAttestationBuilder;
pub use testing_proposer_slashing_builder::TestingProposerSlashingBuilder;
pub use testing_transfer_builder::TestingTransferBuilder;
pub use testing_voluntary_exit_builder::TestingVoluntaryExitBuilder;