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