Add progress on epoch caching
This commit is contained in:
parent
0885d56b36
commit
944ac73ef9
@ -23,7 +23,7 @@ mod exit_cache;
|
||||
mod pubkey_cache;
|
||||
mod tests;
|
||||
|
||||
pub const CACHED_EPOCHS: usize = 4;
|
||||
pub const CACHED_EPOCHS: usize = 3;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum Error {
|
||||
@ -49,6 +49,7 @@ pub enum Error {
|
||||
},
|
||||
PreviousEpochCacheUninitialized,
|
||||
CurrentEpochCacheUnintialized,
|
||||
EpochCacheUnintialized(RelativeEpoch),
|
||||
EpochCacheError(EpochCacheError),
|
||||
TreeHashCacheError(TreeHashCacheError),
|
||||
}
|
||||
@ -117,13 +118,7 @@ where
|
||||
#[ssz(skip_deserializing)]
|
||||
#[tree_hash(skip_hashing)]
|
||||
#[test_random(default)]
|
||||
pub previous_epoch_cache: EpochCache,
|
||||
#[serde(default)]
|
||||
#[ssz(skip_serializing)]
|
||||
#[ssz(skip_deserializing)]
|
||||
#[tree_hash(skip_hashing)]
|
||||
#[test_random(default)]
|
||||
pub current_epoch_cache: EpochCache,
|
||||
pub epoch_caches: [EpochCache; CACHED_EPOCHS],
|
||||
#[serde(default)]
|
||||
#[ssz(skip_serializing)]
|
||||
#[ssz(skip_deserializing)]
|
||||
@ -214,8 +209,11 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
/*
|
||||
* Caching (not in spec)
|
||||
*/
|
||||
previous_epoch_cache: EpochCache::default(),
|
||||
current_epoch_cache: EpochCache::default(),
|
||||
epoch_caches: [
|
||||
EpochCache::default(),
|
||||
EpochCache::default(),
|
||||
EpochCache::default(),
|
||||
],
|
||||
pubkey_cache: PubkeyCache::default(),
|
||||
tree_hash_cache: TreeHashCache::default(),
|
||||
exit_cache: ExitCache::default(),
|
||||
@ -254,8 +252,8 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
/// The epoch corresponding to `self.slot`.
|
||||
///
|
||||
/// Spec v0.6.1
|
||||
pub fn current_epoch(&self, spec: &ChainSpec) -> Epoch {
|
||||
self.slot.epoch(spec.slots_per_epoch)
|
||||
pub fn current_epoch(&self) -> Epoch {
|
||||
self.slot.epoch(T::slots_per_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.
|
||||
///
|
||||
/// Spec v0.6.1
|
||||
pub fn previous_epoch(&self, spec: &ChainSpec) -> Epoch {
|
||||
let current_epoch = self.current_epoch(spec);
|
||||
if current_epoch > spec.genesis_epoch {
|
||||
pub fn previous_epoch(&self) -> Epoch {
|
||||
let current_epoch = self.current_epoch();
|
||||
if current_epoch > T::genesis_epoch() {
|
||||
current_epoch - 1
|
||||
} else {
|
||||
current_epoch
|
||||
@ -275,8 +273,8 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
/// The epoch following `self.current_epoch()`.
|
||||
///
|
||||
/// Spec v0.6.1
|
||||
pub fn next_epoch(&self, spec: &ChainSpec) -> Epoch {
|
||||
self.current_epoch(spec) + 1
|
||||
pub fn next_epoch(&self) -> Epoch {
|
||||
self.current_epoch() + 1
|
||||
}
|
||||
|
||||
/// Return the number of committees at ``epoch``.
|
||||
@ -301,13 +299,13 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
///
|
||||
/// Spec v0.6.1
|
||||
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);
|
||||
}
|
||||
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
|
||||
+ self.get_shard_delta(self.current_epoch(spec), spec))
|
||||
+ self.get_shard_delta(self.current_epoch(), spec))
|
||||
% shard_count;
|
||||
while check_epoch > epoch {
|
||||
check_epoch -= 1;
|
||||
@ -458,8 +456,8 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
/// Safely obtains the index for `latest_randao_mixes`
|
||||
///
|
||||
/// Spec v0.5.1
|
||||
fn get_randao_mix_index(&self, epoch: Epoch, spec: &ChainSpec) -> Result<usize, Error> {
|
||||
let current_epoch = self.current_epoch(spec);
|
||||
fn get_randao_mix_index(&self, epoch: Epoch) -> Result<usize, Error> {
|
||||
let current_epoch = self.current_epoch();
|
||||
let len = T::LatestRandaoMixesLength::to_u64();
|
||||
|
||||
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)));
|
||||
|
||||
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(())
|
||||
}
|
||||
@ -494,21 +492,16 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
/// Return the randao mix at a recent ``epoch``.
|
||||
///
|
||||
/// Spec v0.5.1
|
||||
pub fn get_randao_mix(&self, epoch: Epoch, spec: &ChainSpec) -> Result<&Hash256, Error> {
|
||||
let i = self.get_randao_mix_index(epoch, spec)?;
|
||||
pub fn get_randao_mix(&self, epoch: Epoch) -> Result<&Hash256, Error> {
|
||||
let i = self.get_randao_mix_index(epoch)?;
|
||||
Ok(&self.latest_randao_mixes[i])
|
||||
}
|
||||
|
||||
/// Set the randao mix at a recent ``epoch``.
|
||||
///
|
||||
/// Spec v0.5.1
|
||||
pub fn set_randao_mix(
|
||||
&mut self,
|
||||
epoch: Epoch,
|
||||
mix: Hash256,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<(), Error> {
|
||||
let i = self.get_randao_mix_index(epoch, spec)?;
|
||||
pub fn set_randao_mix(&mut self, epoch: Epoch, mix: Hash256) -> Result<(), Error> {
|
||||
let i = self.get_randao_mix_index(epoch)?;
|
||||
self.latest_randao_mixes[i] = mix;
|
||||
Ok(())
|
||||
}
|
||||
@ -517,7 +510,7 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
///
|
||||
/// Spec v0.6.1
|
||||
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
|
||||
< epoch
|
||||
@ -627,9 +620,9 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
epoch: Epoch,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<&[PendingAttestation], Error> {
|
||||
if epoch == self.current_epoch(spec) {
|
||||
if epoch == self.current_epoch() {
|
||||
Ok(&self.current_epoch_attestations)
|
||||
} else if epoch == self.previous_epoch(spec) {
|
||||
} else if epoch == self.previous_epoch() {
|
||||
Ok(&self.previous_epoch_attestations)
|
||||
} else {
|
||||
Err(Error::EpochOutOfBounds)
|
||||
@ -659,7 +652,7 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
/// Spec v0.5.1
|
||||
pub fn generate_seed(&self, epoch: Epoch, spec: &ChainSpec) -> Result<Hash256, Error> {
|
||||
let mut input = self
|
||||
.get_randao_mix(epoch - spec.min_seed_lookahead, spec)?
|
||||
.get_randao_mix(epoch - spec.min_seed_lookahead)?
|
||||
.as_bytes()
|
||||
.to_vec();
|
||||
|
||||
@ -699,7 +692,7 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
pub fn get_churn_limit(&self, spec: &ChainSpec) -> Result<u64, Error> {
|
||||
Ok(std::cmp::max(
|
||||
spec.min_per_epoch_churn_limit,
|
||||
self.cache(RelativeEpoch::Current, spec)?
|
||||
self.cache(self.current_epoch(), spec)?
|
||||
.active_validator_indices
|
||||
.len() as u64
|
||||
/ spec.churn_limit_quotient,
|
||||
@ -719,7 +712,7 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
validator_index: usize,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<&Option<AttestationDuty>, Error> {
|
||||
let cache = self.cache(RelativeEpoch::Current, spec)?;
|
||||
let cache = self.cache(self.current_epoch(), spec)?;
|
||||
|
||||
Ok(cache
|
||||
.attestation_duties
|
||||
@ -743,8 +736,9 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
|
||||
/// Build all the caches, if they need to be built.
|
||||
pub fn build_all_caches(&mut self, spec: &ChainSpec) -> Result<(), Error> {
|
||||
self.build_previous_epoch_cache(spec)?;
|
||||
self.build_current_epoch_cache(spec)?;
|
||||
self.build_epoch_cache(RelativeEpoch::Previous, spec)?;
|
||||
self.build_epoch_cache(RelativeEpoch::Current, spec)?;
|
||||
self.build_epoch_cache(RelativeEpoch::Next, spec)?;
|
||||
self.update_pubkey_cache()?;
|
||||
self.update_tree_hash_cache()?;
|
||||
self.exit_cache
|
||||
@ -754,9 +748,14 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
}
|
||||
|
||||
/// Build an epoch cache, unless it is has already been built.
|
||||
pub fn build_previous_epoch_cache(&mut self, spec: &ChainSpec) -> Result<(), Error> {
|
||||
if self.caches[cache_index].initialized_epoch == Some(self.slot.epoch(spec.slots_per_epoch))
|
||||
{
|
||||
pub fn build_epoch_cache(
|
||||
&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(())
|
||||
} else {
|
||||
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.
|
||||
pub fn force_build_previous_epoch_cache(&mut self, spec: &ChainSpec) -> Result<(), Error> {
|
||||
let epoch = self.previous_epoch(spec);
|
||||
self.previous_epoch_cache = EpochCache::initialized(
|
||||
&self,
|
||||
epoch,
|
||||
self.generate_seed(epoch, spec)?,
|
||||
self.get_epoch_start_shard(epoch, spec)?,
|
||||
spec,
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
pub fn force_build_epoch_cache(
|
||||
&mut self,
|
||||
relative_epoch: RelativeEpoch,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<(), Error> {
|
||||
let epoch = relative_epoch.into_epoch(self.current_epoch());
|
||||
|
||||
/// Always builds the current epoch cache, even if it is already 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.epoch_caches[Self::cache_index(relative_epoch)] = EpochCache::initialized(
|
||||
&self,
|
||||
epoch,
|
||||
self.generate_seed(epoch, spec)?,
|
||||
@ -792,10 +783,39 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
/// 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.
|
||||
pub fn advance_caches(&mut self) {
|
||||
self.previous_epoch_cache =
|
||||
std::mem::replace(&mut self.current_epoch_cache, EpochCache::default());
|
||||
self.force_build_current_epoch_cache();
|
||||
///
|
||||
/// Note: whilst this function will preserve already-built caches, it will not build any.
|
||||
pub fn advance_caches(&mut self, spec: &ChainSpec) {
|
||||
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
|
||||
|
@ -14,6 +14,20 @@ pub trait EthSpec:
|
||||
|
||||
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.
|
||||
///
|
||||
/// Spec v0.5.1
|
||||
|
@ -1,6 +1,5 @@
|
||||
use super::BeaconState;
|
||||
use crate::*;
|
||||
use honey_badger_split::SplitExt;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use swap_or_not_shuffle::shuffle_list;
|
||||
|
||||
@ -17,8 +16,12 @@ mod tests;
|
||||
pub struct EpochCache {
|
||||
/// `Some(epoch)` if the cache is initialized, where `epoch` is the cache it holds.
|
||||
pub initialized_epoch: Option<Epoch>,
|
||||
/// All crosslink committees for an epoch.
|
||||
pub epoch_crosslink_committees: EpochCrosslinkCommittees,
|
||||
/// All crosslink committees.
|
||||
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.
|
||||
pub attestation_duties: Vec<Option<AttestationDuty>>,
|
||||
/// Indices of all active validators in the epoch
|
||||
@ -33,28 +36,33 @@ impl EpochCache {
|
||||
seed: Hash256,
|
||||
epoch_start_shard: u64,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<EpochCache, Error> {
|
||||
if epoch != state.previous_epoch(spec) && epoch != state.current_epoch(spec) {
|
||||
return Err(Error::EpochOutOfBounds);
|
||||
) -> Result<EpochCache, BeaconStateError> {
|
||||
if epoch != state.previous_epoch() && epoch != state.current_epoch() {
|
||||
return Err(BeaconStateError::EpochOutOfBounds);
|
||||
}
|
||||
|
||||
let active_validator_indices =
|
||||
get_active_validator_indices(&state.validator_registry, epoch);
|
||||
|
||||
let epoch_crosslink_committees = EpochCrosslinkCommittees::new(
|
||||
epoch,
|
||||
active_validator_indices.clone(),
|
||||
seed,
|
||||
epoch_start_shard,
|
||||
state.get_epoch_committee_count(epoch, spec),
|
||||
spec,
|
||||
);
|
||||
let epoch_committee_count = state.get_epoch_committee_count(epoch, spec);
|
||||
|
||||
// Loop through all the validators in the committees and create the following map:
|
||||
//
|
||||
// `attestation_duties`: maps `ValidatorIndex` to `AttestationDuty`.
|
||||
let crosslink_committees = compute_epoch_commitees(
|
||||
epoch,
|
||||
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()];
|
||||
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
|
||||
crosslink_committee.committee.iter().enumerate()
|
||||
{
|
||||
@ -70,20 +78,16 @@ impl EpochCache {
|
||||
|
||||
Ok(EpochCache {
|
||||
initialized_epoch: Some(epoch),
|
||||
epoch_crosslink_committees,
|
||||
crosslink_committees,
|
||||
attestation_duties,
|
||||
shard_crosslink_committees,
|
||||
slot_crosslink_committees,
|
||||
active_validator_indices,
|
||||
})
|
||||
}
|
||||
|
||||
/// Return a vec of `CrosslinkCommittee` for a given slot.
|
||||
pub fn get_crosslink_committees_at_slot(
|
||||
&self,
|
||||
slot: Slot,
|
||||
spec: &ChainSpec,
|
||||
) -> Option<&Vec<CrosslinkCommittee>> {
|
||||
self.epoch_crosslink_committees
|
||||
.get_crosslink_committees_at_slot(slot, spec)
|
||||
pub fn is_initialized_at(&self, epoch: Epoch) -> bool {
|
||||
Some(epoch) == self.initialized_epoch
|
||||
}
|
||||
|
||||
/// Return `Some(CrosslinkCommittee)` if the given shard has a committee during the given
|
||||
@ -93,12 +97,11 @@ impl EpochCache {
|
||||
shard: Shard,
|
||||
spec: &ChainSpec,
|
||||
) -> Option<&CrosslinkCommittee> {
|
||||
if shard > self.shard_committee_indices.len() as u64 {
|
||||
if shard > self.shard_crosslink_committees.len() as u64 {
|
||||
None
|
||||
} else {
|
||||
let (slot, committee) = self.shard_committee_indices[shard as usize]?;
|
||||
let slot_committees = self.get_crosslink_committees_at_slot(slot, spec)?;
|
||||
slot_committees.get(committee)
|
||||
let i = self.shard_crosslink_committees[shard as usize]?;
|
||||
Some(&self.crosslink_committees[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -121,16 +124,52 @@ pub fn get_active_validator_indices(validators: &[Validator], epoch: Epoch) -> V
|
||||
active
|
||||
}
|
||||
|
||||
/// Contains all `CrosslinkCommittees` for an epoch.
|
||||
#[derive(Debug, Default, PartialEq, Clone, Serialize, Deserialize)]
|
||||
pub struct EpochCrosslinkCommittees {
|
||||
/// The epoch the committees are present in.
|
||||
pub fn compute_epoch_commitees<T: EthSpec>(
|
||||
epoch: Epoch,
|
||||
/// Committees indexed by the `index` parameter of `compute_committee` from the spec.
|
||||
///
|
||||
/// The length of the vector is equal to the number of committees in the epoch
|
||||
/// i.e. `state.get_epoch_committee_count(self.epoch)`
|
||||
pub crosslink_committees: Vec<CrosslinkCommittee>,
|
||||
state: &BeaconState<T>,
|
||||
active_validator_indices: Vec<usize>,
|
||||
epoch_committee_count: u64,
|
||||
spec: &ChainSpec,
|
||||
) -> 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(
|
||||
@ -144,74 +183,3 @@ fn crosslink_committee_slot(
|
||||
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)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
#![cfg(all(not(test), test))]
|
||||
#![cfg(test)]
|
||||
|
||||
use super::*;
|
||||
use crate::beacon_state::FewValidatorsEthSpec;
|
||||
|
@ -6,6 +6,7 @@ use crate::test_utils::*;
|
||||
ssz_tests!(FoundationBeaconState);
|
||||
cached_tree_hash_tests!(FoundationBeaconState);
|
||||
|
||||
/*
|
||||
/// Test that
|
||||
///
|
||||
/// 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.
|
||||
assert_eq!(
|
||||
state.get_beacon_proposer_index(slot, relative_epoch, spec),
|
||||
state.get_attestation_duties(0, spec),
|
||||
Err(BeaconStateError::EpochCacheUninitialized(relative_epoch))
|
||||
);
|
||||
|
||||
// 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.
|
||||
let _ = state
|
||||
@ -59,6 +62,7 @@ fn cache_initialization() {
|
||||
test_cache_initialization(&mut state, RelativeEpoch::NextWithRegistryChange, &spec);
|
||||
test_cache_initialization(&mut state, RelativeEpoch::NextWithoutRegistryChange, &spec);
|
||||
}
|
||||
*/
|
||||
|
||||
#[test]
|
||||
fn tree_hash_cache() {
|
||||
|
@ -4,7 +4,6 @@ use crate::*;
|
||||
pub enum Error {
|
||||
EpochTooLow { 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
|
||||
@ -17,28 +16,19 @@ pub enum RelativeEpoch {
|
||||
Previous,
|
||||
/// The current epoch.
|
||||
Current,
|
||||
/// The next epoch if there _is_ a validator registry update.
|
||||
///
|
||||
/// 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,
|
||||
/// The next epoch.
|
||||
Next,
|
||||
}
|
||||
|
||||
impl RelativeEpoch {
|
||||
/// 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 {
|
||||
match self {
|
||||
RelativeEpoch::Previous => base - 1,
|
||||
RelativeEpoch::Current => base,
|
||||
RelativeEpoch::NextWithoutRegistryChange => base + 1,
|
||||
RelativeEpoch::NextWithRegistryChange => base + 1,
|
||||
RelativeEpoch::Next => base + 1,
|
||||
}
|
||||
}
|
||||
|
||||
@ -48,17 +38,15 @@ impl RelativeEpoch {
|
||||
/// Returns an error when:
|
||||
/// - `EpochTooLow` when `other` is more than 1 prior to `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> {
|
||||
if other == base - 1 {
|
||||
Ok(RelativeEpoch::Previous)
|
||||
} else if other == base {
|
||||
Ok(RelativeEpoch::Current)
|
||||
} else if other == base + 1 {
|
||||
Err(Error::AmbiguiousNextEpoch)
|
||||
Ok(RelativeEpoch::Next)
|
||||
} else if other < base {
|
||||
Err(Error::EpochTooLow { base, other })
|
||||
} else {
|
||||
@ -67,11 +55,8 @@ impl RelativeEpoch {
|
||||
}
|
||||
|
||||
/// 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> {
|
||||
Self::from_epoch(
|
||||
base.epoch(spec.slots_per_epoch),
|
||||
other.epoch(spec.slots_per_epoch),
|
||||
)
|
||||
pub fn from_slot(base: Slot, other: Slot, slots_per_epoch: u64) -> Result<Self, Error> {
|
||||
Self::from_epoch(base.epoch(slots_per_epoch), other.epoch(slots_per_epoch))
|
||||
}
|
||||
}
|
||||
|
||||
@ -85,14 +70,7 @@ mod tests {
|
||||
|
||||
assert_eq!(RelativeEpoch::Current.into_epoch(base), base);
|
||||
assert_eq!(RelativeEpoch::Previous.into_epoch(base), base - 1);
|
||||
assert_eq!(
|
||||
RelativeEpoch::NextWithRegistryChange.into_epoch(base),
|
||||
base + 1
|
||||
);
|
||||
assert_eq!(
|
||||
RelativeEpoch::NextWithoutRegistryChange.into_epoch(base),
|
||||
base + 1
|
||||
);
|
||||
assert_eq!(RelativeEpoch::Next.into_epoch(base), base + 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -109,26 +87,26 @@ mod tests {
|
||||
);
|
||||
assert_eq!(
|
||||
RelativeEpoch::from_epoch(base, base + 1),
|
||||
Err(RelativeEpochError::AmbiguiousNextEpoch)
|
||||
Ok(RelativeEpoch::Next)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_slot() {
|
||||
let spec = ChainSpec::foundation();
|
||||
let base = Epoch::new(10).start_slot(spec.slots_per_epoch);
|
||||
let slots_per_epoch: u64 = 64;
|
||||
let base = Slot::new(10 * slots_per_epoch);
|
||||
|
||||
assert_eq!(
|
||||
RelativeEpoch::from_slot(base, base - 1, &spec),
|
||||
RelativeEpoch::from_slot(base, base - 1, slots_per_epoch),
|
||||
Ok(RelativeEpoch::Previous)
|
||||
);
|
||||
assert_eq!(
|
||||
RelativeEpoch::from_slot(base, base, &spec),
|
||||
RelativeEpoch::from_slot(base, base, slots_per_epoch),
|
||||
Ok(RelativeEpoch::Current)
|
||||
);
|
||||
assert_eq!(
|
||||
RelativeEpoch::from_slot(base, base + spec.slots_per_epoch, &spec),
|
||||
Err(RelativeEpochError::AmbiguiousNextEpoch)
|
||||
RelativeEpoch::from_slot(base, base + slots_per_epoch, slots_per_epoch),
|
||||
Ok(RelativeEpoch::Next)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
21
eth2/types/src/test_utils/builders.rs
Normal file
21
eth2/types/src/test_utils/builders.rs
Normal 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::*;
|
@ -16,8 +16,8 @@ impl TestingAttestationDataBuilder {
|
||||
slot: Slot,
|
||||
spec: &ChainSpec,
|
||||
) -> Self {
|
||||
let current_epoch = state.current_epoch(spec);
|
||||
let previous_epoch = state.previous_epoch(spec);
|
||||
let current_epoch = state.current_epoch();
|
||||
let previous_epoch = state.previous_epoch();
|
||||
|
||||
let is_previous_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 {
|
||||
state.previous_epoch(spec)
|
||||
state.previous_epoch()
|
||||
} else {
|
||||
state.current_epoch(spec)
|
||||
state.current_epoch()
|
||||
};
|
||||
|
||||
let target_root = if is_previous_epoch {
|
@ -1,4 +1,4 @@
|
||||
use super::{generate_deterministic_keypairs, KeypairsFile};
|
||||
use super::super::{generate_deterministic_keypairs, KeypairsFile};
|
||||
use crate::test_utils::TestingPendingAttestationBuilder;
|
||||
use crate::*;
|
||||
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
|
||||
/// invalidate this cache.
|
||||
pub fn build_caches(&mut self, spec: &ChainSpec) -> Result<(), BeaconStateError> {
|
||||
let state = &mut self.state;
|
||||
|
||||
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()?;
|
||||
self.state.build_all_caches(spec);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -218,8 +211,8 @@ impl<T: EthSpec> TestingBeaconStateBuilder<T> {
|
||||
.build_epoch_cache(RelativeEpoch::Current, spec)
|
||||
.unwrap();
|
||||
|
||||
let current_epoch = state.current_epoch(spec);
|
||||
let previous_epoch = state.previous_epoch(spec);
|
||||
let current_epoch = state.current_epoch();
|
||||
let previous_epoch = state.previous_epoch();
|
||||
|
||||
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()
|
||||
@ -246,7 +239,7 @@ impl<T: EthSpec> TestingBeaconStateBuilder<T> {
|
||||
builder.add_committee_participation(signers);
|
||||
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)
|
||||
} else {
|
||||
state.current_epoch_attestations.push(attestation)
|
@ -1,20 +1,12 @@
|
||||
#[macro_use]
|
||||
mod macros;
|
||||
mod builders;
|
||||
mod generate_deterministic_keypairs;
|
||||
mod keypairs_file;
|
||||
mod serde_utils;
|
||||
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_keypairs;
|
||||
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 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;
|
||||
|
Loading…
Reference in New Issue
Block a user