spec: v0.6.1 scaffold for updated epoch cache
This commit is contained in:
parent
137afa9131
commit
0885d56b36
@ -47,8 +47,8 @@ pub enum Error {
|
||||
cache_len: usize,
|
||||
registry_len: usize,
|
||||
},
|
||||
EpochCacheUninitialized(RelativeEpoch),
|
||||
RelativeEpochError(RelativeEpochError),
|
||||
PreviousEpochCacheUninitialized,
|
||||
CurrentEpochCacheUnintialized,
|
||||
EpochCacheError(EpochCacheError),
|
||||
TreeHashCacheError(TreeHashCacheError),
|
||||
}
|
||||
@ -117,13 +117,13 @@ where
|
||||
#[ssz(skip_deserializing)]
|
||||
#[tree_hash(skip_hashing)]
|
||||
#[test_random(default)]
|
||||
pub cache_index_offset: usize,
|
||||
pub previous_epoch_cache: EpochCache,
|
||||
#[serde(default)]
|
||||
#[ssz(skip_serializing)]
|
||||
#[ssz(skip_deserializing)]
|
||||
#[tree_hash(skip_hashing)]
|
||||
#[test_random(default)]
|
||||
pub caches: [EpochCache; CACHED_EPOCHS],
|
||||
pub current_epoch_cache: EpochCache,
|
||||
#[serde(default)]
|
||||
#[ssz(skip_serializing)]
|
||||
#[ssz(skip_deserializing)]
|
||||
@ -214,13 +214,8 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
/*
|
||||
* Caching (not in spec)
|
||||
*/
|
||||
cache_index_offset: 0,
|
||||
caches: [
|
||||
EpochCache::default(),
|
||||
EpochCache::default(),
|
||||
EpochCache::default(),
|
||||
EpochCache::default(),
|
||||
],
|
||||
previous_epoch_cache: EpochCache::default(),
|
||||
current_epoch_cache: EpochCache::default(),
|
||||
pubkey_cache: PubkeyCache::default(),
|
||||
tree_hash_cache: TreeHashCache::default(),
|
||||
exit_cache: ExitCache::default(),
|
||||
@ -338,24 +333,17 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
+ offset / (committee_count / spec.slots_per_epoch))
|
||||
}
|
||||
|
||||
/// Returns the active validator indices for the given epoch, assuming there is no validator
|
||||
/// registry update in the next epoch.
|
||||
///
|
||||
/// This uses the cache, so it saves an iteration over the validator registry, however it can
|
||||
/// not return a result for any epoch before the previous epoch.
|
||||
///
|
||||
/// Note: Utilizes the cache and will fail if the appropriate cache is not initialized.
|
||||
///
|
||||
/// Spec v0.5.1
|
||||
// FIXME(sproul): get_cached_current_active_validator_indices
|
||||
/*
|
||||
pub fn get_cached_active_validator_indices(
|
||||
&self,
|
||||
relative_epoch: RelativeEpoch,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<&[usize], Error> {
|
||||
let cache = self.cache(relative_epoch, spec)?;
|
||||
|
||||
Ok(&cache.active_validator_indices)
|
||||
}
|
||||
*/
|
||||
|
||||
/// Returns the active validator indices for the given epoch.
|
||||
///
|
||||
@ -376,22 +364,14 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
slot: Slot,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<&Vec<CrosslinkCommittee>, Error> {
|
||||
// If the slot is in the next epoch, assume there was no validator registry update.
|
||||
let relative_epoch = match RelativeEpoch::from_slot(self.slot, slot, spec) {
|
||||
Err(RelativeEpochError::AmbiguiousNextEpoch) => {
|
||||
Ok(RelativeEpoch::NextWithoutRegistryChange)
|
||||
}
|
||||
e => e,
|
||||
}?;
|
||||
|
||||
let cache = self.cache(relative_epoch, spec)?;
|
||||
|
||||
Ok(cache
|
||||
.get_crosslink_committees_at_slot(slot, spec)
|
||||
.ok_or_else(|| Error::SlotOutOfBounds)?)
|
||||
unimplemented!("FIXME(sproul)")
|
||||
}
|
||||
|
||||
// FIXME(sproul): implement this
|
||||
/// Return the crosslink committeee for `shard` in `epoch`.
|
||||
///
|
||||
/// Note: Utilizes the cache and will fail if the appropriate cache is not initialized.
|
||||
///
|
||||
/// Spec v0.6.1
|
||||
pub fn get_crosslink_committee(
|
||||
&self,
|
||||
epoch: Epoch,
|
||||
@ -402,43 +382,14 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
/// Returns the crosslink committees for some shard in an epoch.
|
||||
///
|
||||
/// Note: Utilizes the cache and will fail if the appropriate cache is not initialized.
|
||||
///
|
||||
/// Spec v0.5.1
|
||||
pub fn get_crosslink_committee_for_shard(
|
||||
&self,
|
||||
epoch: Epoch,
|
||||
shard: Shard,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<&CrosslinkCommittee, Error> {
|
||||
// If the slot is in the next epoch, assume there was no validator registry update.
|
||||
let relative_epoch = match RelativeEpoch::from_epoch(self.current_epoch(spec), epoch) {
|
||||
Err(RelativeEpochError::AmbiguiousNextEpoch) => {
|
||||
Ok(RelativeEpoch::NextWithoutRegistryChange)
|
||||
}
|
||||
e => e,
|
||||
}?;
|
||||
|
||||
let cache = self.cache(relative_epoch, spec)?;
|
||||
|
||||
Ok(cache
|
||||
.get_crosslink_committee_for_shard(shard, spec)
|
||||
.ok_or_else(|| Error::NoCommitteeForShard)?)
|
||||
}
|
||||
|
||||
/// Returns the beacon proposer index for the `slot`.
|
||||
///
|
||||
/// If the state does not contain an index for a beacon proposer at the requested `slot`, then `None` is returned.
|
||||
///
|
||||
/// Spec v0.5.1
|
||||
pub fn get_beacon_proposer_index(
|
||||
&self,
|
||||
slot: Slot,
|
||||
relative_epoch: RelativeEpoch,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<usize, Error> {
|
||||
pub fn get_beacon_proposer_index(&self, spec: &ChainSpec) -> Result<usize, Error> {
|
||||
unimplemented!("FIXME(sproul)")
|
||||
/*
|
||||
let cache = self.cache(relative_epoch, spec)?;
|
||||
|
||||
let committees = cache
|
||||
@ -457,6 +408,7 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
.ok_or(Error::UnableToDetermineProducer)?;
|
||||
Ok(first.committee[index])
|
||||
})
|
||||
*/
|
||||
}
|
||||
|
||||
/// Safely obtains the index for latest block roots, given some `slot`.
|
||||
@ -791,10 +743,8 @@ 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_epoch_cache(RelativeEpoch::Previous, spec)?;
|
||||
self.build_epoch_cache(RelativeEpoch::Current, spec)?;
|
||||
self.build_epoch_cache(RelativeEpoch::NextWithoutRegistryChange, spec)?;
|
||||
self.build_epoch_cache(RelativeEpoch::NextWithRegistryChange, spec)?;
|
||||
self.build_previous_epoch_cache(spec)?;
|
||||
self.build_current_epoch_cache(spec)?;
|
||||
self.update_pubkey_cache()?;
|
||||
self.update_tree_hash_cache()?;
|
||||
self.exit_cache
|
||||
@ -804,13 +754,7 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
}
|
||||
|
||||
/// Build an epoch cache, unless it is has already been built.
|
||||
pub fn build_epoch_cache(
|
||||
&mut self,
|
||||
relative_epoch: RelativeEpoch,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<(), Error> {
|
||||
let cache_index = self.cache_index(relative_epoch);
|
||||
|
||||
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))
|
||||
{
|
||||
Ok(())
|
||||
@ -819,67 +763,42 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Always builds an epoch cache, even if it is already initialized.
|
||||
pub fn force_build_epoch_cache(
|
||||
&mut self,
|
||||
relative_epoch: RelativeEpoch,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<(), Error> {
|
||||
let cache_index = self.cache_index(relative_epoch);
|
||||
|
||||
self.caches[cache_index] = EpochCache::initialized(&self, relative_epoch, spec)?;
|
||||
/// 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(())
|
||||
}
|
||||
|
||||
/// 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,
|
||||
self.generate_seed(epoch, spec)?,
|
||||
self.get_epoch_start_shard(epoch, spec)?,
|
||||
spec,
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 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.
|
||||
///
|
||||
/// The `Next` cache becomes the `Current` and the `Current` cache becomes the `Previous`. The
|
||||
/// `Previous` cache is abandoned.
|
||||
///
|
||||
/// Care should be taken to update the `Current` epoch in case a registry update is performed
|
||||
/// -- `Next` epoch is always _without_ a registry change. If you perform a registry update,
|
||||
/// you should rebuild the `Current` cache so it uses the new seed.
|
||||
pub fn advance_caches(&mut self) {
|
||||
self.drop_cache(RelativeEpoch::Previous);
|
||||
|
||||
self.cache_index_offset += 1;
|
||||
self.cache_index_offset %= CACHED_EPOCHS;
|
||||
self.previous_epoch_cache =
|
||||
std::mem::replace(&mut self.current_epoch_cache, EpochCache::default());
|
||||
self.force_build_current_epoch_cache();
|
||||
}
|
||||
|
||||
/// Removes the specified cache and sets it to uninitialized.
|
||||
pub fn drop_cache(&mut self, relative_epoch: RelativeEpoch) {
|
||||
let previous_cache_index = self.cache_index(relative_epoch);
|
||||
self.caches[previous_cache_index] = EpochCache::default();
|
||||
}
|
||||
|
||||
/// Returns the index of `self.caches` for some `RelativeEpoch`.
|
||||
fn cache_index(&self, relative_epoch: RelativeEpoch) -> usize {
|
||||
let base_index = match relative_epoch {
|
||||
RelativeEpoch::Previous => 0,
|
||||
RelativeEpoch::Current => 1,
|
||||
RelativeEpoch::NextWithoutRegistryChange => 2,
|
||||
RelativeEpoch::NextWithRegistryChange => 3,
|
||||
};
|
||||
|
||||
(base_index + self.cache_index_offset) % CACHED_EPOCHS
|
||||
}
|
||||
|
||||
/// Returns the cache for some `RelativeEpoch`. Returns an error if the cache has not been
|
||||
/// initialized.
|
||||
fn cache(&self, relative_epoch: RelativeEpoch, spec: &ChainSpec) -> Result<&EpochCache, Error> {
|
||||
let cache = &self.caches[self.cache_index(relative_epoch)];
|
||||
|
||||
let epoch = relative_epoch.into_epoch(self.slot.epoch(spec.slots_per_epoch));
|
||||
|
||||
if cache.initialized_epoch == Some(epoch) {
|
||||
Ok(cache)
|
||||
} else {
|
||||
Err(Error::EpochCacheUninitialized(relative_epoch))
|
||||
}
|
||||
}
|
||||
// FIXME(sproul): drop_previous/current_epoch_cache
|
||||
|
||||
/// Updates the pubkey cache, if required.
|
||||
///
|
||||
@ -940,12 +859,6 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RelativeEpochError> for Error {
|
||||
fn from(e: RelativeEpochError) -> Error {
|
||||
Error::RelativeEpochError(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<EpochCacheError> for Error {
|
||||
fn from(e: EpochCacheError) -> Error {
|
||||
Error::EpochCacheError(e)
|
||||
|
@ -6,6 +6,7 @@ use swap_or_not_shuffle::shuffle_list;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum Error {
|
||||
EpochOutOfBounds,
|
||||
UnableToShuffle,
|
||||
UnableToGenerateSeed,
|
||||
}
|
||||
@ -20,8 +21,6 @@ pub struct EpochCache {
|
||||
pub epoch_crosslink_committees: EpochCrosslinkCommittees,
|
||||
/// Maps validator index to a slot, shard and committee index for attestation.
|
||||
pub attestation_duties: Vec<Option<AttestationDuty>>,
|
||||
/// Maps a shard to an index of `self.committees`.
|
||||
pub shard_committee_indices: Vec<Option<(Slot, usize)>>,
|
||||
/// Indices of all active validators in the epoch
|
||||
pub active_validator_indices: Vec<usize>,
|
||||
}
|
||||
@ -30,72 +29,42 @@ impl EpochCache {
|
||||
/// Return a new, fully initialized cache.
|
||||
pub fn initialized<T: EthSpec>(
|
||||
state: &BeaconState<T>,
|
||||
relative_epoch: RelativeEpoch,
|
||||
epoch: Epoch,
|
||||
seed: Hash256,
|
||||
epoch_start_shard: u64,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<EpochCache, Error> {
|
||||
let epoch = relative_epoch.into_epoch(state.slot.epoch(spec.slots_per_epoch));
|
||||
if epoch != state.previous_epoch(spec) && epoch != state.current_epoch(spec) {
|
||||
return Err(Error::EpochOutOfBounds);
|
||||
}
|
||||
|
||||
let active_validator_indices =
|
||||
get_active_validator_indices(&state.validator_registry, epoch);
|
||||
|
||||
let builder = match relative_epoch {
|
||||
RelativeEpoch::Previous => EpochCrosslinkCommitteesBuilder::for_previous_epoch(
|
||||
state,
|
||||
active_validator_indices.clone(),
|
||||
spec,
|
||||
),
|
||||
RelativeEpoch::Current => EpochCrosslinkCommitteesBuilder::for_current_epoch(
|
||||
state,
|
||||
active_validator_indices.clone(),
|
||||
spec,
|
||||
),
|
||||
RelativeEpoch::NextWithRegistryChange => {
|
||||
EpochCrosslinkCommitteesBuilder::for_next_epoch(
|
||||
state,
|
||||
active_validator_indices.clone(),
|
||||
true,
|
||||
spec,
|
||||
)?
|
||||
}
|
||||
RelativeEpoch::NextWithoutRegistryChange => {
|
||||
EpochCrosslinkCommitteesBuilder::for_next_epoch(
|
||||
state,
|
||||
active_validator_indices.clone(),
|
||||
false,
|
||||
spec,
|
||||
)?
|
||||
}
|
||||
};
|
||||
let epoch_crosslink_committees = builder.build(spec)?;
|
||||
let epoch_crosslink_committees = EpochCrosslinkCommittees::new(
|
||||
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 maps:
|
||||
// Loop through all the validators in the committees and create the following map:
|
||||
//
|
||||
// 1. `attestation_duties`: maps `ValidatorIndex` to `AttestationDuty`.
|
||||
// 2. `shard_committee_indices`: maps `Shard` into a `CrosslinkCommittee` in
|
||||
// `EpochCrosslinkCommittees`.
|
||||
// `attestation_duties`: maps `ValidatorIndex` to `AttestationDuty`.
|
||||
let mut attestation_duties = vec![None; state.validator_registry.len()];
|
||||
let mut shard_committee_indices = vec![None; spec.shard_count as usize];
|
||||
for (i, slot_committees) in epoch_crosslink_committees
|
||||
.crosslink_committees
|
||||
.iter()
|
||||
.enumerate()
|
||||
{
|
||||
let slot = epoch.start_slot(spec.slots_per_epoch) + i as u64;
|
||||
|
||||
for (j, crosslink_committee) in slot_committees.iter().enumerate() {
|
||||
let shard = crosslink_committee.shard;
|
||||
|
||||
shard_committee_indices[shard as usize] = Some((slot, j));
|
||||
|
||||
for (k, validator_index) in crosslink_committee.committee.iter().enumerate() {
|
||||
let attestation_duty = AttestationDuty {
|
||||
slot,
|
||||
shard,
|
||||
committee_index: k,
|
||||
committee_len: crosslink_committee.committee.len(),
|
||||
};
|
||||
attestation_duties[*validator_index] = Some(attestation_duty)
|
||||
}
|
||||
for crosslink_committee in epoch_crosslink_committees.crosslink_committees.iter() {
|
||||
for (committee_index, validator_index) in
|
||||
crosslink_committee.committee.iter().enumerate()
|
||||
{
|
||||
let attestation_duty = AttestationDuty {
|
||||
slot: crosslink_committee.slot,
|
||||
shard: crosslink_committee.shard,
|
||||
committee_index,
|
||||
committee_len: crosslink_committee.committee.len(),
|
||||
};
|
||||
attestation_duties[*validator_index] = Some(attestation_duty);
|
||||
}
|
||||
}
|
||||
|
||||
@ -103,7 +72,6 @@ impl EpochCache {
|
||||
initialized_epoch: Some(epoch),
|
||||
epoch_crosslink_committees,
|
||||
attestation_duties,
|
||||
shard_committee_indices,
|
||||
active_validator_indices,
|
||||
})
|
||||
}
|
||||
@ -138,7 +106,7 @@ impl EpochCache {
|
||||
/// Returns a list of all `validator_registry` indices where the validator is active at the given
|
||||
/// `epoch`.
|
||||
///
|
||||
/// Spec v0.5.1
|
||||
/// Spec v0.6.1
|
||||
pub fn get_active_validator_indices(validators: &[Validator], epoch: Epoch) -> Vec<usize> {
|
||||
let mut active = Vec::with_capacity(validators.len());
|
||||
|
||||
@ -158,17 +126,76 @@ pub fn get_active_validator_indices(validators: &[Validator], epoch: Epoch) -> V
|
||||
pub struct EpochCrosslinkCommittees {
|
||||
/// The epoch the committees are present in.
|
||||
epoch: Epoch,
|
||||
/// Each commitee for each slot of the epoch.
|
||||
pub crosslink_committees: Vec<Vec<CrosslinkCommittee>>,
|
||||
/// 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>,
|
||||
}
|
||||
|
||||
fn crosslink_committee_slot(
|
||||
shard: u64,
|
||||
epoch: Epoch,
|
||||
epoch_start_shard: u64,
|
||||
epoch_committee_count: u64,
|
||||
spec: &ChainSpec,
|
||||
) -> Slot {
|
||||
// Excerpt from `get_attestation_slot` in the spec.
|
||||
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 {
|
||||
/// Return a new instances where all slots have zero committees.
|
||||
fn new(epoch: Epoch, spec: &ChainSpec) -> Self {
|
||||
Self {
|
||||
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: vec![vec![]; spec.slots_per_epoch as usize],
|
||||
}
|
||||
crosslink_committees,
|
||||
})
|
||||
}
|
||||
|
||||
/// Return a vec of `CrosslinkCommittee` for a given slot.
|
||||
@ -188,145 +215,3 @@ impl EpochCrosslinkCommittees {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Builds an `EpochCrosslinkCommittees` object.
|
||||
pub struct EpochCrosslinkCommitteesBuilder {
|
||||
epoch: Epoch,
|
||||
shuffling_start_shard: Shard,
|
||||
shuffling_seed: Hash256,
|
||||
active_validator_indices: Vec<usize>,
|
||||
committees_per_epoch: u64,
|
||||
}
|
||||
|
||||
impl EpochCrosslinkCommitteesBuilder {
|
||||
/// Instantiates a builder that will build for the `state`'s previous epoch.
|
||||
pub fn for_previous_epoch<T: EthSpec>(
|
||||
state: &BeaconState<T>,
|
||||
active_validator_indices: Vec<usize>,
|
||||
spec: &ChainSpec,
|
||||
) -> Self {
|
||||
Self {
|
||||
epoch: state.previous_epoch(spec),
|
||||
// FIXME(sproul)
|
||||
shuffling_start_shard: 0,
|
||||
shuffling_seed: spec.zero_hash,
|
||||
committees_per_epoch: spec.get_epoch_committee_count(active_validator_indices.len()),
|
||||
active_validator_indices,
|
||||
}
|
||||
}
|
||||
|
||||
/// Instantiates a builder that will build for the `state`'s next epoch.
|
||||
pub fn for_current_epoch<T: EthSpec>(
|
||||
state: &BeaconState<T>,
|
||||
active_validator_indices: Vec<usize>,
|
||||
spec: &ChainSpec,
|
||||
) -> Self {
|
||||
Self {
|
||||
epoch: state.current_epoch(spec),
|
||||
// FIXME(sproul)
|
||||
shuffling_start_shard: 0,
|
||||
shuffling_seed: spec.zero_hash,
|
||||
committees_per_epoch: spec.get_epoch_committee_count(active_validator_indices.len()),
|
||||
active_validator_indices,
|
||||
}
|
||||
}
|
||||
|
||||
/// Instantiates a builder that will build for the `state`'s next epoch.
|
||||
///
|
||||
/// Note: there are two possible epoch builds for the next epoch, one where there is a registry
|
||||
/// change and one where there is not.
|
||||
pub fn for_next_epoch<T: EthSpec>(
|
||||
state: &BeaconState<T>,
|
||||
active_validator_indices: Vec<usize>,
|
||||
registry_change: bool,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<Self, Error> {
|
||||
let current_epoch = state.current_epoch(spec);
|
||||
let next_epoch = state.next_epoch(spec);
|
||||
let committees_per_epoch = spec.get_epoch_committee_count(active_validator_indices.len());
|
||||
|
||||
// FIXME(sproul)
|
||||
// current_epoch - state.validator_registry_update_epoch;
|
||||
let epochs_since_last_registry_update = 0u64;
|
||||
|
||||
let (seed, shuffling_start_shard) = if registry_change {
|
||||
let next_seed = state
|
||||
.generate_seed(next_epoch, spec)
|
||||
.map_err(|_| Error::UnableToGenerateSeed)?;
|
||||
(
|
||||
next_seed,
|
||||
0,
|
||||
// FIXME(sproul)
|
||||
// (state.current_shuffling_start_shard + committees_per_epoch) % spec.shard_count,
|
||||
)
|
||||
} else if (epochs_since_last_registry_update > 1)
|
||||
& epochs_since_last_registry_update.is_power_of_two()
|
||||
{
|
||||
let next_seed = state
|
||||
.generate_seed(next_epoch, spec)
|
||||
.map_err(|_| Error::UnableToGenerateSeed)?;
|
||||
(
|
||||
next_seed, 0, /* FIXME(sproul) state.current_shuffling_start_shard*/
|
||||
)
|
||||
} else {
|
||||
(
|
||||
spec.zero_hash, // state.current_shuffling_seed,
|
||||
0 // state.current_shuffling_start_shard,
|
||||
)
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
epoch: state.next_epoch(spec),
|
||||
shuffling_start_shard,
|
||||
shuffling_seed: seed,
|
||||
active_validator_indices,
|
||||
committees_per_epoch,
|
||||
})
|
||||
}
|
||||
|
||||
/// Consumes the builder, returning a fully-build `EpochCrosslinkCommittee`.
|
||||
pub fn build(self, spec: &ChainSpec) -> Result<EpochCrosslinkCommittees, Error> {
|
||||
// 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 self.active_validator_indices.is_empty() {
|
||||
vec![]
|
||||
} else {
|
||||
shuffle_list(
|
||||
self.active_validator_indices,
|
||||
spec.shuffle_round_count,
|
||||
&self.shuffling_seed[..],
|
||||
false,
|
||||
)
|
||||
.ok_or_else(|| Error::UnableToShuffle)?
|
||||
};
|
||||
|
||||
let mut committees: Vec<Vec<usize>> = shuffled_active_validator_indices
|
||||
.honey_badger_split(self.committees_per_epoch as usize)
|
||||
.map(|slice: &[usize]| slice.to_vec())
|
||||
.collect();
|
||||
|
||||
let mut epoch_crosslink_committees = EpochCrosslinkCommittees::new(self.epoch, spec);
|
||||
let mut shard = self.shuffling_start_shard;
|
||||
|
||||
let committees_per_slot = (self.committees_per_epoch / spec.slots_per_epoch) as usize;
|
||||
|
||||
for (i, slot) in self.epoch.slot_iter(spec.slots_per_epoch).enumerate() {
|
||||
for j in (0..committees.len())
|
||||
.skip(i * committees_per_slot)
|
||||
.take(committees_per_slot)
|
||||
{
|
||||
let crosslink_committee = CrosslinkCommittee {
|
||||
slot,
|
||||
shard,
|
||||
committee: committees[j].drain(..).collect(),
|
||||
};
|
||||
epoch_crosslink_committees.crosslink_committees[i].push(crosslink_committee);
|
||||
|
||||
shard += 1;
|
||||
shard %= spec.shard_count;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(epoch_crosslink_committees)
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user