Refactor shuffling generation
This commit is contained in:
parent
d6456a9486
commit
7f4af20212
9
eth2/types/src/attestation_duty.rs
Normal file
9
eth2/types/src/attestation_duty.rs
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
use crate::*;
|
||||||
|
use serde_derive::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Clone, Default, Serialize, Deserialize)]
|
||||||
|
pub struct AttestationDuty {
|
||||||
|
pub slot: Slot,
|
||||||
|
pub shard: Shard,
|
||||||
|
pub committee_index: usize,
|
||||||
|
}
|
@ -1,17 +1,14 @@
|
|||||||
use self::epoch_cache::EpochCache;
|
use self::epoch_cache::EpochCache;
|
||||||
use crate::test_utils::TestRandom;
|
use crate::test_utils::TestRandom;
|
||||||
use crate::{validator_registry::get_active_validator_indices, *};
|
use crate::{validator_registry::get_active_validator_indices, *};
|
||||||
use helpers::*;
|
|
||||||
use honey_badger_split::SplitExt;
|
|
||||||
use int_to_bytes::int_to_bytes32;
|
use int_to_bytes::int_to_bytes32;
|
||||||
use log::{debug, error, trace};
|
use log::{debug, trace};
|
||||||
use pubkey_cache::PubkeyCache;
|
use pubkey_cache::PubkeyCache;
|
||||||
use rand::RngCore;
|
use rand::RngCore;
|
||||||
use serde_derive::{Deserialize, Serialize};
|
use serde_derive::{Deserialize, Serialize};
|
||||||
use ssz::{hash, SignedRoot};
|
use ssz::{hash, SignedRoot, TreeHash};
|
||||||
use ssz_derive::{Decode, Encode, TreeHash};
|
use ssz_derive::{Decode, Encode, TreeHash};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use swap_or_not_shuffle::shuffle_list;
|
|
||||||
use test_random_derive::TestRandom;
|
use test_random_derive::TestRandom;
|
||||||
|
|
||||||
pub use builder::BeaconStateBuilder;
|
pub use builder::BeaconStateBuilder;
|
||||||
@ -22,22 +19,7 @@ pub mod helpers;
|
|||||||
mod pubkey_cache;
|
mod pubkey_cache;
|
||||||
mod tests;
|
mod tests;
|
||||||
|
|
||||||
pub type Committee = Vec<usize>;
|
pub const CACHED_EPOCHS: usize = 4;
|
||||||
pub type CrosslinkCommittees = Vec<(Committee, u64)>;
|
|
||||||
pub type Shard = u64;
|
|
||||||
pub type CommitteeIndex = u64;
|
|
||||||
pub type AttestationDuty = (Slot, Shard, CommitteeIndex);
|
|
||||||
pub type AttestationDutyMap = HashMap<u64, AttestationDuty>;
|
|
||||||
pub type ShardCommitteeIndexMap = HashMap<Shard, (usize, usize)>;
|
|
||||||
|
|
||||||
pub const CACHED_EPOCHS: usize = 3;
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone, Copy)]
|
|
||||||
pub enum RelativeEpoch {
|
|
||||||
Previous,
|
|
||||||
Current,
|
|
||||||
Next,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
@ -61,6 +43,7 @@ pub enum Error {
|
|||||||
cache_len: usize,
|
cache_len: usize,
|
||||||
registry_len: usize,
|
registry_len: usize,
|
||||||
},
|
},
|
||||||
|
RelativeEpochError(RelativeEpochError),
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! safe_add_assign {
|
macro_rules! safe_add_assign {
|
||||||
@ -212,13 +195,12 @@ impl BeaconState {
|
|||||||
EpochCache::default(),
|
EpochCache::default(),
|
||||||
EpochCache::default(),
|
EpochCache::default(),
|
||||||
EpochCache::default(),
|
EpochCache::default(),
|
||||||
|
EpochCache::default(),
|
||||||
],
|
],
|
||||||
pubkey_cache: PubkeyCache::default(),
|
pubkey_cache: PubkeyCache::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
|
|
||||||
/// Returns the `hash_tree_root` of the state.
|
/// Returns the `hash_tree_root` of the state.
|
||||||
///
|
///
|
||||||
/// Spec v0.5.0
|
/// Spec v0.5.0
|
||||||
@ -226,8 +208,6 @@ impl BeaconState {
|
|||||||
Hash256::from_slice(&self.hash_tree_root()[..])
|
Hash256::from_slice(&self.hash_tree_root()[..])
|
||||||
}
|
}
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
/// 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_epoch_cache(
|
pub fn build_epoch_cache(
|
||||||
&mut self,
|
&mut self,
|
||||||
@ -236,7 +216,8 @@ impl BeaconState {
|
|||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
let cache_index = self.cache_index(relative_epoch);
|
let cache_index = self.cache_index(relative_epoch);
|
||||||
|
|
||||||
if self.caches[cache_index].initialized {
|
if self.caches[cache_index].initialized_epoch == Some(self.slot.epoch(spec.slots_per_epoch))
|
||||||
|
{
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
self.force_build_epoch_cache(relative_epoch, spec)
|
self.force_build_epoch_cache(relative_epoch, spec)
|
||||||
@ -249,36 +230,13 @@ impl BeaconState {
|
|||||||
relative_epoch: RelativeEpoch,
|
relative_epoch: RelativeEpoch,
|
||||||
spec: &ChainSpec,
|
spec: &ChainSpec,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
let epoch = self.absolute_epoch(relative_epoch, spec);
|
|
||||||
let cache_index = self.cache_index(relative_epoch);
|
let cache_index = self.cache_index(relative_epoch);
|
||||||
|
|
||||||
self.caches[cache_index] = EpochCache::initialized(&self, epoch, spec)?;
|
self.caches[cache_index] = EpochCache::initialized(&self, relative_epoch, spec)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Converts a `RelativeEpoch` into an `Epoch` with respect to the epoch of this state.
|
|
||||||
fn absolute_epoch(&self, relative_epoch: RelativeEpoch, spec: &ChainSpec) -> Epoch {
|
|
||||||
match relative_epoch {
|
|
||||||
RelativeEpoch::Previous => self.previous_epoch(spec),
|
|
||||||
RelativeEpoch::Current => self.current_epoch(spec),
|
|
||||||
RelativeEpoch::Next => self.next_epoch(spec),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Converts an `Epoch` into a `RelativeEpoch` with respect to the epoch of this state.
|
|
||||||
///
|
|
||||||
/// Returns an error if the given `epoch` not "previous", "current" or "next" compared to the
|
|
||||||
/// epoch of this tate.
|
|
||||||
fn relative_epoch(&self, epoch: Epoch, spec: &ChainSpec) -> Result<RelativeEpoch, Error> {
|
|
||||||
match epoch {
|
|
||||||
e if e == self.current_epoch(spec) => Ok(RelativeEpoch::Current),
|
|
||||||
e if e == self.previous_epoch(spec) => Ok(RelativeEpoch::Previous),
|
|
||||||
e if e == self.next_epoch(spec) => Ok(RelativeEpoch::Next),
|
|
||||||
_ => Err(Error::EpochOutOfBounds),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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.
|
||||||
@ -305,9 +263,10 @@ impl BeaconState {
|
|||||||
/// Returns the index of `self.caches` for some `RelativeEpoch`.
|
/// Returns the index of `self.caches` for some `RelativeEpoch`.
|
||||||
fn cache_index(&self, relative_epoch: RelativeEpoch) -> usize {
|
fn cache_index(&self, relative_epoch: RelativeEpoch) -> usize {
|
||||||
let base_index = match relative_epoch {
|
let base_index = match relative_epoch {
|
||||||
RelativeEpoch::Current => 1,
|
|
||||||
RelativeEpoch::Previous => 0,
|
RelativeEpoch::Previous => 0,
|
||||||
RelativeEpoch::Next => 2,
|
RelativeEpoch::Current => 1,
|
||||||
|
RelativeEpoch::NextWithoutRegistryChange => 2,
|
||||||
|
RelativeEpoch::NextWithRegistryChange => 3,
|
||||||
};
|
};
|
||||||
|
|
||||||
(base_index + self.cache_index_offset) % CACHED_EPOCHS
|
(base_index + self.cache_index_offset) % CACHED_EPOCHS
|
||||||
@ -315,10 +274,10 @@ impl BeaconState {
|
|||||||
|
|
||||||
/// Returns the cache for some `RelativeEpoch`. Returns an error if the cache has not been
|
/// Returns the cache for some `RelativeEpoch`. Returns an error if the cache has not been
|
||||||
/// initialized.
|
/// initialized.
|
||||||
fn cache(&self, relative_epoch: RelativeEpoch) -> Result<&EpochCache, Error> {
|
fn cache(&self, relative_epoch: RelativeEpoch, spec: &ChainSpec) -> Result<&EpochCache, Error> {
|
||||||
let cache = &self.caches[self.cache_index(relative_epoch)];
|
let cache = &self.caches[self.cache_index(relative_epoch)];
|
||||||
|
|
||||||
if cache.initialized {
|
if cache.initialized_epoch == Some(self.slot.epoch(spec.slots_per_epoch)) {
|
||||||
Ok(cache)
|
Ok(cache)
|
||||||
} else {
|
} else {
|
||||||
Err(Error::EpochCacheUninitialized(relative_epoch))
|
Err(Error::EpochCacheUninitialized(relative_epoch))
|
||||||
@ -367,7 +326,7 @@ impl BeaconState {
|
|||||||
|
|
||||||
/// The epoch corresponding to `self.slot`.
|
/// The epoch corresponding to `self.slot`.
|
||||||
///
|
///
|
||||||
/// Spec v0.4.0
|
/// Spec v0.5.0
|
||||||
pub fn current_epoch(&self, spec: &ChainSpec) -> Epoch {
|
pub fn current_epoch(&self, spec: &ChainSpec) -> Epoch {
|
||||||
self.slot.epoch(spec.slots_per_epoch)
|
self.slot.epoch(spec.slots_per_epoch)
|
||||||
}
|
}
|
||||||
@ -376,58 +335,16 @@ impl BeaconState {
|
|||||||
///
|
///
|
||||||
/// 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.4.0
|
/// Spec v0.5.0
|
||||||
pub fn previous_epoch(&self, spec: &ChainSpec) -> Epoch {
|
pub fn previous_epoch(&self, spec: &ChainSpec) -> Epoch {
|
||||||
let current_epoch = self.current_epoch(&spec);
|
self.current_epoch(&spec) - 1
|
||||||
std::cmp::max(current_epoch - 1, spec.genesis_epoch)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The epoch following `self.current_epoch()`.
|
/// The epoch following `self.current_epoch()`.
|
||||||
///
|
///
|
||||||
/// Spec v0.4.0
|
/// Spec v0.5.0
|
||||||
pub fn next_epoch(&self, spec: &ChainSpec) -> Epoch {
|
pub fn next_epoch(&self, spec: &ChainSpec) -> Epoch {
|
||||||
self.current_epoch(spec).saturating_add(1_u64)
|
self.current_epoch(spec) + 1
|
||||||
}
|
|
||||||
|
|
||||||
/// The first slot of the epoch corresponding to `self.slot`.
|
|
||||||
///
|
|
||||||
/// Spec v0.4.0
|
|
||||||
pub fn current_epoch_start_slot(&self, spec: &ChainSpec) -> Slot {
|
|
||||||
self.current_epoch(spec).start_slot(spec.slots_per_epoch)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The first slot of the epoch preceding the one corresponding to `self.slot`.
|
|
||||||
///
|
|
||||||
/// Spec v0.4.0
|
|
||||||
pub fn previous_epoch_start_slot(&self, spec: &ChainSpec) -> Slot {
|
|
||||||
self.previous_epoch(spec).start_slot(spec.slots_per_epoch)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return the number of committees in the previous epoch.
|
|
||||||
///
|
|
||||||
/// Spec v0.4.0
|
|
||||||
fn get_previous_epoch_committee_count(&self, spec: &ChainSpec) -> u64 {
|
|
||||||
let previous_active_validators =
|
|
||||||
get_active_validator_indices(&self.validator_registry, self.previous_shuffling_epoch);
|
|
||||||
spec.get_epoch_committee_count(previous_active_validators.len())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return the number of committees in the current epoch.
|
|
||||||
///
|
|
||||||
/// Spec v0.4.0
|
|
||||||
pub fn get_current_epoch_committee_count(&self, spec: &ChainSpec) -> u64 {
|
|
||||||
let current_active_validators =
|
|
||||||
get_active_validator_indices(&self.validator_registry, self.current_shuffling_epoch);
|
|
||||||
spec.get_epoch_committee_count(current_active_validators.len())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return the number of committees in the next epoch.
|
|
||||||
///
|
|
||||||
/// Spec v0.4.0
|
|
||||||
pub fn get_next_epoch_committee_count(&self, spec: &ChainSpec) -> u64 {
|
|
||||||
let next_active_validators =
|
|
||||||
get_active_validator_indices(&self.validator_registry, self.next_epoch(spec));
|
|
||||||
spec.get_epoch_committee_count(next_active_validators.len())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the crosslink committees for some slot.
|
/// Returns the crosslink committees for some slot.
|
||||||
@ -438,15 +355,14 @@ impl BeaconState {
|
|||||||
pub fn get_crosslink_committees_at_slot(
|
pub fn get_crosslink_committees_at_slot(
|
||||||
&self,
|
&self,
|
||||||
slot: Slot,
|
slot: Slot,
|
||||||
|
relative_epoch: RelativeEpoch,
|
||||||
spec: &ChainSpec,
|
spec: &ChainSpec,
|
||||||
) -> Result<&CrosslinkCommittees, Error> {
|
) -> Result<&Vec<CrosslinkCommittee>, Error> {
|
||||||
let epoch = slot.epoch(spec.slots_per_epoch);
|
let cache = self.cache(relative_epoch, spec)?;
|
||||||
let relative_epoch = self.relative_epoch(epoch, spec)?;
|
|
||||||
let cache = self.cache(relative_epoch)?;
|
|
||||||
|
|
||||||
let slot_offset = slot - epoch.start_slot(spec.slots_per_epoch);
|
Ok(cache
|
||||||
|
.get_crosslink_committees_at_slot(slot, spec)
|
||||||
Ok(&cache.committees[slot_offset.as_usize()])
|
.ok_or_else(|| Error::SlotOutOfBounds)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the block root at a recent `slot`.
|
/// Return the block root at a recent `slot`.
|
||||||
@ -525,8 +441,13 @@ impl BeaconState {
|
|||||||
/// If the state does not contain an index for a beacon proposer at the requested `slot`, then `None` is returned.
|
/// If the state does not contain an index for a beacon proposer at the requested `slot`, then `None` is returned.
|
||||||
///
|
///
|
||||||
/// Spec v0.4.0
|
/// Spec v0.4.0
|
||||||
pub fn get_beacon_proposer_index(&self, slot: Slot, spec: &ChainSpec) -> Result<usize, Error> {
|
pub fn get_beacon_proposer_index(
|
||||||
let committees = self.get_crosslink_committees_at_slot(slot, spec)?;
|
&self,
|
||||||
|
slot: Slot,
|
||||||
|
relative_epoch: RelativeEpoch,
|
||||||
|
spec: &ChainSpec,
|
||||||
|
) -> Result<usize, Error> {
|
||||||
|
let committees = self.get_crosslink_committees_at_slot(slot, relative_epoch, spec)?;
|
||||||
trace!(
|
trace!(
|
||||||
"get_beacon_proposer_index: slot: {}, committees_count: {}",
|
"get_beacon_proposer_index: slot: {}, committees_count: {}",
|
||||||
slot,
|
slot,
|
||||||
@ -535,71 +456,28 @@ impl BeaconState {
|
|||||||
committees
|
committees
|
||||||
.first()
|
.first()
|
||||||
.ok_or(Error::InsufficientValidators)
|
.ok_or(Error::InsufficientValidators)
|
||||||
.and_then(|(first_committee, _)| {
|
.and_then(|first| {
|
||||||
let index = slot
|
let index = slot
|
||||||
.as_usize()
|
.as_usize()
|
||||||
.checked_rem(first_committee.len())
|
.checked_rem(first.committee.len())
|
||||||
.ok_or(Error::InsufficientValidators)?;
|
.ok_or(Error::InsufficientValidators)?;
|
||||||
Ok(first_committee[index])
|
Ok(first.committee[index])
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the list of validator indices which participiated in the attestation.
|
|
||||||
///
|
|
||||||
/// Note: Utilizes the cache and will fail if the appropriate cache is not initialized.
|
|
||||||
///
|
|
||||||
/// Spec v0.4.0
|
|
||||||
pub fn get_attestation_participants(
|
|
||||||
&self,
|
|
||||||
attestation_data: &AttestationData,
|
|
||||||
bitfield: &Bitfield,
|
|
||||||
spec: &ChainSpec,
|
|
||||||
) -> Result<Vec<usize>, Error> {
|
|
||||||
let epoch = attestation_data.slot.epoch(spec.slots_per_epoch);
|
|
||||||
let relative_epoch = self.relative_epoch(epoch, spec)?;
|
|
||||||
let cache = self.cache(relative_epoch)?;
|
|
||||||
|
|
||||||
let (committee_slot_index, committee_index) = cache
|
|
||||||
.shard_committee_indices
|
|
||||||
.get(attestation_data.shard as usize)
|
|
||||||
.ok_or_else(|| Error::ShardOutOfBounds)?;
|
|
||||||
let (committee, shard) = &cache.committees[*committee_slot_index][*committee_index];
|
|
||||||
|
|
||||||
assert_eq!(*shard, attestation_data.shard, "Bad epoch cache build.");
|
|
||||||
|
|
||||||
if !verify_bitfield_length(&bitfield, committee.len()) {
|
|
||||||
return Err(Error::InvalidBitfield);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut participants = Vec::with_capacity(committee.len());
|
|
||||||
for (i, validator_index) in committee.iter().enumerate() {
|
|
||||||
match bitfield.get(i) {
|
|
||||||
Ok(bit) if bit == true => participants.push(*validator_index),
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
participants.shrink_to_fit();
|
|
||||||
|
|
||||||
Ok(participants)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return the effective balance (also known as "balance at stake") for a validator with the given ``index``.
|
/// Return the effective balance (also known as "balance at stake") for a validator with the given ``index``.
|
||||||
///
|
///
|
||||||
/// Spec v0.4.0
|
/// Spec v0.4.0
|
||||||
pub fn get_effective_balance(&self, validator_index: usize, spec: &ChainSpec) -> u64 {
|
pub fn get_effective_balance(
|
||||||
std::cmp::min(
|
&self,
|
||||||
self.validator_balances[validator_index],
|
validator_index: usize,
|
||||||
spec.max_deposit_amount,
|
spec: &ChainSpec,
|
||||||
)
|
) -> Result<u64, Error> {
|
||||||
}
|
let balance = self
|
||||||
|
.validator_balances
|
||||||
/// Return the combined effective balance of an array of validators.
|
.get(validator_index)
|
||||||
///
|
.ok_or_else(|| Error::UnknownValidator)?;
|
||||||
/// Spec v0.4.0
|
Ok(std::cmp::min(*balance, spec.max_deposit_amount))
|
||||||
pub fn get_total_balance(&self, validator_indices: &[usize], spec: &ChainSpec) -> u64 {
|
|
||||||
validator_indices
|
|
||||||
.iter()
|
|
||||||
.fold(0, |acc, i| acc + self.get_effective_balance(*i, spec))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the epoch at which an activation or exit triggered in ``epoch`` takes effect.
|
/// Return the epoch at which an activation or exit triggered in ``epoch`` takes effect.
|
||||||
@ -767,11 +645,15 @@ impl BeaconState {
|
|||||||
|
|
||||||
self.exit_validator(validator_index, spec);
|
self.exit_validator(validator_index, spec);
|
||||||
|
|
||||||
self.latest_slashed_balances[current_epoch.as_usize() % spec.latest_slashed_exit_length] +=
|
let effective_balance = self.get_effective_balance(validator_index, spec)?;
|
||||||
self.get_effective_balance(validator_index, spec);
|
|
||||||
|
|
||||||
let whistleblower_index = self.get_beacon_proposer_index(self.slot, spec)?;
|
self.latest_slashed_balances[current_epoch.as_usize() % spec.latest_slashed_exit_length] +=
|
||||||
let whistleblower_reward = self.get_effective_balance(validator_index, spec);
|
effective_balance;
|
||||||
|
|
||||||
|
let whistleblower_index =
|
||||||
|
self.get_beacon_proposer_index(self.slot, RelativeEpoch::Current, spec)?;
|
||||||
|
|
||||||
|
let whistleblower_reward = effective_balance;
|
||||||
safe_add_assign!(
|
safe_add_assign!(
|
||||||
self.validator_balances[whistleblower_index as usize],
|
self.validator_balances[whistleblower_index as usize],
|
||||||
whistleblower_reward
|
whistleblower_reward
|
||||||
@ -801,166 +683,6 @@ impl BeaconState {
|
|||||||
self.current_epoch(spec) + spec.min_validator_withdrawability_delay;
|
self.current_epoch(spec) + spec.min_validator_withdrawability_delay;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the crosslink committees for some slot.
|
|
||||||
///
|
|
||||||
/// Utilizes the cache and will fail if the appropriate cache is not initialized.
|
|
||||||
///
|
|
||||||
/// Spec v0.4.0
|
|
||||||
pub(crate) fn get_shuffling_for_slot(
|
|
||||||
&self,
|
|
||||||
slot: Slot,
|
|
||||||
registry_change: bool,
|
|
||||||
spec: &ChainSpec,
|
|
||||||
) -> Result<Vec<Vec<usize>>, Error> {
|
|
||||||
let (_committees_per_epoch, seed, shuffling_epoch, _shuffling_start_shard) =
|
|
||||||
self.get_committee_params_at_slot(slot, registry_change, spec)?;
|
|
||||||
|
|
||||||
self.get_shuffling(seed, shuffling_epoch, spec)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Shuffle ``validators`` into crosslink committees seeded by ``seed`` and ``epoch``.
|
|
||||||
///
|
|
||||||
/// Return a list of ``committees_per_epoch`` committees where each
|
|
||||||
/// committee is itself a list of validator indices.
|
|
||||||
///
|
|
||||||
/// Spec v0.4.0
|
|
||||||
pub(crate) fn get_shuffling(
|
|
||||||
&self,
|
|
||||||
seed: Hash256,
|
|
||||||
epoch: Epoch,
|
|
||||||
spec: &ChainSpec,
|
|
||||||
) -> Result<Vec<Vec<usize>>, Error> {
|
|
||||||
let active_validator_indices =
|
|
||||||
get_active_validator_indices(&self.validator_registry, epoch);
|
|
||||||
if active_validator_indices.is_empty() {
|
|
||||||
error!("get_shuffling: no validators.");
|
|
||||||
return Err(Error::InsufficientValidators);
|
|
||||||
}
|
|
||||||
|
|
||||||
debug!("Shuffling {} validators...", active_validator_indices.len());
|
|
||||||
|
|
||||||
let committees_per_epoch = spec.get_epoch_committee_count(active_validator_indices.len());
|
|
||||||
|
|
||||||
trace!(
|
|
||||||
"get_shuffling: active_validator_indices.len() == {}, committees_per_epoch: {}",
|
|
||||||
active_validator_indices.len(),
|
|
||||||
committees_per_epoch
|
|
||||||
);
|
|
||||||
|
|
||||||
let active_validator_indices: Vec<usize> = active_validator_indices.to_vec();
|
|
||||||
|
|
||||||
let shuffled_active_validator_indices = shuffle_list(
|
|
||||||
active_validator_indices,
|
|
||||||
spec.shuffle_round_count,
|
|
||||||
&seed[..],
|
|
||||||
true,
|
|
||||||
)
|
|
||||||
.ok_or_else(|| Error::UnableToShuffle)?;
|
|
||||||
|
|
||||||
Ok(shuffled_active_validator_indices
|
|
||||||
.honey_badger_split(committees_per_epoch as usize)
|
|
||||||
.map(|slice: &[usize]| slice.to_vec())
|
|
||||||
.collect())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the following params for the given slot:
|
|
||||||
///
|
|
||||||
/// - epoch committee count
|
|
||||||
/// - epoch seed
|
|
||||||
/// - calculation epoch
|
|
||||||
/// - start shard
|
|
||||||
///
|
|
||||||
/// In the spec, this functionality is included in the `get_crosslink_committees_at_slot(..)`
|
|
||||||
/// function. It is separated here to allow the division of shuffling and committee building,
|
|
||||||
/// as is required for efficient operations.
|
|
||||||
///
|
|
||||||
/// Spec v0.4.0
|
|
||||||
pub(crate) fn get_committee_params_at_slot(
|
|
||||||
&self,
|
|
||||||
slot: Slot,
|
|
||||||
registry_change: bool,
|
|
||||||
spec: &ChainSpec,
|
|
||||||
) -> Result<(u64, Hash256, Epoch, u64), Error> {
|
|
||||||
let epoch = slot.epoch(spec.slots_per_epoch);
|
|
||||||
let current_epoch = self.current_epoch(spec);
|
|
||||||
let previous_epoch = self.previous_epoch(spec);
|
|
||||||
let next_epoch = self.next_epoch(spec);
|
|
||||||
|
|
||||||
if epoch == current_epoch {
|
|
||||||
Ok((
|
|
||||||
self.get_current_epoch_committee_count(spec),
|
|
||||||
self.current_shuffling_seed,
|
|
||||||
self.current_shuffling_epoch,
|
|
||||||
self.current_shuffling_start_shard,
|
|
||||||
))
|
|
||||||
} else if epoch == previous_epoch {
|
|
||||||
Ok((
|
|
||||||
self.get_previous_epoch_committee_count(spec),
|
|
||||||
self.previous_shuffling_seed,
|
|
||||||
self.previous_shuffling_epoch,
|
|
||||||
self.previous_shuffling_start_shard,
|
|
||||||
))
|
|
||||||
} else if epoch == next_epoch {
|
|
||||||
let current_committees_per_epoch = self.get_current_epoch_committee_count(spec);
|
|
||||||
let epochs_since_last_registry_update =
|
|
||||||
current_epoch - self.validator_registry_update_epoch;
|
|
||||||
let (seed, shuffling_start_shard) = if registry_change {
|
|
||||||
let next_seed = self.generate_seed(next_epoch, spec)?;
|
|
||||||
(
|
|
||||||
next_seed,
|
|
||||||
(self.current_shuffling_start_shard + current_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 = self.generate_seed(next_epoch, spec)?;
|
|
||||||
(next_seed, self.current_shuffling_start_shard)
|
|
||||||
} else {
|
|
||||||
(
|
|
||||||
self.current_shuffling_seed,
|
|
||||||
self.current_shuffling_start_shard,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
Ok((
|
|
||||||
self.get_next_epoch_committee_count(spec),
|
|
||||||
seed,
|
|
||||||
next_epoch,
|
|
||||||
shuffling_start_shard,
|
|
||||||
))
|
|
||||||
} else {
|
|
||||||
Err(Error::EpochOutOfBounds)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return the ordered list of shards tuples for the `slot`.
|
|
||||||
///
|
|
||||||
/// Note: There are two possible shufflings for crosslink committees for a
|
|
||||||
/// `slot` in the next epoch: with and without a `registry_change`
|
|
||||||
///
|
|
||||||
/// Spec v0.4.0
|
|
||||||
pub(crate) fn get_shards_for_slot(
|
|
||||||
&self,
|
|
||||||
slot: Slot,
|
|
||||||
registry_change: bool,
|
|
||||||
spec: &ChainSpec,
|
|
||||||
) -> Result<Vec<u64>, Error> {
|
|
||||||
let (committees_per_epoch, _seed, _shuffling_epoch, shuffling_start_shard) =
|
|
||||||
self.get_committee_params_at_slot(slot, registry_change, spec)?;
|
|
||||||
|
|
||||||
let offset = slot.as_u64() % spec.slots_per_epoch;
|
|
||||||
let committees_per_slot = committees_per_epoch / spec.slots_per_epoch;
|
|
||||||
let slot_start_shard =
|
|
||||||
(shuffling_start_shard + committees_per_slot * offset) % spec.shard_count;
|
|
||||||
|
|
||||||
let mut shards_at_slot = vec![];
|
|
||||||
for i in 0..committees_per_slot {
|
|
||||||
shards_at_slot.push((slot_start_shard + i) % spec.shard_count)
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(shards_at_slot)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the `slot`, `shard` and `committee_index` for which a validator must produce an
|
/// Returns the `slot`, `shard` and `committee_index` for which a validator must produce an
|
||||||
/// attestation.
|
/// attestation.
|
||||||
///
|
///
|
||||||
@ -969,14 +691,14 @@ impl BeaconState {
|
|||||||
/// Note: Utilizes the cache and will fail if the appropriate cache is not initialized.
|
/// Note: Utilizes the cache and will fail if the appropriate cache is not initialized.
|
||||||
///
|
///
|
||||||
/// Spec v0.4.0
|
/// Spec v0.4.0
|
||||||
pub fn attestation_slot_and_shard_for_validator(
|
pub fn get_attestation_duties(
|
||||||
&self,
|
&self,
|
||||||
validator_index: usize,
|
validator_index: usize,
|
||||||
_spec: &ChainSpec,
|
spec: &ChainSpec,
|
||||||
) -> Result<Option<(Slot, u64, u64)>, Error> {
|
) -> Result<&Option<AttestationDuty>, Error> {
|
||||||
let cache = self.cache(RelativeEpoch::Current)?;
|
let cache = self.cache(RelativeEpoch::Current, spec)?;
|
||||||
|
|
||||||
Ok(*cache
|
Ok(cache
|
||||||
.attestation_duties
|
.attestation_duties
|
||||||
.get(validator_index)
|
.get(validator_index)
|
||||||
.ok_or_else(|| Error::UnknownValidator)?)
|
.ok_or_else(|| Error::UnknownValidator)?)
|
||||||
@ -985,11 +707,11 @@ impl BeaconState {
|
|||||||
/// Process the slashings.
|
/// Process the slashings.
|
||||||
///
|
///
|
||||||
/// Spec v0.4.0
|
/// Spec v0.4.0
|
||||||
pub fn process_slashings(&mut self, spec: &ChainSpec) {
|
pub fn process_slashings(&mut self, spec: &ChainSpec) -> Result<(), Error> {
|
||||||
let current_epoch = self.current_epoch(spec);
|
let current_epoch = self.current_epoch(spec);
|
||||||
let active_validator_indices =
|
let active_validator_indices =
|
||||||
get_active_validator_indices(&self.validator_registry, current_epoch);
|
get_active_validator_indices(&self.validator_registry, current_epoch);
|
||||||
let total_balance = self.get_total_balance(&active_validator_indices[..], spec);
|
let total_balance = self.get_total_balance(&active_validator_indices[..], spec)?;
|
||||||
|
|
||||||
for (index, validator) in self.validator_registry.iter().enumerate() {
|
for (index, validator) in self.validator_registry.iter().enumerate() {
|
||||||
if validator.slashed
|
if validator.slashed
|
||||||
@ -1003,16 +725,19 @@ impl BeaconState {
|
|||||||
[(epoch_index + 1) % spec.latest_slashed_exit_length];
|
[(epoch_index + 1) % spec.latest_slashed_exit_length];
|
||||||
let total_at_end = self.latest_slashed_balances[epoch_index];
|
let total_at_end = self.latest_slashed_balances[epoch_index];
|
||||||
let total_penalities = total_at_end.saturating_sub(total_at_start);
|
let total_penalities = total_at_end.saturating_sub(total_at_start);
|
||||||
|
|
||||||
|
let effective_balance = self.get_effective_balance(index, spec)?;
|
||||||
let penalty = std::cmp::max(
|
let penalty = std::cmp::max(
|
||||||
self.get_effective_balance(index, spec)
|
effective_balance * std::cmp::min(total_penalities * 3, total_balance)
|
||||||
* std::cmp::min(total_penalities * 3, total_balance)
|
|
||||||
/ total_balance,
|
/ total_balance,
|
||||||
self.get_effective_balance(index, spec) / spec.min_penalty_quotient,
|
effective_balance / spec.min_penalty_quotient,
|
||||||
);
|
);
|
||||||
|
|
||||||
safe_sub_assign!(self.validator_balances[index], penalty);
|
safe_sub_assign!(self.validator_balances[index], penalty);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Process the exit queue.
|
/// Process the exit queue.
|
||||||
@ -1047,11 +772,11 @@ impl BeaconState {
|
|||||||
/// Update validator registry, activating/exiting validators if possible.
|
/// Update validator registry, activating/exiting validators if possible.
|
||||||
///
|
///
|
||||||
/// Spec v0.4.0
|
/// Spec v0.4.0
|
||||||
pub fn update_validator_registry(&mut self, spec: &ChainSpec) {
|
pub fn update_validator_registry(&mut self, spec: &ChainSpec) -> Result<(), Error> {
|
||||||
let current_epoch = self.current_epoch(spec);
|
let current_epoch = self.current_epoch(spec);
|
||||||
let active_validator_indices =
|
let active_validator_indices =
|
||||||
get_active_validator_indices(&self.validator_registry, current_epoch);
|
get_active_validator_indices(&self.validator_registry, current_epoch);
|
||||||
let total_balance = self.get_total_balance(&active_validator_indices[..], spec);
|
let total_balance = self.get_total_balance(&active_validator_indices[..], spec)?;
|
||||||
|
|
||||||
let max_balance_churn = std::cmp::max(
|
let max_balance_churn = std::cmp::max(
|
||||||
spec.max_deposit_amount,
|
spec.max_deposit_amount,
|
||||||
@ -1065,7 +790,7 @@ impl BeaconState {
|
|||||||
if (validator.activation_epoch == spec.far_future_epoch)
|
if (validator.activation_epoch == spec.far_future_epoch)
|
||||||
& (self.validator_balances[index] == spec.max_deposit_amount)
|
& (self.validator_balances[index] == spec.max_deposit_amount)
|
||||||
{
|
{
|
||||||
balance_churn += self.get_effective_balance(index, spec);
|
balance_churn += self.get_effective_balance(index, spec)?;
|
||||||
if balance_churn > max_balance_churn {
|
if balance_churn > max_balance_churn {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -1078,7 +803,7 @@ impl BeaconState {
|
|||||||
let validator = &self.validator_registry[index];
|
let validator = &self.validator_registry[index];
|
||||||
|
|
||||||
if (validator.exit_epoch == spec.far_future_epoch) & (validator.initiated_exit) {
|
if (validator.exit_epoch == spec.far_future_epoch) & (validator.initiated_exit) {
|
||||||
balance_churn += self.get_effective_balance(index, spec);
|
balance_churn += self.get_effective_balance(index, spec)?;
|
||||||
if balance_churn > max_balance_churn {
|
if balance_churn > max_balance_churn {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -1088,6 +813,8 @@ impl BeaconState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.validator_registry_update_epoch = current_epoch;
|
self.validator_registry_update_epoch = current_epoch;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Iterate through the validator registry and eject active validators with balance below
|
/// Iterate through the validator registry and eject active validators with balance below
|
||||||
@ -1115,12 +842,13 @@ impl BeaconState {
|
|||||||
epochs_since_finality: Epoch,
|
epochs_since_finality: Epoch,
|
||||||
base_reward_quotient: u64,
|
base_reward_quotient: u64,
|
||||||
spec: &ChainSpec,
|
spec: &ChainSpec,
|
||||||
) -> u64 {
|
) -> Result<u64, Error> {
|
||||||
let effective_balance = self.get_effective_balance(validator_index, spec);
|
let effective_balance = self.get_effective_balance(validator_index, spec)?;
|
||||||
self.base_reward(validator_index, base_reward_quotient, spec)
|
let base_reward = self.base_reward(validator_index, base_reward_quotient, spec)?;
|
||||||
|
Ok(base_reward
|
||||||
+ effective_balance * epochs_since_finality.as_u64()
|
+ effective_balance * epochs_since_finality.as_u64()
|
||||||
/ spec.inactivity_penalty_quotient
|
/ spec.inactivity_penalty_quotient
|
||||||
/ 2
|
/ 2)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the base reward for some validator.
|
/// Returns the base reward for some validator.
|
||||||
@ -1133,30 +861,27 @@ impl BeaconState {
|
|||||||
validator_index: usize,
|
validator_index: usize,
|
||||||
base_reward_quotient: u64,
|
base_reward_quotient: u64,
|
||||||
spec: &ChainSpec,
|
spec: &ChainSpec,
|
||||||
) -> u64 {
|
) -> Result<u64, Error> {
|
||||||
self.get_effective_balance(validator_index, spec) / base_reward_quotient / 5
|
Ok(self.get_effective_balance(validator_index, spec)? / base_reward_quotient / 5)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the union of all participants in the provided attestations
|
/// Return the combined effective balance of an array of validators.
|
||||||
///
|
///
|
||||||
/// Spec v0.4.0
|
/// Spec v0.4.0
|
||||||
pub fn get_attestation_participants_union(
|
pub fn get_total_balance(
|
||||||
&self,
|
&self,
|
||||||
attestations: &[&PendingAttestation],
|
validator_indices: &[usize],
|
||||||
spec: &ChainSpec,
|
spec: &ChainSpec,
|
||||||
) -> Result<Vec<usize>, Error> {
|
) -> Result<u64, Error> {
|
||||||
let mut all_participants = attestations
|
validator_indices.iter().try_fold(0_u64, |acc, i| {
|
||||||
.iter()
|
self.get_effective_balance(*i, spec)
|
||||||
.try_fold::<_, _, Result<Vec<usize>, Error>>(vec![], |mut acc, a| {
|
.and_then(|bal| Ok(bal + acc))
|
||||||
acc.append(&mut self.get_attestation_participants(
|
})
|
||||||
&a.data,
|
}
|
||||||
&a.aggregation_bitfield,
|
}
|
||||||
spec,
|
|
||||||
)?);
|
impl From<RelativeEpochError> for Error {
|
||||||
Ok(acc)
|
fn from(e: RelativeEpochError) -> Error {
|
||||||
})?;
|
Error::RelativeEpochError(e)
|
||||||
all_participants.sort_unstable();
|
|
||||||
all_participants.dedup();
|
|
||||||
Ok(all_participants)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -43,12 +43,14 @@ impl BeaconStateBuilder {
|
|||||||
self.state.deposit_index = initial_validator_deposits.len() as u64;
|
self.state.deposit_index = initial_validator_deposits.len() as u64;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn activate_genesis_validators(&mut self, spec: &ChainSpec) {
|
fn activate_genesis_validators(&mut self, spec: &ChainSpec) -> Result<(), BeaconStateError> {
|
||||||
for validator_index in 0..self.state.validator_registry.len() {
|
for validator_index in 0..self.state.validator_registry.len() {
|
||||||
if self.state.get_effective_balance(validator_index, spec) >= spec.max_deposit_amount {
|
if self.state.get_effective_balance(validator_index, spec)? >= spec.max_deposit_amount {
|
||||||
self.state.activate_validator(validator_index, true, spec);
|
self.state.activate_validator(validator_index, true, spec);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Instantiate the validator registry from a YAML file.
|
/// Instantiate the validator registry from a YAML file.
|
||||||
|
@ -1,69 +1,283 @@
|
|||||||
use super::{AttestationDuty, BeaconState, CrosslinkCommittees, Error};
|
use super::{BeaconState, Error};
|
||||||
use crate::{ChainSpec, Epoch};
|
use crate::*;
|
||||||
|
use honey_badger_split::SplitExt;
|
||||||
use serde_derive::{Deserialize, Serialize};
|
use serde_derive::{Deserialize, Serialize};
|
||||||
|
use swap_or_not_shuffle::shuffle_list;
|
||||||
|
|
||||||
#[derive(Debug, Default, PartialEq, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Default, PartialEq, Clone, Serialize, Deserialize)]
|
||||||
pub struct EpochCache {
|
pub struct EpochCache {
|
||||||
/// True if this cache has been initialized.
|
/// `Some(epoch)` if the cache is initialized, where `epoch` is the cache it holds.
|
||||||
pub initialized: bool,
|
pub initialized_epoch: Option<Epoch>,
|
||||||
/// The crosslink committees for an epoch.
|
/// All crosslink committees for an epoch.
|
||||||
pub committees: Vec<CrosslinkCommittees>,
|
pub epoch_crosslink_committees: EpochCrosslinkCommittees,
|
||||||
/// 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>>,
|
||||||
/// Maps a shard to an index of `self.committees`.
|
/// Maps a shard to an index of `self.committees`.
|
||||||
pub shard_committee_indices: Vec<(usize, usize)>,
|
pub shard_committee_indices: Vec<(Slot, usize)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EpochCache {
|
impl EpochCache {
|
||||||
/// Return a new, fully initialized cache.
|
/// Return a new, fully initialized cache.
|
||||||
pub fn initialized(
|
pub fn initialized(
|
||||||
state: &BeaconState,
|
state: &BeaconState,
|
||||||
epoch: Epoch,
|
relative_epoch: RelativeEpoch,
|
||||||
spec: &ChainSpec,
|
spec: &ChainSpec,
|
||||||
) -> Result<EpochCache, Error> {
|
) -> Result<EpochCache, Error> {
|
||||||
let mut epoch_committees: Vec<CrosslinkCommittees> =
|
let epoch = relative_epoch.into_epoch(state.slot.epoch(spec.slots_per_epoch));
|
||||||
Vec::with_capacity(spec.slots_per_epoch as usize);
|
|
||||||
|
|
||||||
|
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,
|
||||||
|
spec,
|
||||||
|
),
|
||||||
|
RelativeEpoch::Current => EpochCrosslinkCommitteesBuilder::for_current_epoch(
|
||||||
|
state,
|
||||||
|
active_validator_indices,
|
||||||
|
spec,
|
||||||
|
),
|
||||||
|
RelativeEpoch::NextWithRegistryChange => {
|
||||||
|
EpochCrosslinkCommitteesBuilder::for_next_epoch(
|
||||||
|
state,
|
||||||
|
active_validator_indices,
|
||||||
|
true,
|
||||||
|
spec,
|
||||||
|
)?
|
||||||
|
}
|
||||||
|
RelativeEpoch::NextWithoutRegistryChange => {
|
||||||
|
EpochCrosslinkCommitteesBuilder::for_next_epoch(
|
||||||
|
state,
|
||||||
|
active_validator_indices,
|
||||||
|
false,
|
||||||
|
spec,
|
||||||
|
)?
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let epoch_crosslink_committees = builder.build(spec)?;
|
||||||
|
|
||||||
|
// Loop through all the validators in the committees and create the following maps:
|
||||||
|
//
|
||||||
|
// 1. `attestation_duties`: maps `ValidatorIndex` to `AttestationDuty`.
|
||||||
|
// 2. `shard_committee_indices`: maps `Shard` into a `CrosslinkCommittee` in
|
||||||
|
// `EpochCrosslinkCommittees`.
|
||||||
let mut attestation_duties = vec![None; state.validator_registry.len()];
|
let mut attestation_duties = vec![None; state.validator_registry.len()];
|
||||||
|
let mut shard_committee_indices = vec![(Slot::default(), 0); 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;
|
||||||
|
|
||||||
let mut shard_committee_indices = vec![(0, 0); spec.shard_count as usize];
|
for (j, crosslink_committee) in slot_committees.iter().enumerate() {
|
||||||
|
let shard = crosslink_committee.shard;
|
||||||
|
|
||||||
let mut shuffling =
|
shard_committee_indices[shard as usize] = (slot, j);
|
||||||
state.get_shuffling_for_slot(epoch.start_slot(spec.slots_per_epoch), false, spec)?;
|
|
||||||
|
|
||||||
for (epoch_committees_index, slot) in epoch.slot_iter(spec.slots_per_epoch).enumerate() {
|
for (k, validator_index) in crosslink_committee.committee.iter().enumerate() {
|
||||||
let mut slot_committees: Vec<(Vec<usize>, u64)> = vec![];
|
let attestation_duty = AttestationDuty {
|
||||||
|
slot,
|
||||||
let shards = state.get_shards_for_slot(slot, false, spec)?;
|
shard,
|
||||||
for shard in shards {
|
committee_index: k,
|
||||||
let committee = shuffling.remove(0);
|
};
|
||||||
slot_committees.push((committee, shard));
|
attestation_duties[*validator_index] = Some(attestation_duty)
|
||||||
}
|
|
||||||
|
|
||||||
for (slot_committees_index, (committee, shard)) in slot_committees.iter().enumerate() {
|
|
||||||
if committee.is_empty() {
|
|
||||||
return Err(Error::InsufficientValidators);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Store the slot and committee index for this shard.
|
|
||||||
shard_committee_indices[*shard as usize] =
|
|
||||||
(epoch_committees_index, slot_committees_index);
|
|
||||||
|
|
||||||
// For each validator, store their attestation duties.
|
|
||||||
for (committee_index, validator_index) in committee.iter().enumerate() {
|
|
||||||
attestation_duties[*validator_index] =
|
|
||||||
Some((slot, *shard, committee_index as u64))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
epoch_committees.push(slot_committees)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(EpochCache {
|
Ok(EpochCache {
|
||||||
initialized: true,
|
initialized_epoch: Some(epoch),
|
||||||
committees: epoch_committees,
|
epoch_crosslink_committees,
|
||||||
attestation_duties,
|
attestation_duties,
|
||||||
shard_committee_indices,
|
shard_committee_indices,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 get_crosslink_committee_for_shard(
|
||||||
|
&self,
|
||||||
|
shard: Shard,
|
||||||
|
spec: &ChainSpec,
|
||||||
|
) -> Option<&CrosslinkCommittee> {
|
||||||
|
let (slot, committee) = self.shard_committee_indices.get(shard as usize)?;
|
||||||
|
let slot_committees = self.get_crosslink_committees_at_slot(*slot, spec)?;
|
||||||
|
slot_committees.get(*committee)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_active_validator_indices(validators: &[Validator], epoch: Epoch) -> Vec<usize> {
|
||||||
|
let mut active = Vec::with_capacity(validators.len());
|
||||||
|
|
||||||
|
for (index, validator) in validators.iter().enumerate() {
|
||||||
|
if validator.is_active_at(epoch) {
|
||||||
|
active.push(index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
active.shrink_to_fit();
|
||||||
|
|
||||||
|
active
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default, PartialEq, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct EpochCrosslinkCommittees {
|
||||||
|
epoch: Epoch,
|
||||||
|
pub crosslink_committees: Vec<Vec<CrosslinkCommittee>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EpochCrosslinkCommittees {
|
||||||
|
fn new(epoch: Epoch, spec: &ChainSpec) -> Self {
|
||||||
|
Self {
|
||||||
|
epoch,
|
||||||
|
crosslink_committees: vec![vec![]; spec.slots_per_epoch as usize],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct EpochCrosslinkCommitteesBuilder {
|
||||||
|
epoch: Epoch,
|
||||||
|
shuffling_start_shard: Shard,
|
||||||
|
shuffling_seed: Hash256,
|
||||||
|
active_validator_indices: Vec<usize>,
|
||||||
|
committees_per_epoch: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EpochCrosslinkCommitteesBuilder {
|
||||||
|
pub fn for_previous_epoch(
|
||||||
|
state: &BeaconState,
|
||||||
|
active_validator_indices: Vec<usize>,
|
||||||
|
spec: &ChainSpec,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
epoch: state.previous_epoch(spec),
|
||||||
|
shuffling_start_shard: state.previous_shuffling_start_shard,
|
||||||
|
shuffling_seed: state.previous_shuffling_seed,
|
||||||
|
committees_per_epoch: spec.get_epoch_committee_count(active_validator_indices.len()),
|
||||||
|
active_validator_indices,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn for_current_epoch(
|
||||||
|
state: &BeaconState,
|
||||||
|
active_validator_indices: Vec<usize>,
|
||||||
|
spec: &ChainSpec,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
epoch: state.current_epoch(spec),
|
||||||
|
shuffling_start_shard: state.current_shuffling_start_shard,
|
||||||
|
shuffling_seed: state.current_shuffling_seed,
|
||||||
|
committees_per_epoch: spec.get_epoch_committee_count(active_validator_indices.len()),
|
||||||
|
active_validator_indices,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn for_next_epoch(
|
||||||
|
state: &BeaconState,
|
||||||
|
active_validator_indices: Vec<usize>,
|
||||||
|
registry_change: bool,
|
||||||
|
spec: &ChainSpec,
|
||||||
|
) -> Result<Self, BeaconStateError> {
|
||||||
|
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());
|
||||||
|
|
||||||
|
let epochs_since_last_registry_update =
|
||||||
|
current_epoch - state.validator_registry_update_epoch;
|
||||||
|
|
||||||
|
let (seed, shuffling_start_shard) = if registry_change {
|
||||||
|
let next_seed = state.generate_seed(next_epoch, spec)?;
|
||||||
|
(
|
||||||
|
next_seed,
|
||||||
|
(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)?;
|
||||||
|
(next_seed, state.current_shuffling_start_shard)
|
||||||
|
} else {
|
||||||
|
(
|
||||||
|
state.current_shuffling_seed,
|
||||||
|
state.current_shuffling_start_shard,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
epoch: state.next_epoch(spec),
|
||||||
|
shuffling_start_shard,
|
||||||
|
shuffling_seed: seed,
|
||||||
|
active_validator_indices,
|
||||||
|
committees_per_epoch,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build(self, spec: &ChainSpec) -> Result<EpochCrosslinkCommittees, BeaconStateError> {
|
||||||
|
if self.active_validator_indices.is_empty() {
|
||||||
|
return Err(Error::InsufficientValidators);
|
||||||
|
}
|
||||||
|
|
||||||
|
let shuffled_active_validator_indices = shuffle_list(
|
||||||
|
self.active_validator_indices,
|
||||||
|
spec.shuffle_round_count,
|
||||||
|
&self.shuffling_seed[..],
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
.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 in 0..spec.slots_per_epoch as usize {
|
||||||
|
for j in (0..committees.len())
|
||||||
|
.into_iter()
|
||||||
|
.skip(i * committees_per_slot)
|
||||||
|
.take(committees_per_slot)
|
||||||
|
{
|
||||||
|
let crosslink_committee = CrosslinkCommittee {
|
||||||
|
shard,
|
||||||
|
committee: committees.remove(j),
|
||||||
|
};
|
||||||
|
epoch_crosslink_committees.crosslink_committees[i].push(crosslink_committee);
|
||||||
|
|
||||||
|
shard += 1;
|
||||||
|
shard %= spec.shard_count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(epoch_crosslink_committees)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,53 +1,5 @@
|
|||||||
#![cfg(test)]
|
#![cfg(test)]
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::test_utils::TestingBeaconStateBuilder;
|
|
||||||
use crate::test_utils::{SeedableRng, TestRandom, XorShiftRng};
|
|
||||||
use crate::{BeaconState, ChainSpec};
|
|
||||||
|
|
||||||
/// Tests that `get_attestation_participants` is consistent with the result of
|
|
||||||
/// get_crosslink_committees_at_slot` with a full bitfield.
|
|
||||||
#[test]
|
|
||||||
pub fn get_attestation_participants_consistency() {
|
|
||||||
let mut rng = XorShiftRng::from_seed([42; 16]);
|
|
||||||
|
|
||||||
let spec = ChainSpec::few_validators();
|
|
||||||
let builder = TestingBeaconStateBuilder::from_deterministic_keypairs(8, &spec);
|
|
||||||
let (mut state, _keypairs) = builder.build();
|
|
||||||
|
|
||||||
state
|
|
||||||
.build_epoch_cache(RelativeEpoch::Previous, &spec)
|
|
||||||
.unwrap();
|
|
||||||
state
|
|
||||||
.build_epoch_cache(RelativeEpoch::Current, &spec)
|
|
||||||
.unwrap();
|
|
||||||
state.build_epoch_cache(RelativeEpoch::Next, &spec).unwrap();
|
|
||||||
|
|
||||||
for slot in state
|
|
||||||
.slot
|
|
||||||
.epoch(spec.slots_per_epoch)
|
|
||||||
.slot_iter(spec.slots_per_epoch)
|
|
||||||
{
|
|
||||||
let committees = state.get_crosslink_committees_at_slot(slot, &spec).unwrap();
|
|
||||||
|
|
||||||
for (committee, shard) in committees {
|
|
||||||
let mut attestation_data = AttestationData::random_for_test(&mut rng);
|
|
||||||
attestation_data.slot = slot;
|
|
||||||
attestation_data.shard = *shard;
|
|
||||||
|
|
||||||
let mut bitfield = Bitfield::new();
|
|
||||||
for (i, _) in committee.iter().enumerate() {
|
|
||||||
bitfield.set(i, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
state
|
|
||||||
.get_attestation_participants(&attestation_data, &bitfield, &spec)
|
|
||||||
.unwrap(),
|
|
||||||
*committee
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ssz_tests!(BeaconState);
|
ssz_tests!(BeaconState);
|
||||||
|
9
eth2/types/src/crosslink_committee.rs
Normal file
9
eth2/types/src/crosslink_committee.rs
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
use crate::*;
|
||||||
|
use serde_derive::{Deserialize, Serialize};
|
||||||
|
use ssz_derive::{Decode, Encode, TreeHash};
|
||||||
|
|
||||||
|
#[derive(Default, Clone, Debug, PartialEq, Serialize, Deserialize, Decode, Encode, TreeHash)]
|
||||||
|
pub struct CrosslinkCommittee {
|
||||||
|
pub shard: Shard,
|
||||||
|
pub committee: Vec<usize>,
|
||||||
|
}
|
0
eth2/types/src/epoch_cache.rs
Normal file
0
eth2/types/src/epoch_cache.rs
Normal file
@ -6,6 +6,7 @@ pub mod test_utils;
|
|||||||
pub mod attestation;
|
pub mod attestation;
|
||||||
pub mod attestation_data;
|
pub mod attestation_data;
|
||||||
pub mod attestation_data_and_custody_bit;
|
pub mod attestation_data_and_custody_bit;
|
||||||
|
pub mod attestation_duty;
|
||||||
pub mod attester_slashing;
|
pub mod attester_slashing;
|
||||||
pub mod beacon_block;
|
pub mod beacon_block;
|
||||||
pub mod beacon_block_body;
|
pub mod beacon_block_body;
|
||||||
@ -13,6 +14,7 @@ pub mod beacon_block_header;
|
|||||||
pub mod beacon_state;
|
pub mod beacon_state;
|
||||||
pub mod chain_spec;
|
pub mod chain_spec;
|
||||||
pub mod crosslink;
|
pub mod crosslink;
|
||||||
|
pub mod crosslink_committee;
|
||||||
pub mod deposit;
|
pub mod deposit;
|
||||||
pub mod deposit_data;
|
pub mod deposit_data;
|
||||||
pub mod deposit_input;
|
pub mod deposit_input;
|
||||||
@ -28,6 +30,7 @@ pub mod transfer;
|
|||||||
pub mod voluntary_exit;
|
pub mod voluntary_exit;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
pub mod slot_epoch_macros;
|
pub mod slot_epoch_macros;
|
||||||
|
pub mod relative_epoch;
|
||||||
pub mod slot_epoch;
|
pub mod slot_epoch;
|
||||||
pub mod slot_height;
|
pub mod slot_height;
|
||||||
pub mod validator;
|
pub mod validator;
|
||||||
@ -39,13 +42,15 @@ use std::collections::HashMap;
|
|||||||
pub use crate::attestation::Attestation;
|
pub use crate::attestation::Attestation;
|
||||||
pub use crate::attestation_data::AttestationData;
|
pub use crate::attestation_data::AttestationData;
|
||||||
pub use crate::attestation_data_and_custody_bit::AttestationDataAndCustodyBit;
|
pub use crate::attestation_data_and_custody_bit::AttestationDataAndCustodyBit;
|
||||||
|
pub use crate::attestation_duty::AttestationDuty;
|
||||||
pub use crate::attester_slashing::AttesterSlashing;
|
pub use crate::attester_slashing::AttesterSlashing;
|
||||||
pub use crate::beacon_block::BeaconBlock;
|
pub use crate::beacon_block::BeaconBlock;
|
||||||
pub use crate::beacon_block_body::BeaconBlockBody;
|
pub use crate::beacon_block_body::BeaconBlockBody;
|
||||||
pub use crate::beacon_block_header::BeaconBlockHeader;
|
pub use crate::beacon_block_header::BeaconBlockHeader;
|
||||||
pub use crate::beacon_state::{BeaconState, Error as BeaconStateError, RelativeEpoch};
|
pub use crate::beacon_state::{BeaconState, Error as BeaconStateError};
|
||||||
pub use crate::chain_spec::{ChainSpec, Domain};
|
pub use crate::chain_spec::{ChainSpec, Domain};
|
||||||
pub use crate::crosslink::Crosslink;
|
pub use crate::crosslink::Crosslink;
|
||||||
|
pub use crate::crosslink_committee::CrosslinkCommittee;
|
||||||
pub use crate::deposit::Deposit;
|
pub use crate::deposit::Deposit;
|
||||||
pub use crate::deposit_data::DepositData;
|
pub use crate::deposit_data::DepositData;
|
||||||
pub use crate::deposit_input::DepositInput;
|
pub use crate::deposit_input::DepositInput;
|
||||||
@ -56,6 +61,7 @@ pub use crate::free_attestation::FreeAttestation;
|
|||||||
pub use crate::historical_batch::HistoricalBatch;
|
pub use crate::historical_batch::HistoricalBatch;
|
||||||
pub use crate::pending_attestation::PendingAttestation;
|
pub use crate::pending_attestation::PendingAttestation;
|
||||||
pub use crate::proposer_slashing::ProposerSlashing;
|
pub use crate::proposer_slashing::ProposerSlashing;
|
||||||
|
pub use crate::relative_epoch::{Error as RelativeEpochError, RelativeEpoch};
|
||||||
pub use crate::slashable_attestation::SlashableAttestation;
|
pub use crate::slashable_attestation::SlashableAttestation;
|
||||||
pub use crate::slot_epoch::{Epoch, Slot};
|
pub use crate::slot_epoch::{Epoch, Slot};
|
||||||
pub use crate::slot_height::SlotHeight;
|
pub use crate::slot_height::SlotHeight;
|
||||||
@ -63,6 +69,10 @@ pub use crate::transfer::Transfer;
|
|||||||
pub use crate::validator::Validator;
|
pub use crate::validator::Validator;
|
||||||
pub use crate::voluntary_exit::VoluntaryExit;
|
pub use crate::voluntary_exit::VoluntaryExit;
|
||||||
|
|
||||||
|
pub type Shard = u64;
|
||||||
|
pub type Committee = Vec<usize>;
|
||||||
|
pub type CrosslinkCommittees = Vec<(Committee, u64)>;
|
||||||
|
|
||||||
pub type Hash256 = H256;
|
pub type Hash256 = H256;
|
||||||
pub type Address = H160;
|
pub type Address = H160;
|
||||||
pub type EthBalance = U256;
|
pub type EthBalance = U256;
|
||||||
|
76
eth2/types/src/relative_epoch.rs
Normal file
76
eth2/types/src/relative_epoch.rs
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
use crate::*;
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Clone, Copy)]
|
||||||
|
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
|
||||||
|
/// to and following some epoch.
|
||||||
|
///
|
||||||
|
/// Spec v0.5.0
|
||||||
|
#[derive(Debug, PartialEq, Clone, Copy)]
|
||||||
|
pub enum RelativeEpoch {
|
||||||
|
/// The prior epoch.
|
||||||
|
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,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RelativeEpoch {
|
||||||
|
/// Returns the `epoch` that `self` refers to, with respect to the `base` epoch.
|
||||||
|
///
|
||||||
|
/// Spec v0.5.0
|
||||||
|
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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Converts the `other` epoch into a `RelativeEpoch`, with respect to `base`
|
||||||
|
///
|
||||||
|
/// ## Errors
|
||||||
|
/// 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.0
|
||||||
|
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)
|
||||||
|
} else if other < base {
|
||||||
|
Err(Error::EpochTooLow { base, other })
|
||||||
|
} else {
|
||||||
|
Err(Error::EpochTooHigh { base, other })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
@ -109,12 +109,20 @@ impl TestingBeaconBlockBuilder {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (committee, shard) in state.get_crosslink_committees_at_slot(slot, spec)? {
|
let relative_epoch = RelativeEpoch::from_slot(state.slot, slot, spec).unwrap();
|
||||||
|
for crosslink_committee in
|
||||||
|
state.get_crosslink_committees_at_slot(slot, relative_epoch, spec)?
|
||||||
|
{
|
||||||
if attestations_added >= num_attestations {
|
if attestations_added >= num_attestations {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
committees.push((slot, committee.clone(), committee.clone(), *shard));
|
committees.push((
|
||||||
|
slot,
|
||||||
|
crosslink_committee.committee.clone(),
|
||||||
|
crosslink_committee.committee.clone(),
|
||||||
|
crosslink_committee.shard,
|
||||||
|
));
|
||||||
|
|
||||||
attestations_added += 1;
|
attestations_added += 1;
|
||||||
}
|
}
|
||||||
|
@ -159,7 +159,8 @@ impl TestingBeaconStateBuilder {
|
|||||||
|
|
||||||
state.build_epoch_cache(RelativeEpoch::Previous, &spec)?;
|
state.build_epoch_cache(RelativeEpoch::Previous, &spec)?;
|
||||||
state.build_epoch_cache(RelativeEpoch::Current, &spec)?;
|
state.build_epoch_cache(RelativeEpoch::Current, &spec)?;
|
||||||
state.build_epoch_cache(RelativeEpoch::Next, &spec)?;
|
state.build_epoch_cache(RelativeEpoch::NextWithRegistryChange, &spec)?;
|
||||||
|
state.build_epoch_cache(RelativeEpoch::NextWithoutRegistryChange, &spec)?;
|
||||||
|
|
||||||
state.update_pubkey_cache()?;
|
state.update_pubkey_cache()?;
|
||||||
|
|
||||||
@ -222,15 +223,21 @@ impl TestingBeaconStateBuilder {
|
|||||||
for slot in first_slot..last_slot + 1 {
|
for slot in first_slot..last_slot + 1 {
|
||||||
let slot = Slot::from(slot);
|
let slot = Slot::from(slot);
|
||||||
|
|
||||||
|
let relative_epoch = RelativeEpoch::from_slot(state.slot, slot, spec).unwrap();
|
||||||
let committees = state
|
let committees = state
|
||||||
.get_crosslink_committees_at_slot(slot, spec)
|
.get_crosslink_committees_at_slot(slot, relative_epoch, spec)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.clone();
|
.clone();
|
||||||
|
|
||||||
for (committee, shard) in committees {
|
for crosslink_committee in committees {
|
||||||
let mut builder = TestingPendingAttestationBuilder::new(state, shard, slot, spec);
|
let mut builder = TestingPendingAttestationBuilder::new(
|
||||||
|
state,
|
||||||
|
crosslink_committee.shard,
|
||||||
|
slot,
|
||||||
|
spec,
|
||||||
|
);
|
||||||
// The entire committee should have signed the pending attestation.
|
// The entire committee should have signed the pending attestation.
|
||||||
let signers = vec![true; committee.len()];
|
let signers = vec![true; crosslink_committee.committee.len()];
|
||||||
builder.add_committee_participation(signers);
|
builder.add_committee_participation(signers);
|
||||||
let attestation = builder.build();
|
let attestation = builder.build();
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user