mirror of
https://github.com/sigp/lighthouse.git
synced 2026-03-19 12:56:12 +00:00
Update to spec v0.9.0
This commit is contained in:
@@ -9,7 +9,7 @@ use tree_hash_derive::{SignedRoot, TreeHash};
|
||||
|
||||
/// Details an attestation that can be slashable.
|
||||
///
|
||||
/// Spec v0.8.0
|
||||
/// Spec v0.9.0
|
||||
#[derive(
|
||||
Debug,
|
||||
Clone,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::test_utils::TestRandom;
|
||||
use crate::{Checkpoint, Crosslink, Hash256};
|
||||
use crate::{Checkpoint, Hash256, Slot};
|
||||
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use ssz_derive::{Decode, Encode};
|
||||
@@ -8,20 +8,20 @@ use tree_hash_derive::TreeHash;
|
||||
|
||||
/// The data upon which an attestation is based.
|
||||
///
|
||||
/// Spec v0.8.0
|
||||
/// Spec v0.9.0
|
||||
#[derive(
|
||||
Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Hash, Encode, Decode, TreeHash, TestRandom,
|
||||
)]
|
||||
pub struct AttestationData {
|
||||
pub slot: Slot,
|
||||
pub index: u64,
|
||||
|
||||
// LMD GHOST vote
|
||||
pub beacon_block_root: Hash256,
|
||||
|
||||
// FFG Vote
|
||||
pub source: Checkpoint,
|
||||
pub target: Checkpoint,
|
||||
|
||||
// Crosslink Vote
|
||||
pub crosslink: Crosslink,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -7,7 +7,7 @@ use tree_hash_derive::TreeHash;
|
||||
|
||||
/// Used for pairing an attestation with a proof-of-custody.
|
||||
///
|
||||
/// Spec v0.8.1
|
||||
/// Spec v0.9.0
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom)]
|
||||
pub struct AttestationDataAndCustodyBit {
|
||||
pub data: AttestationData,
|
||||
|
||||
@@ -3,8 +3,12 @@ use serde_derive::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, PartialEq, Clone, Copy, Default, Serialize, Deserialize)]
|
||||
pub struct AttestationDuty {
|
||||
/// The slot during which the attester must attest.
|
||||
pub slot: Slot,
|
||||
pub shard: Shard,
|
||||
pub committee_index: usize,
|
||||
/// The index of this committee within the committees in `slot`.
|
||||
pub index: CommitteeIndex,
|
||||
/// The position of the attester within the committee.
|
||||
pub committee_position: usize,
|
||||
/// The total number of attesters in the committee.
|
||||
pub committee_len: usize,
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ use tree_hash_derive::TreeHash;
|
||||
|
||||
/// Two conflicting attestations.
|
||||
///
|
||||
/// Spec v0.8.0
|
||||
/// Spec v0.9.0
|
||||
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom)]
|
||||
#[serde(bound = "T: EthSpec")]
|
||||
pub struct AttesterSlashing<T: EthSpec> {
|
||||
|
||||
@@ -10,7 +10,7 @@ use tree_hash_derive::{SignedRoot, TreeHash};
|
||||
|
||||
/// A block of the `BeaconChain`.
|
||||
///
|
||||
/// Spec v0.8.0
|
||||
/// Spec v0.9.0
|
||||
#[derive(
|
||||
Debug,
|
||||
PartialEq,
|
||||
@@ -36,7 +36,7 @@ pub struct BeaconBlock<T: EthSpec> {
|
||||
impl<T: EthSpec> BeaconBlock<T> {
|
||||
/// Returns an empty block to be used during genesis.
|
||||
///
|
||||
/// Spec v0.8.1
|
||||
/// Spec v0.9.0
|
||||
pub fn empty(spec: &ChainSpec) -> Self {
|
||||
BeaconBlock {
|
||||
slot: spec.genesis_slot,
|
||||
@@ -55,7 +55,6 @@ impl<T: EthSpec> BeaconBlock<T> {
|
||||
attestations: VariableList::empty(),
|
||||
deposits: VariableList::empty(),
|
||||
voluntary_exits: VariableList::empty(),
|
||||
transfers: VariableList::empty(),
|
||||
},
|
||||
signature: Signature::empty_signature(),
|
||||
}
|
||||
@@ -68,7 +67,7 @@ impl<T: EthSpec> BeaconBlock<T> {
|
||||
|
||||
/// Returns the `signed_root` of the block.
|
||||
///
|
||||
/// Spec v0.8.1
|
||||
/// Spec v0.9.0
|
||||
pub fn canonical_root(&self) -> Hash256 {
|
||||
Hash256::from_slice(&self.signed_root()[..])
|
||||
}
|
||||
@@ -80,7 +79,7 @@ impl<T: EthSpec> BeaconBlock<T> {
|
||||
///
|
||||
/// Note: performs a full tree-hash of `self.body`.
|
||||
///
|
||||
/// Spec v0.8.1
|
||||
/// Spec v0.9.0
|
||||
pub fn block_header(&self) -> BeaconBlockHeader {
|
||||
BeaconBlockHeader {
|
||||
slot: self.slot,
|
||||
@@ -93,7 +92,7 @@ impl<T: EthSpec> BeaconBlock<T> {
|
||||
|
||||
/// Returns a "temporary" header, where the `state_root` is `Hash256::zero()`.
|
||||
///
|
||||
/// Spec v0.8.0
|
||||
/// Spec v0.9.0
|
||||
pub fn temporary_block_header(&self) -> BeaconBlockHeader {
|
||||
BeaconBlockHeader {
|
||||
state_root: Hash256::zero(),
|
||||
|
||||
@@ -10,7 +10,7 @@ use tree_hash_derive::TreeHash;
|
||||
|
||||
/// The body of a `BeaconChain` block, containing operations.
|
||||
///
|
||||
/// Spec v0.8.0
|
||||
/// Spec v0.9.0
|
||||
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom)]
|
||||
#[serde(bound = "T: EthSpec")]
|
||||
pub struct BeaconBlockBody<T: EthSpec> {
|
||||
@@ -26,7 +26,6 @@ pub struct BeaconBlockBody<T: EthSpec> {
|
||||
pub attestations: VariableList<Attestation<T>, T::MaxAttestations>,
|
||||
pub deposits: VariableList<Deposit, T::MaxDeposits>,
|
||||
pub voluntary_exits: VariableList<VoluntaryExit, T::MaxVoluntaryExits>,
|
||||
pub transfers: VariableList<Transfer, T::MaxTransfers>,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -10,7 +10,7 @@ use tree_hash_derive::{SignedRoot, TreeHash};
|
||||
|
||||
/// A header of a `BeaconBlock`.
|
||||
///
|
||||
/// Spec v0.8.1
|
||||
/// Spec v0.9.0
|
||||
#[derive(
|
||||
Debug,
|
||||
PartialEq,
|
||||
@@ -35,14 +35,14 @@ pub struct BeaconBlockHeader {
|
||||
impl BeaconBlockHeader {
|
||||
/// Returns the `tree_hash_root` of the header.
|
||||
///
|
||||
/// Spec v0.8.1
|
||||
/// Spec v0.9.0
|
||||
pub fn canonical_root(&self) -> Hash256 {
|
||||
Hash256::from_slice(&self.signed_root()[..])
|
||||
}
|
||||
|
||||
/// Given a `body`, consumes `self` and returns a complete `BeaconBlock`.
|
||||
///
|
||||
/// Spec v0.8.0
|
||||
/// Spec v0.9.0
|
||||
pub fn into_block<T: EthSpec>(self, body: BeaconBlockBody<T>) -> BeaconBlock<T> {
|
||||
BeaconBlock {
|
||||
slot: self.slot,
|
||||
|
||||
@@ -1,25 +1,25 @@
|
||||
use crate::*;
|
||||
|
||||
#[derive(Default, Clone, Debug, PartialEq)]
|
||||
pub struct CrosslinkCommittee<'a> {
|
||||
pub struct BeaconCommittee<'a> {
|
||||
pub slot: Slot,
|
||||
pub shard: Shard,
|
||||
pub index: CommitteeIndex,
|
||||
pub committee: &'a [usize],
|
||||
}
|
||||
|
||||
impl<'a> CrosslinkCommittee<'a> {
|
||||
pub fn into_owned(self) -> OwnedCrosslinkCommittee {
|
||||
OwnedCrosslinkCommittee {
|
||||
impl<'a> BeaconCommittee<'a> {
|
||||
pub fn into_owned(self) -> OwnedBeaconCommittee {
|
||||
OwnedBeaconCommittee {
|
||||
slot: self.slot,
|
||||
shard: self.shard,
|
||||
index: self.index,
|
||||
committee: self.committee.to_vec(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Clone, Debug, PartialEq)]
|
||||
pub struct OwnedCrosslinkCommittee {
|
||||
pub struct OwnedBeaconCommittee {
|
||||
pub slot: Slot,
|
||||
pub shard: Shard,
|
||||
pub index: CommitteeIndex,
|
||||
pub committee: Vec<usize>,
|
||||
}
|
||||
@@ -5,12 +5,13 @@ use crate::*;
|
||||
use cached_tree_hash::{CachedTreeHash, MultiTreeHashCache, TreeHashCache};
|
||||
use compare_fields_derive::CompareFields;
|
||||
use eth2_hashing::hash;
|
||||
use int_to_bytes::{int_to_bytes32, int_to_bytes8};
|
||||
use int_to_bytes::{int_to_bytes4, int_to_bytes8};
|
||||
use pubkey_cache::PubkeyCache;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use ssz::ssz_encode;
|
||||
use ssz_derive::{Decode, Encode};
|
||||
use ssz_types::{typenum::Unsigned, BitVector, FixedVector};
|
||||
use swap_or_not_shuffle::compute_shuffled_index;
|
||||
use test_random_derive::TestRandom;
|
||||
use tree_hash::TreeHash;
|
||||
use tree_hash_derive::{CachedTreeHash, TreeHash};
|
||||
@@ -31,7 +32,6 @@ const MAX_RANDOM_BYTE: u64 = (1 << 8) - 1;
|
||||
pub enum Error {
|
||||
EpochOutOfBounds,
|
||||
SlotOutOfBounds,
|
||||
ShardOutOfBounds,
|
||||
UnknownValidator,
|
||||
UnableToDetermineProducer,
|
||||
InvalidBitfield,
|
||||
@@ -45,8 +45,10 @@ pub enum Error {
|
||||
InsufficientAttestations,
|
||||
InsufficientCommittees,
|
||||
InsufficientStateRoots,
|
||||
NoCommitteeForShard,
|
||||
NoCommitteeForSlot,
|
||||
NoCommittee {
|
||||
slot: Slot,
|
||||
index: CommitteeIndex,
|
||||
},
|
||||
ZeroSlotsPerEpoch,
|
||||
PubkeyCacheInconsistent,
|
||||
PubkeyCacheIncomplete {
|
||||
@@ -56,7 +58,7 @@ pub enum Error {
|
||||
PreviousCommitteeCacheUninitialized,
|
||||
CurrentCommitteeCacheUninitialized,
|
||||
RelativeEpochError(RelativeEpochError),
|
||||
CommitteeCacheUninitialized(RelativeEpoch),
|
||||
CommitteeCacheUninitialized(Option<RelativeEpoch>),
|
||||
SszTypesError(ssz_types::Error),
|
||||
CachedTreeHashError(cached_tree_hash::Error),
|
||||
}
|
||||
@@ -86,8 +88,6 @@ pub struct BeaconTreeHashCache {
|
||||
validators: MultiTreeHashCache,
|
||||
balances: TreeHashCache,
|
||||
randao_mixes: TreeHashCache,
|
||||
active_index_roots: TreeHashCache,
|
||||
compact_committees_roots: TreeHashCache,
|
||||
slashings: TreeHashCache,
|
||||
}
|
||||
|
||||
@@ -99,7 +99,7 @@ impl BeaconTreeHashCache {
|
||||
|
||||
/// The state of the `BeaconChain` at some slot.
|
||||
///
|
||||
/// Spec v0.8.0
|
||||
/// Spec v0.9.0
|
||||
#[derive(
|
||||
Debug,
|
||||
PartialEq,
|
||||
@@ -148,16 +148,9 @@ where
|
||||
#[cached_tree_hash(balances)]
|
||||
pub balances: VariableList<u64, T::ValidatorRegistryLimit>,
|
||||
|
||||
// Shuffling
|
||||
pub start_shard: u64,
|
||||
// Randomness
|
||||
#[cached_tree_hash(randao_mixes)]
|
||||
pub randao_mixes: FixedVector<Hash256, T::EpochsPerHistoricalVector>,
|
||||
#[compare_fields(as_slice)]
|
||||
#[cached_tree_hash(active_index_roots)]
|
||||
pub active_index_roots: FixedVector<Hash256, T::EpochsPerHistoricalVector>,
|
||||
#[compare_fields(as_slice)]
|
||||
#[cached_tree_hash(compact_committees_roots)]
|
||||
pub compact_committees_roots: FixedVector<Hash256, T::EpochsPerHistoricalVector>,
|
||||
|
||||
// Slashings
|
||||
#[cached_tree_hash(slashings)]
|
||||
@@ -167,10 +160,6 @@ where
|
||||
pub previous_epoch_attestations: VariableList<PendingAttestation<T>, T::MaxPendingAttestations>,
|
||||
pub current_epoch_attestations: VariableList<PendingAttestation<T>, T::MaxPendingAttestations>,
|
||||
|
||||
// Crosslinks
|
||||
pub previous_crosslinks: FixedVector<Crosslink, T::ShardCount>,
|
||||
pub current_crosslinks: FixedVector<Crosslink, T::ShardCount>,
|
||||
|
||||
// Finality
|
||||
#[test_random(default)]
|
||||
pub justification_bits: BitVector<T::JustificationBitsLength>,
|
||||
@@ -210,7 +199,7 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
///
|
||||
/// Not a complete genesis state, see `initialize_beacon_state_from_eth1`.
|
||||
///
|
||||
/// Spec v0.8.0
|
||||
/// Spec v0.9.0
|
||||
pub fn new(genesis_time: u64, eth1_data: Eth1Data, spec: &ChainSpec) -> Self {
|
||||
BeaconState {
|
||||
// Versioning
|
||||
@@ -233,11 +222,8 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
validators: VariableList::empty(), // Set later.
|
||||
balances: VariableList::empty(), // Set later.
|
||||
|
||||
// Shuffling
|
||||
start_shard: 0,
|
||||
// Randomness
|
||||
randao_mixes: FixedVector::from_elem(Hash256::zero()),
|
||||
active_index_roots: FixedVector::from_elem(Hash256::zero()),
|
||||
compact_committees_roots: FixedVector::from_elem(Hash256::zero()),
|
||||
|
||||
// Slashings
|
||||
slashings: FixedVector::from_elem(0),
|
||||
@@ -246,10 +232,6 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
previous_epoch_attestations: VariableList::empty(),
|
||||
current_epoch_attestations: VariableList::empty(),
|
||||
|
||||
// Crosslinks
|
||||
previous_crosslinks: FixedVector::from_elem(Crosslink::default()),
|
||||
current_crosslinks: FixedVector::from_elem(Crosslink::default()),
|
||||
|
||||
// Finality
|
||||
justification_bits: BitVector::new(),
|
||||
previous_justified_checkpoint: Checkpoint::default(),
|
||||
@@ -270,7 +252,7 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
|
||||
/// Returns the `tree_hash_root` of the state.
|
||||
///
|
||||
/// Spec v0.8.1
|
||||
/// Spec v0.9.0
|
||||
pub fn canonical_root(&self) -> Hash256 {
|
||||
Hash256::from_slice(&self.tree_hash_root()[..])
|
||||
}
|
||||
@@ -299,7 +281,7 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
|
||||
/// The epoch corresponding to `self.slot`.
|
||||
///
|
||||
/// Spec v0.8.1
|
||||
/// Spec v0.9.0
|
||||
pub fn current_epoch(&self) -> Epoch {
|
||||
self.slot.epoch(T::slots_per_epoch())
|
||||
}
|
||||
@@ -308,7 +290,7 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
///
|
||||
/// If the current epoch is the genesis epoch, the genesis_epoch is returned.
|
||||
///
|
||||
/// Spec v0.8.1
|
||||
/// Spec v0.9.0
|
||||
pub fn previous_epoch(&self) -> Epoch {
|
||||
let current_epoch = self.current_epoch();
|
||||
if current_epoch > T::genesis_epoch() {
|
||||
@@ -320,43 +302,25 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
|
||||
/// The epoch following `self.current_epoch()`.
|
||||
///
|
||||
/// Spec v0.8.1
|
||||
/// Spec v0.9.0
|
||||
pub fn next_epoch(&self) -> Epoch {
|
||||
self.current_epoch() + 1
|
||||
}
|
||||
|
||||
pub fn get_committee_count(&self, relative_epoch: RelativeEpoch) -> Result<u64, Error> {
|
||||
// FIXME(sproul): comments
|
||||
pub fn get_committee_count_at_slot(&self, slot: Slot) -> Result<u64, Error> {
|
||||
// TODO(sproul): factor into cache at slot method?
|
||||
let epoch = slot.epoch(T::slots_per_epoch());
|
||||
let relative_epoch = RelativeEpoch::from_epoch(self.current_epoch(), epoch)?;
|
||||
let cache = self.cache(relative_epoch)?;
|
||||
Ok(cache.committees_per_slot() as u64)
|
||||
}
|
||||
|
||||
pub fn get_epoch_committee_count(&self, relative_epoch: RelativeEpoch) -> Result<u64, Error> {
|
||||
let cache = self.cache(relative_epoch)?;
|
||||
Ok(cache.epoch_committee_count() as u64)
|
||||
}
|
||||
|
||||
pub fn get_epoch_start_shard(&self, relative_epoch: RelativeEpoch) -> Result<u64, Error> {
|
||||
let cache = self.cache(relative_epoch)?;
|
||||
|
||||
Ok(cache.epoch_start_shard())
|
||||
}
|
||||
|
||||
/// Get the slot of an attestation.
|
||||
///
|
||||
/// Note: Utilizes the cache and will fail if the appropriate cache is not initialized.
|
||||
///
|
||||
/// Spec v0.8.0
|
||||
pub fn get_attestation_data_slot(
|
||||
&self,
|
||||
attestation_data: &AttestationData,
|
||||
) -> Result<Slot, Error> {
|
||||
let target_relative_epoch =
|
||||
RelativeEpoch::from_epoch(self.current_epoch(), attestation_data.target.epoch)?;
|
||||
|
||||
let cc = self.get_crosslink_committee_for_shard(
|
||||
attestation_data.crosslink.shard,
|
||||
target_relative_epoch,
|
||||
)?;
|
||||
|
||||
Ok(cc.slot)
|
||||
}
|
||||
|
||||
/// Return the cached active validator indices at some epoch.
|
||||
///
|
||||
/// Note: the indices are shuffled (i.e., not in ascending order).
|
||||
@@ -375,7 +339,7 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
///
|
||||
/// Does not utilize the cache, performs a full iteration over the validator registry.
|
||||
///
|
||||
/// Spec v0.8.1
|
||||
/// Spec v0.9.0
|
||||
pub fn get_active_validator_indices(&self, epoch: Epoch) -> Vec<usize> {
|
||||
get_active_validator_indices(&self.validators, epoch)
|
||||
}
|
||||
@@ -391,86 +355,97 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
Ok(cache.shuffling())
|
||||
}
|
||||
|
||||
/// Returns the crosslink committees for some slot.
|
||||
/// Get the Beacon committee at the given slot and index.
|
||||
///
|
||||
/// Note: Utilizes the cache and will fail if the appropriate cache is not initialized.
|
||||
///
|
||||
/// Spec v0.8.1
|
||||
pub fn get_crosslink_committees_at_slot(
|
||||
/// Spec v0.9.0
|
||||
pub fn get_beacon_committee(
|
||||
&self,
|
||||
slot: Slot,
|
||||
) -> Result<Vec<CrosslinkCommittee>, Error> {
|
||||
let relative_epoch = RelativeEpoch::from_slot(self.slot, slot, T::slots_per_epoch())?;
|
||||
index: CommitteeIndex,
|
||||
) -> Result<BeaconCommittee, Error> {
|
||||
let epoch = slot.epoch(T::slots_per_epoch());
|
||||
let relative_epoch = RelativeEpoch::from_epoch(self.current_epoch(), epoch)?;
|
||||
let cache = self.cache(relative_epoch)?;
|
||||
|
||||
cache
|
||||
.get_crosslink_committees_for_slot(slot)
|
||||
.ok_or_else(|| Error::NoCommitteeForSlot)
|
||||
.get_beacon_committee(slot, index)
|
||||
.ok_or(Error::NoCommittee { slot, index })
|
||||
}
|
||||
|
||||
/// Returns the crosslink committees for some shard in some cached epoch.
|
||||
///
|
||||
/// Note: Utilizes the cache and will fail if the appropriate cache is not initialized.
|
||||
///
|
||||
/// Spec v0.8.1
|
||||
pub fn get_crosslink_committee_for_shard(
|
||||
&self,
|
||||
shard: u64,
|
||||
relative_epoch: RelativeEpoch,
|
||||
) -> Result<CrosslinkCommittee, Error> {
|
||||
// TODO(sproul): optimise?
|
||||
pub fn get_beacon_committees_at_slot(&self, slot: Slot) -> Result<Vec<BeaconCommittee>, Error> {
|
||||
let epoch = slot.epoch(T::slots_per_epoch());
|
||||
let relative_epoch = RelativeEpoch::from_epoch(self.current_epoch(), epoch)?;
|
||||
let cache = self.cache(relative_epoch)?;
|
||||
|
||||
let committee = cache
|
||||
.get_crosslink_committee_for_shard(shard)
|
||||
.ok_or_else(|| Error::NoCommitteeForShard)?;
|
||||
|
||||
Ok(committee)
|
||||
cache.get_beacon_committees_at_slot(slot)
|
||||
}
|
||||
|
||||
/// Returns the beacon proposer index for the `slot` in the given `relative_epoch`.
|
||||
/// Compute the proposer (not necessarily for the Beacon chain) from a list of indices.
|
||||
///
|
||||
/// Spec v0.8.1
|
||||
/// Spec v0.9.0
|
||||
// NOTE: be sure to test this bad boy.
|
||||
pub fn get_beacon_proposer_index(
|
||||
pub fn compute_proposer_index(
|
||||
&self,
|
||||
slot: Slot,
|
||||
relative_epoch: RelativeEpoch,
|
||||
indices: &[usize],
|
||||
seed: &[u8],
|
||||
spec: &ChainSpec,
|
||||
) -> Result<usize, Error> {
|
||||
let cache = self.cache(relative_epoch)?;
|
||||
let epoch = relative_epoch.into_epoch(self.current_epoch());
|
||||
|
||||
let first_committee = cache
|
||||
.first_committee_at_slot(slot)
|
||||
.ok_or_else(|| Error::SlotOutOfBounds)?;
|
||||
let seed = self.get_seed(epoch, spec)?;
|
||||
|
||||
if first_committee.is_empty() {
|
||||
if indices.is_empty() {
|
||||
return Err(Error::InsufficientValidators);
|
||||
}
|
||||
|
||||
let mut i = 0;
|
||||
Ok(loop {
|
||||
let candidate_index = first_committee[(epoch.as_usize() + i) % first_committee.len()];
|
||||
loop {
|
||||
let candidate_index = indices[compute_shuffled_index(
|
||||
i % indices.len(),
|
||||
indices.len(),
|
||||
seed,
|
||||
spec.shuffle_round_count,
|
||||
)
|
||||
.ok_or(Error::UnableToShuffle)?];
|
||||
let random_byte = {
|
||||
let mut preimage = seed.as_bytes().to_vec();
|
||||
let mut preimage = seed.to_vec();
|
||||
preimage.append(&mut int_to_bytes8((i / 32) as u64));
|
||||
let hash = hash(&preimage);
|
||||
hash[i % 32]
|
||||
};
|
||||
let effective_balance = self.validators[candidate_index].effective_balance;
|
||||
if (effective_balance * MAX_RANDOM_BYTE)
|
||||
>= (spec.max_effective_balance * u64::from(random_byte))
|
||||
if effective_balance * MAX_RANDOM_BYTE
|
||||
>= spec.max_effective_balance * u64::from(random_byte)
|
||||
{
|
||||
break candidate_index;
|
||||
return Ok(candidate_index);
|
||||
}
|
||||
i += 1;
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the beacon proposer index for the `slot` in the given `relative_epoch`.
|
||||
///
|
||||
/// Spec v0.9.0
|
||||
pub fn get_beacon_proposer_index(&self, slot: Slot, spec: &ChainSpec) -> Result<usize, Error> {
|
||||
let epoch = slot.epoch(T::slots_per_epoch());
|
||||
let seed = self.get_beacon_proposer_seed(slot, spec)?;
|
||||
let indices = self.get_active_validator_indices(epoch);
|
||||
|
||||
self.compute_proposer_index(&indices, &seed, spec)
|
||||
}
|
||||
|
||||
/// Compute the seed to use for the beacon proposer selection at the given `slot`.
|
||||
///
|
||||
/// Spec v0.9.0
|
||||
fn get_beacon_proposer_seed(&self, slot: Slot, spec: &ChainSpec) -> Result<Vec<u8>, Error> {
|
||||
let epoch = slot.epoch(T::slots_per_epoch());
|
||||
let mut preimage = self
|
||||
.get_seed(epoch, Domain::BeaconProposer, spec)?
|
||||
.as_bytes()
|
||||
.to_vec();
|
||||
preimage.append(&mut int_to_bytes8(slot.as_u64()));
|
||||
Ok(hash(&preimage))
|
||||
}
|
||||
|
||||
/// Safely obtains the index for latest block roots, given some `slot`.
|
||||
///
|
||||
/// Spec v0.8.1
|
||||
/// Spec v0.9.0
|
||||
fn get_latest_block_roots_index(&self, slot: Slot) -> Result<usize, Error> {
|
||||
if (slot < self.slot) && (self.slot <= slot + self.block_roots.len() as u64) {
|
||||
Ok(slot.as_usize() % self.block_roots.len())
|
||||
@@ -481,7 +456,7 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
|
||||
/// Return the block root at a recent `slot`.
|
||||
///
|
||||
/// Spec v0.8.1
|
||||
/// Spec v0.9.0
|
||||
pub fn get_block_root(&self, slot: Slot) -> Result<&Hash256, BeaconStateError> {
|
||||
let i = self.get_latest_block_roots_index(slot)?;
|
||||
Ok(&self.block_roots[i])
|
||||
@@ -489,7 +464,7 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
|
||||
/// Return the block root at a recent `epoch`.
|
||||
///
|
||||
/// Spec v0.8.1
|
||||
/// Spec v0.9.0
|
||||
// NOTE: the spec calls this get_block_root
|
||||
pub fn get_block_root_at_epoch(&self, epoch: Epoch) -> Result<&Hash256, BeaconStateError> {
|
||||
self.get_block_root(epoch.start_slot(T::slots_per_epoch()))
|
||||
@@ -497,7 +472,7 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
|
||||
/// Sets the block root for some given slot.
|
||||
///
|
||||
/// Spec v0.8.1
|
||||
/// Spec v0.9.0
|
||||
pub fn set_block_root(
|
||||
&mut self,
|
||||
slot: Slot,
|
||||
@@ -508,9 +483,14 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Fill `randao_mixes` with
|
||||
pub fn fill_randao_mixes_with(&mut self, index_root: Hash256) {
|
||||
self.randao_mixes = FixedVector::from_elem(index_root);
|
||||
}
|
||||
|
||||
/// Safely obtains the index for `randao_mixes`
|
||||
///
|
||||
/// Spec v0.8.1
|
||||
/// Spec v0.9.0
|
||||
fn get_randao_mix_index(
|
||||
&self,
|
||||
epoch: Epoch,
|
||||
@@ -532,7 +512,7 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
///
|
||||
/// See `Self::get_randao_mix`.
|
||||
///
|
||||
/// Spec v0.8.0
|
||||
/// Spec v0.9.0
|
||||
pub fn update_randao_mix(&mut self, epoch: Epoch, signature: &Signature) -> Result<(), Error> {
|
||||
let i = epoch.as_usize() % T::EpochsPerHistoricalVector::to_usize();
|
||||
|
||||
@@ -545,7 +525,7 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
|
||||
/// Return the randao mix at a recent ``epoch``.
|
||||
///
|
||||
/// Spec v0.8.1
|
||||
/// Spec v0.9.0
|
||||
pub fn get_randao_mix(&self, epoch: Epoch) -> Result<&Hash256, Error> {
|
||||
let i = self.get_randao_mix_index(epoch, AllowNextEpoch::False)?;
|
||||
Ok(&self.randao_mixes[i])
|
||||
@@ -553,115 +533,16 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
|
||||
/// Set the randao mix at a recent ``epoch``.
|
||||
///
|
||||
/// Spec v0.8.1
|
||||
/// Spec v0.9.0
|
||||
pub fn set_randao_mix(&mut self, epoch: Epoch, mix: Hash256) -> Result<(), Error> {
|
||||
let i = self.get_randao_mix_index(epoch, AllowNextEpoch::True)?;
|
||||
self.randao_mixes[i] = mix;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Safely obtains the index for `active_index_roots`, given some `epoch`.
|
||||
///
|
||||
/// If `allow_next_epoch` is `True`, then we allow an _extra_ one epoch of lookahead.
|
||||
///
|
||||
/// Spec v0.8.1
|
||||
fn get_active_index_root_index(
|
||||
&self,
|
||||
epoch: Epoch,
|
||||
spec: &ChainSpec,
|
||||
allow_next_epoch: AllowNextEpoch,
|
||||
) -> Result<usize, Error> {
|
||||
let current_epoch = self.current_epoch();
|
||||
|
||||
let lookahead = spec.activation_exit_delay;
|
||||
let lookback = self.active_index_roots.len() as u64 - lookahead;
|
||||
let epoch_upper_bound = allow_next_epoch.upper_bound_of(current_epoch) + lookahead;
|
||||
|
||||
if current_epoch < epoch + lookback && epoch <= epoch_upper_bound {
|
||||
Ok(epoch.as_usize() % self.active_index_roots.len())
|
||||
} else {
|
||||
Err(Error::EpochOutOfBounds)
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the `active_index_root` at a recent `epoch`.
|
||||
///
|
||||
/// Spec v0.8.1
|
||||
pub fn get_active_index_root(&self, epoch: Epoch, spec: &ChainSpec) -> Result<Hash256, Error> {
|
||||
let i = self.get_active_index_root_index(epoch, spec, AllowNextEpoch::False)?;
|
||||
Ok(self.active_index_roots[i])
|
||||
}
|
||||
|
||||
/// Set the `active_index_root` at a recent `epoch`.
|
||||
///
|
||||
/// Spec v0.8.1
|
||||
pub fn set_active_index_root(
|
||||
&mut self,
|
||||
epoch: Epoch,
|
||||
index_root: Hash256,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<(), Error> {
|
||||
let i = self.get_active_index_root_index(epoch, spec, AllowNextEpoch::True)?;
|
||||
self.active_index_roots[i] = index_root;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Replace `active_index_roots` with clones of `index_root`.
|
||||
///
|
||||
/// Spec v0.8.0
|
||||
pub fn fill_active_index_roots_with(&mut self, index_root: Hash256) {
|
||||
self.active_index_roots = FixedVector::from_elem(index_root);
|
||||
}
|
||||
|
||||
/// Safely obtains the index for `compact_committees_roots`, given some `epoch`.
|
||||
///
|
||||
/// Spec v0.8.1
|
||||
fn get_compact_committee_root_index(
|
||||
&self,
|
||||
epoch: Epoch,
|
||||
allow_next_epoch: AllowNextEpoch,
|
||||
) -> Result<usize, Error> {
|
||||
let current_epoch = self.current_epoch();
|
||||
let len = T::EpochsPerHistoricalVector::to_u64();
|
||||
|
||||
if current_epoch < epoch + len && epoch <= allow_next_epoch.upper_bound_of(current_epoch) {
|
||||
Ok(epoch.as_usize() % len as usize)
|
||||
} else {
|
||||
Err(Error::EpochOutOfBounds)
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the `compact_committee_root` at a recent `epoch`.
|
||||
///
|
||||
/// Spec v0.8.1
|
||||
pub fn get_compact_committee_root(&self, epoch: Epoch) -> Result<Hash256, Error> {
|
||||
let i = self.get_compact_committee_root_index(epoch, AllowNextEpoch::False)?;
|
||||
Ok(self.compact_committees_roots[i])
|
||||
}
|
||||
|
||||
/// Set the `compact_committee_root` at a recent `epoch`.
|
||||
///
|
||||
/// Spec v0.8.1
|
||||
pub fn set_compact_committee_root(
|
||||
&mut self,
|
||||
epoch: Epoch,
|
||||
index_root: Hash256,
|
||||
) -> Result<(), Error> {
|
||||
let i = self.get_compact_committee_root_index(epoch, AllowNextEpoch::True)?;
|
||||
self.compact_committees_roots[i] = index_root;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Replace `compact_committees_roots` with clones of `committee_root`.
|
||||
///
|
||||
/// Spec v0.8.0
|
||||
pub fn fill_compact_committees_roots_with(&mut self, committee_root: Hash256) {
|
||||
self.compact_committees_roots = FixedVector::from_elem(committee_root);
|
||||
}
|
||||
|
||||
/// Safely obtains the index for latest state roots, given some `slot`.
|
||||
///
|
||||
/// Spec v0.8.1
|
||||
/// Spec v0.9.0
|
||||
fn get_latest_state_roots_index(&self, slot: Slot) -> Result<usize, Error> {
|
||||
if (slot < self.slot) && (self.slot <= slot + Slot::from(self.state_roots.len())) {
|
||||
Ok(slot.as_usize() % self.state_roots.len())
|
||||
@@ -672,7 +553,7 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
|
||||
/// Gets the state root for some slot.
|
||||
///
|
||||
/// Spec v0.8.1
|
||||
/// Spec v0.9.0
|
||||
pub fn get_state_root(&self, slot: Slot) -> Result<&Hash256, Error> {
|
||||
let i = self.get_latest_state_roots_index(slot)?;
|
||||
Ok(&self.state_roots[i])
|
||||
@@ -680,7 +561,7 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
|
||||
/// Gets the oldest (earliest slot) state root.
|
||||
///
|
||||
/// Spec v0.8.1
|
||||
/// Spec v0.9.0
|
||||
pub fn get_oldest_state_root(&self) -> Result<&Hash256, Error> {
|
||||
let i =
|
||||
self.get_latest_state_roots_index(self.slot - Slot::from(self.state_roots.len()))?;
|
||||
@@ -689,7 +570,7 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
|
||||
/// Sets the latest state root for slot.
|
||||
///
|
||||
/// Spec v0.8.1
|
||||
/// Spec v0.9.0
|
||||
pub fn set_state_root(&mut self, slot: Slot, state_root: Hash256) -> Result<(), Error> {
|
||||
let i = self.get_latest_state_roots_index(slot)?;
|
||||
self.state_roots[i] = state_root;
|
||||
@@ -698,7 +579,7 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
|
||||
/// Safely obtain the index for `slashings`, given some `epoch`.
|
||||
///
|
||||
/// Spec v0.8.1
|
||||
/// Spec v0.9.0
|
||||
fn get_slashings_index(
|
||||
&self,
|
||||
epoch: Epoch,
|
||||
@@ -718,14 +599,14 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
|
||||
/// Get a reference to the entire `slashings` vector.
|
||||
///
|
||||
/// Spec v0.8.0
|
||||
/// Spec v0.9.0
|
||||
pub fn get_all_slashings(&self) -> &[u64] {
|
||||
&self.slashings
|
||||
}
|
||||
|
||||
/// Get the total slashed balances for some epoch.
|
||||
///
|
||||
/// Spec v0.8.1
|
||||
/// Spec v0.9.0
|
||||
pub fn get_slashings(&self, epoch: Epoch) -> Result<u64, Error> {
|
||||
let i = self.get_slashings_index(epoch, AllowNextEpoch::False)?;
|
||||
Ok(self.slashings[i])
|
||||
@@ -733,7 +614,7 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
|
||||
/// Set the total slashed balances for some epoch.
|
||||
///
|
||||
/// Spec v0.8.1
|
||||
/// Spec v0.9.0
|
||||
pub fn set_slashings(&mut self, epoch: Epoch, value: u64) -> Result<(), Error> {
|
||||
let i = self.get_slashings_index(epoch, AllowNextEpoch::True)?;
|
||||
self.slashings[i] = value;
|
||||
@@ -742,7 +623,7 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
|
||||
/// Get the attestations from the current or previous epoch.
|
||||
///
|
||||
/// Spec v0.8.1
|
||||
/// Spec v0.9.0
|
||||
pub fn get_matching_source_attestations(
|
||||
&self,
|
||||
epoch: Epoch,
|
||||
@@ -756,48 +637,40 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the current crosslink for a shard.
|
||||
///
|
||||
/// Spec v0.8.1
|
||||
pub fn get_current_crosslink(&self, shard: u64) -> Result<&Crosslink, Error> {
|
||||
self.current_crosslinks
|
||||
.get(shard as usize)
|
||||
.ok_or(Error::ShardOutOfBounds)
|
||||
}
|
||||
|
||||
/// Get the previous crosslink for a shard.
|
||||
///
|
||||
/// Spec v0.8.1
|
||||
pub fn get_previous_crosslink(&self, shard: u64) -> Result<&Crosslink, Error> {
|
||||
self.previous_crosslinks
|
||||
.get(shard as usize)
|
||||
.ok_or(Error::ShardOutOfBounds)
|
||||
}
|
||||
|
||||
/// Generate a seed for the given `epoch`.
|
||||
///
|
||||
/// Spec v0.8.0
|
||||
pub fn get_seed(&self, epoch: Epoch, spec: &ChainSpec) -> Result<Hash256, Error> {
|
||||
/// Spec v0.9.0
|
||||
pub fn get_seed(
|
||||
&self,
|
||||
epoch: Epoch,
|
||||
domain_type: Domain,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<Hash256, Error> {
|
||||
// Bypass the safe getter for RANDAO so we can gracefully handle the scenario where `epoch
|
||||
// == 0`.
|
||||
let randao = {
|
||||
let mix = {
|
||||
let i = epoch + T::EpochsPerHistoricalVector::to_u64() - spec.min_seed_lookahead - 1;
|
||||
self.randao_mixes[i.as_usize() % self.randao_mixes.len()]
|
||||
};
|
||||
let active_index_root = self.get_active_index_root(epoch, spec)?;
|
||||
let epoch_bytes = int_to_bytes32(epoch.as_u64());
|
||||
let domain_bytes = int_to_bytes4(spec.get_domain_constant(domain_type));
|
||||
let epoch_bytes = int_to_bytes8(epoch.as_u64());
|
||||
|
||||
let mut preimage = [0; 32 * 3];
|
||||
preimage[0..32].copy_from_slice(&randao[..]);
|
||||
preimage[32..64].copy_from_slice(&active_index_root[..]);
|
||||
preimage[64..].copy_from_slice(&epoch_bytes);
|
||||
const NUM_DOMAIN_BYTES: usize = 4;
|
||||
const NUM_EPOCH_BYTES: usize = 8;
|
||||
const NUM_MIX_BYTES: usize = 32;
|
||||
|
||||
let mut preimage = [0; NUM_DOMAIN_BYTES + NUM_EPOCH_BYTES + NUM_MIX_BYTES];
|
||||
preimage[0..NUM_DOMAIN_BYTES].copy_from_slice(&domain_bytes);
|
||||
preimage[NUM_DOMAIN_BYTES..NUM_DOMAIN_BYTES + NUM_EPOCH_BYTES]
|
||||
.copy_from_slice(&epoch_bytes);
|
||||
preimage[NUM_DOMAIN_BYTES + NUM_EPOCH_BYTES..].copy_from_slice(mix.as_bytes());
|
||||
|
||||
Ok(Hash256::from_slice(&hash(&preimage)))
|
||||
}
|
||||
|
||||
/// Return the effective balance (also known as "balance at stake") for a validator with the given ``index``.
|
||||
///
|
||||
/// Spec v0.8.1
|
||||
/// Spec v0.9.0
|
||||
pub fn get_effective_balance(
|
||||
&self,
|
||||
validator_index: usize,
|
||||
@@ -811,16 +684,16 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
|
||||
/// Return the epoch at which an activation or exit triggered in ``epoch`` takes effect.
|
||||
///
|
||||
/// Spec v0.8.1
|
||||
/// Spec v0.9.0
|
||||
pub fn compute_activation_exit_epoch(&self, epoch: Epoch, spec: &ChainSpec) -> Epoch {
|
||||
epoch + 1 + spec.activation_exit_delay
|
||||
epoch + 1 + spec.max_seed_lookahead
|
||||
}
|
||||
|
||||
/// Return the churn limit for the current epoch (number of validators who can leave per epoch).
|
||||
///
|
||||
/// Uses the epoch cache, and will error if it isn't initialized.
|
||||
///
|
||||
/// Spec v0.8.1
|
||||
/// Spec v0.9.0
|
||||
pub fn get_churn_limit(&self, spec: &ChainSpec) -> Result<u64, Error> {
|
||||
Ok(std::cmp::max(
|
||||
spec.min_per_epoch_churn_limit,
|
||||
@@ -829,12 +702,12 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
))
|
||||
}
|
||||
|
||||
/// Returns the `slot`, `shard` and `committee_index` for which a validator must produce an
|
||||
/// Returns the `slot`, `index` and `committee_position` for which a validator must produce an
|
||||
/// attestation.
|
||||
///
|
||||
/// Note: Utilizes the cache and will fail if the appropriate cache is not initialized.
|
||||
///
|
||||
/// Spec v0.8.1
|
||||
/// Spec v0.9.0
|
||||
pub fn get_attestation_duties(
|
||||
&self,
|
||||
validator_index: usize,
|
||||
@@ -847,7 +720,7 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
|
||||
/// Return the combined effective balance of an array of validators.
|
||||
///
|
||||
/// Spec v0.8.1
|
||||
/// Spec v0.9.0
|
||||
pub fn get_total_balance(
|
||||
&self,
|
||||
validator_indices: &[usize],
|
||||
@@ -936,13 +809,14 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
|
||||
/// Returns the cache for some `RelativeEpoch`. Returns an error if the cache has not been
|
||||
/// initialized.
|
||||
// FIXME(sproul): rename to commitee_cache
|
||||
fn cache(&self, relative_epoch: RelativeEpoch) -> Result<&CommitteeCache, Error> {
|
||||
let cache = &self.committee_caches[Self::cache_index(relative_epoch)];
|
||||
|
||||
if cache.is_initialized_at(relative_epoch.into_epoch(self.current_epoch())) {
|
||||
Ok(cache)
|
||||
} else {
|
||||
Err(Error::CommitteeCacheUninitialized(relative_epoch))
|
||||
Err(Error::CommitteeCacheUninitialized(Some(relative_epoch)))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,22 +15,20 @@ pub struct CommitteeCache {
|
||||
initialized_epoch: Option<Epoch>,
|
||||
shuffling: Vec<usize>,
|
||||
shuffling_positions: Vec<Option<NonZeroUsize>>,
|
||||
shuffling_start_shard: u64,
|
||||
shard_count: u64,
|
||||
committee_count: usize,
|
||||
committees_per_slot: u64,
|
||||
slots_per_epoch: u64,
|
||||
}
|
||||
|
||||
impl CommitteeCache {
|
||||
/// Return a new, fully initialized cache.
|
||||
///
|
||||
/// Spec v0.8.1
|
||||
/// Spec v0.9.0
|
||||
pub fn initialized<T: EthSpec>(
|
||||
state: &BeaconState<T>,
|
||||
epoch: Epoch,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<CommitteeCache, Error> {
|
||||
let relative_epoch = RelativeEpoch::from_epoch(state.current_epoch(), epoch)
|
||||
RelativeEpoch::from_epoch(state.current_epoch(), epoch)
|
||||
.map_err(|_| Error::EpochOutOfBounds)?;
|
||||
|
||||
// May cause divide-by-zero errors.
|
||||
@@ -44,14 +42,10 @@ impl CommitteeCache {
|
||||
return Err(Error::InsufficientValidators);
|
||||
}
|
||||
|
||||
let committee_count =
|
||||
T::get_committee_count(active_validator_indices.len(), spec.target_committee_size)
|
||||
as usize;
|
||||
let committees_per_slot =
|
||||
T::get_committee_count_per_slot(active_validator_indices.len(), spec) as u64;
|
||||
|
||||
let shuffling_start_shard =
|
||||
Self::compute_start_shard(state, relative_epoch, active_validator_indices.len(), spec);
|
||||
|
||||
let seed = state.get_seed(epoch, spec)?;
|
||||
let seed = state.get_seed(epoch, Domain::BeaconAttester, spec)?;
|
||||
|
||||
let shuffling = shuffle_list(
|
||||
active_validator_indices,
|
||||
@@ -73,46 +67,13 @@ impl CommitteeCache {
|
||||
|
||||
Ok(CommitteeCache {
|
||||
initialized_epoch: Some(epoch),
|
||||
shuffling_start_shard,
|
||||
shuffling,
|
||||
shard_count: T::shard_count() as u64,
|
||||
committee_count,
|
||||
slots_per_epoch: T::slots_per_epoch(),
|
||||
shuffling_positions,
|
||||
committees_per_slot,
|
||||
slots_per_epoch: T::slots_per_epoch(),
|
||||
})
|
||||
}
|
||||
|
||||
/// Compute the shard which must be attested to first in a given relative epoch.
|
||||
///
|
||||
/// The `active_validator_count` must be the number of validators active at `relative_epoch`.
|
||||
///
|
||||
/// Spec v0.8.1
|
||||
pub fn compute_start_shard<T: EthSpec>(
|
||||
state: &BeaconState<T>,
|
||||
relative_epoch: RelativeEpoch,
|
||||
active_validator_count: usize,
|
||||
spec: &ChainSpec,
|
||||
) -> u64 {
|
||||
match relative_epoch {
|
||||
RelativeEpoch::Current => state.start_shard,
|
||||
RelativeEpoch::Previous => {
|
||||
let shard_delta =
|
||||
T::get_shard_delta(active_validator_count, spec.target_committee_size);
|
||||
|
||||
(state.start_shard + T::ShardCount::to_u64() - shard_delta)
|
||||
% T::ShardCount::to_u64()
|
||||
}
|
||||
RelativeEpoch::Next => {
|
||||
let current_active_validators =
|
||||
get_active_validator_count(&state.validators, state.current_epoch());
|
||||
let shard_delta =
|
||||
T::get_shard_delta(current_active_validators, spec.target_committee_size);
|
||||
|
||||
(state.start_shard + shard_delta) % T::ShardCount::to_u64()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if the cache has been initialized at the supplied `epoch`.
|
||||
///
|
||||
/// An non-initialized cache does not provide any useful information.
|
||||
@@ -126,7 +87,7 @@ impl CommitteeCache {
|
||||
///
|
||||
/// Always returns `&[]` for a non-initialized epoch.
|
||||
///
|
||||
/// Spec v0.8.1
|
||||
/// Spec v0.9.0
|
||||
pub fn active_validator_indices(&self) -> &[usize] {
|
||||
&self.shuffling
|
||||
}
|
||||
@@ -135,34 +96,44 @@ impl CommitteeCache {
|
||||
///
|
||||
/// Always returns `&[]` for a non-initialized epoch.
|
||||
///
|
||||
/// Spec v0.8.1
|
||||
/// Spec v0.9.0
|
||||
pub fn shuffling(&self) -> &[usize] {
|
||||
&self.shuffling
|
||||
}
|
||||
|
||||
/// Return `Some(CrosslinkCommittee)` if the given shard has a committee during the given
|
||||
/// `epoch`.
|
||||
///
|
||||
/// Always returns `None` for a non-initialized epoch.
|
||||
///
|
||||
/// Spec v0.8.1
|
||||
pub fn get_crosslink_committee_for_shard(&self, shard: Shard) -> Option<CrosslinkCommittee> {
|
||||
if shard >= self.shard_count || self.initialized_epoch.is_none() {
|
||||
pub fn get_beacon_committee(
|
||||
&self,
|
||||
slot: Slot,
|
||||
index: CommitteeIndex,
|
||||
) -> Option<BeaconCommittee> {
|
||||
if self.initialized_epoch.is_none() || index >= self.committees_per_slot {
|
||||
return None;
|
||||
}
|
||||
|
||||
let committee_index =
|
||||
(shard + self.shard_count - self.shuffling_start_shard) % self.shard_count;
|
||||
(slot.as_u64() % self.slots_per_epoch) * self.committees_per_slot + index;
|
||||
let committee = self.compute_committee(committee_index as usize)?;
|
||||
let slot = self.crosslink_slot_for_shard(shard)?;
|
||||
|
||||
Some(CrosslinkCommittee {
|
||||
shard,
|
||||
committee,
|
||||
Some(BeaconCommittee {
|
||||
slot,
|
||||
index,
|
||||
committee,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_beacon_committees_at_slot(&self, slot: Slot) -> Result<Vec<BeaconCommittee>, Error> {
|
||||
if self.initialized_epoch.is_none() {
|
||||
return Err(Error::CommitteeCacheUninitialized(None));
|
||||
}
|
||||
|
||||
(0..self.committees_per_slot())
|
||||
.map(|index| {
|
||||
self.get_beacon_committee(slot, index)
|
||||
.ok_or(Error::NoCommittee { slot, index })
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Returns the `AttestationDuty` for the given `validator_index`.
|
||||
///
|
||||
/// Returns `None` if the `validator_index` does not exist, does not have duties or `Self` is
|
||||
@@ -170,36 +141,45 @@ impl CommitteeCache {
|
||||
pub fn get_attestation_duties(&self, validator_index: usize) -> Option<AttestationDuty> {
|
||||
let i = self.shuffled_position(validator_index)?;
|
||||
|
||||
(0..self.committee_count)
|
||||
(0..self.epoch_committee_count())
|
||||
.map(|nth_committee| (nth_committee, self.compute_committee_range(nth_committee)))
|
||||
.find(|(_, range)| {
|
||||
if let Some(range) = range {
|
||||
(range.start <= i) && (range.end > i)
|
||||
range.start <= i && range.end > i
|
||||
} else {
|
||||
false
|
||||
}
|
||||
})
|
||||
.and_then(|(nth_committee, range)| {
|
||||
let shard = (self.shuffling_start_shard + nth_committee as u64) % self.shard_count;
|
||||
let slot = self.crosslink_slot_for_shard(shard)?;
|
||||
let (slot, index) = self.convert_to_slot_and_index(nth_committee as u64)?;
|
||||
let range = range?;
|
||||
let committee_index = i - range.start;
|
||||
let committee_position = i - range.start;
|
||||
let committee_len = range.end - range.start;
|
||||
|
||||
Some(AttestationDuty {
|
||||
slot,
|
||||
shard,
|
||||
committee_index,
|
||||
index,
|
||||
committee_position,
|
||||
committee_len,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
fn convert_to_slot_and_index(
|
||||
&self,
|
||||
global_committee_index: u64,
|
||||
) -> Option<(Slot, CommitteeIndex)> {
|
||||
let epoch_start_slot = self.initialized_epoch?.start_slot(self.slots_per_epoch);
|
||||
let slot_offset = global_committee_index / self.committees_per_slot;
|
||||
let index = global_committee_index % self.committees_per_slot;
|
||||
Some((epoch_start_slot + slot_offset, index))
|
||||
}
|
||||
|
||||
/// Returns the number of active validators in the initialized epoch.
|
||||
///
|
||||
/// Always returns `usize::default()` for a non-initialized epoch.
|
||||
///
|
||||
/// Spec v0.8.1
|
||||
/// Spec v0.9.0
|
||||
pub fn active_validator_count(&self) -> usize {
|
||||
self.shuffling.len()
|
||||
}
|
||||
@@ -208,64 +188,18 @@ impl CommitteeCache {
|
||||
///
|
||||
/// Always returns `usize::default()` for a non-initialized epoch.
|
||||
///
|
||||
/// Spec v0.8.1
|
||||
/// Spec v0.9.0
|
||||
pub fn epoch_committee_count(&self) -> usize {
|
||||
self.committee_count
|
||||
self.committees_per_slot as usize * self.slots_per_epoch as usize
|
||||
}
|
||||
|
||||
/// Returns the shard assigned to the first committee in the initialized epoch.
|
||||
///
|
||||
/// Always returns `u64::default()` for a non-initialized epoch.
|
||||
pub fn epoch_start_shard(&self) -> u64 {
|
||||
self.shuffling_start_shard
|
||||
}
|
||||
|
||||
/// Returns all crosslink committees, if any, for the given slot in the initialized epoch.
|
||||
///
|
||||
/// Returns `None` if `slot` is not in the initialized epoch, or if `Self` is not initialized.
|
||||
///
|
||||
/// Spec v0.8.1
|
||||
pub fn get_crosslink_committees_for_slot(&self, slot: Slot) -> Option<Vec<CrosslinkCommittee>> {
|
||||
let position = self
|
||||
.initialized_epoch?
|
||||
.position(slot, self.slots_per_epoch)?;
|
||||
let committees_per_slot = self.committee_count / self.slots_per_epoch as usize;
|
||||
let position = position * committees_per_slot;
|
||||
|
||||
if position >= self.committee_count {
|
||||
None
|
||||
} else {
|
||||
let mut committees = Vec::with_capacity(committees_per_slot);
|
||||
|
||||
for index in position..position + committees_per_slot {
|
||||
let committee = self.compute_committee(index)?;
|
||||
let shard = (self.shuffling_start_shard + index as u64) % self.shard_count;
|
||||
|
||||
committees.push(CrosslinkCommittee {
|
||||
committee,
|
||||
shard,
|
||||
slot,
|
||||
});
|
||||
}
|
||||
|
||||
Some(committees)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the first committee of the first slot of the initialized epoch.
|
||||
///
|
||||
/// Always returns `None` for a non-initialized epoch.
|
||||
///
|
||||
/// Spec v0.8.1
|
||||
pub fn first_committee_at_slot(&self, slot: Slot) -> Option<&[usize]> {
|
||||
self.get_crosslink_committees_for_slot(slot)?
|
||||
.first()
|
||||
.and_then(|cc| Some(cc.committee))
|
||||
pub fn committees_per_slot(&self) -> u64 {
|
||||
self.committees_per_slot
|
||||
}
|
||||
|
||||
/// Returns a slice of `self.shuffling` that represents the `index`'th committee in the epoch.
|
||||
///
|
||||
/// Spec v0.8.1
|
||||
/// Spec v0.9.0
|
||||
fn compute_committee(&self, index: usize) -> Option<&[usize]> {
|
||||
Some(&self.shuffling[self.compute_committee_range(index)?])
|
||||
}
|
||||
@@ -276,34 +210,20 @@ impl CommitteeCache {
|
||||
///
|
||||
/// Will also return `None` if the index is out of bounds.
|
||||
///
|
||||
/// Spec v0.8.1
|
||||
/// Spec v0.9.0
|
||||
fn compute_committee_range(&self, index: usize) -> Option<Range<usize>> {
|
||||
if self.committee_count == 0 || index >= self.committee_count {
|
||||
let count = self.epoch_committee_count();
|
||||
if count == 0 || index >= count {
|
||||
return None;
|
||||
}
|
||||
|
||||
let num_validators = self.shuffling.len();
|
||||
let count = self.committee_count;
|
||||
|
||||
let start = (num_validators * index) / count;
|
||||
let end = (num_validators * (index + 1)) / count;
|
||||
|
||||
Some(start..end)
|
||||
}
|
||||
|
||||
/// Returns the `slot` that `shard` will be crosslink-ed in during the initialized epoch.
|
||||
///
|
||||
/// Always returns `None` for a non-initialized epoch.
|
||||
///
|
||||
/// Spec v0.8.1
|
||||
fn crosslink_slot_for_shard(&self, shard: u64) -> Option<Slot> {
|
||||
let offset = (shard + self.shard_count - self.shuffling_start_shard) % self.shard_count;
|
||||
Some(
|
||||
self.initialized_epoch?.start_slot(self.slots_per_epoch)
|
||||
+ offset / (self.committee_count as u64 / self.slots_per_epoch),
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns the index of some validator in `self.shuffling`.
|
||||
///
|
||||
/// Always returns `None` for a non-initialized epoch.
|
||||
@@ -317,7 +237,7 @@ impl CommitteeCache {
|
||||
/// Returns a list of all `validators` indices where the validator is active at the given
|
||||
/// `epoch`.
|
||||
///
|
||||
/// Spec v0.8.1
|
||||
/// Spec v0.9.0
|
||||
pub fn get_active_validator_indices(validators: &[Validator], epoch: Epoch) -> Vec<usize> {
|
||||
let mut active = Vec::with_capacity(validators.len());
|
||||
|
||||
@@ -332,10 +252,12 @@ pub fn get_active_validator_indices(validators: &[Validator], epoch: Epoch) -> V
|
||||
active
|
||||
}
|
||||
|
||||
/*
|
||||
/// Returns the count of all `validators` indices where the validator is active at the given
|
||||
/// `epoch`.
|
||||
///
|
||||
/// Spec v0.8.1
|
||||
/// Spec v0.9.0
|
||||
fn get_active_validator_count(validators: &[Validator], epoch: Epoch) -> usize {
|
||||
validators.iter().filter(|v| v.is_active_at(epoch)).count()
|
||||
}
|
||||
*/
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
#![cfg(test)]
|
||||
use super::*;
|
||||
use crate::{test_utils::*, *};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use ssz_types::typenum::*;
|
||||
|
||||
#[test]
|
||||
fn default_values() {
|
||||
@@ -10,13 +8,11 @@ fn default_values() {
|
||||
|
||||
assert_eq!(cache.is_initialized_at(Epoch::new(0)), false);
|
||||
assert!(&cache.active_validator_indices().is_empty());
|
||||
assert_eq!(cache.get_crosslink_committee_for_shard(0), None);
|
||||
assert_eq!(cache.get_beacon_committee(Slot::new(0), 0), None);
|
||||
assert_eq!(cache.get_attestation_duties(0), None);
|
||||
assert_eq!(cache.active_validator_count(), 0);
|
||||
assert_eq!(cache.epoch_committee_count(), 0);
|
||||
assert_eq!(cache.epoch_start_shard(), 0);
|
||||
assert_eq!(cache.get_crosslink_committees_for_slot(Slot::new(0)), None);
|
||||
assert_eq!(cache.first_committee_at_slot(Slot::new(0)), None);
|
||||
assert!(cache.get_beacon_committees_at_slot(Slot::new(0)).is_err());
|
||||
}
|
||||
|
||||
fn new_state<T: EthSpec>(validator_count: usize, slot: Slot) -> BeaconState<T> {
|
||||
@@ -78,9 +74,15 @@ fn shuffles_for_the_right_epoch() {
|
||||
|
||||
state.randao_mixes = FixedVector::from(distinct_hashes);
|
||||
|
||||
let previous_seed = state.get_seed(state.previous_epoch(), spec).unwrap();
|
||||
let current_seed = state.get_seed(state.current_epoch(), spec).unwrap();
|
||||
let next_seed = state.get_seed(state.next_epoch(), spec).unwrap();
|
||||
let previous_seed = state
|
||||
.get_seed(state.previous_epoch(), Domain::BeaconAttester, spec)
|
||||
.unwrap();
|
||||
let current_seed = state
|
||||
.get_seed(state.current_epoch(), Domain::BeaconAttester, spec)
|
||||
.unwrap();
|
||||
let next_seed = state
|
||||
.get_seed(state.next_epoch(), Domain::BeaconAttester, spec)
|
||||
.unwrap();
|
||||
|
||||
assert!((previous_seed != current_seed) && (current_seed != next_seed));
|
||||
|
||||
@@ -116,153 +118,3 @@ fn shuffles_for_the_right_epoch() {
|
||||
assert_eq!(cache.shuffling, shuffling_with_seed(next_seed));
|
||||
assert_shuffling_positions_accurate(&cache);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_start_on_any_shard() {
|
||||
let num_validators = MinimalEthSpec::minimum_validator_count() * 2;
|
||||
let epoch = Epoch::new(100_000_000);
|
||||
let slot = epoch.start_slot(MinimalEthSpec::slots_per_epoch());
|
||||
|
||||
let mut state = new_state::<MinimalEthSpec>(num_validators, slot);
|
||||
let spec = &MinimalEthSpec::default_spec();
|
||||
|
||||
let target_committee_size = MinimalEthSpec::default_spec().target_committee_size;
|
||||
|
||||
let shard_delta = MinimalEthSpec::get_shard_delta(num_validators, target_committee_size);
|
||||
let shard_count = MinimalEthSpec::shard_count() as u64;
|
||||
|
||||
for i in 0..MinimalEthSpec::shard_count() as u64 {
|
||||
state.start_shard = i;
|
||||
|
||||
let cache = CommitteeCache::initialized(&state, state.current_epoch(), spec).unwrap();
|
||||
assert_eq!(cache.shuffling_start_shard, i);
|
||||
|
||||
let cache = CommitteeCache::initialized(&state, state.previous_epoch(), spec).unwrap();
|
||||
assert_eq!(
|
||||
cache.shuffling_start_shard,
|
||||
(i + shard_count - shard_delta) % shard_count
|
||||
);
|
||||
|
||||
let cache = CommitteeCache::initialized(&state, state.next_epoch(), spec).unwrap();
|
||||
assert_eq!(cache.shuffling_start_shard, (i + shard_delta) % shard_count);
|
||||
}
|
||||
}
|
||||
|
||||
/// This spec has more shards than slots in an epoch, permitting epochs where not all shards are
|
||||
/// included in the committee.
|
||||
#[derive(Clone, PartialEq, Debug, Default, Serialize, Deserialize)]
|
||||
pub struct ExcessShardsEthSpec;
|
||||
|
||||
impl EthSpec for ExcessShardsEthSpec {
|
||||
type ShardCount = U128;
|
||||
type SlotsPerEpoch = U8;
|
||||
type MaxPendingAttestations = U1024;
|
||||
|
||||
params_from_eth_spec!(MinimalEthSpec {
|
||||
JustificationBitsLength,
|
||||
MaxValidatorsPerCommittee,
|
||||
GenesisEpoch,
|
||||
SlotsPerEth1VotingPeriod,
|
||||
SlotsPerHistoricalRoot,
|
||||
EpochsPerHistoricalVector,
|
||||
EpochsPerSlashingsVector,
|
||||
HistoricalRootsLimit,
|
||||
ValidatorRegistryLimit,
|
||||
MaxProposerSlashings,
|
||||
MaxAttesterSlashings,
|
||||
MaxAttestations,
|
||||
MaxDeposits,
|
||||
MaxVoluntaryExits,
|
||||
MaxTransfers
|
||||
});
|
||||
|
||||
fn default_spec() -> ChainSpec {
|
||||
ChainSpec::minimal()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn starts_on_the_correct_shard() {
|
||||
let spec = &ExcessShardsEthSpec::default_spec();
|
||||
|
||||
let num_validators = spec.target_committee_size * ExcessShardsEthSpec::shard_count();
|
||||
|
||||
let epoch = Epoch::new(100_000_000);
|
||||
let slot = epoch.start_slot(ExcessShardsEthSpec::slots_per_epoch());
|
||||
|
||||
let mut state = new_state::<ExcessShardsEthSpec>(num_validators, slot);
|
||||
|
||||
let validator_count = state.validators.len();
|
||||
|
||||
let previous_epoch = state.previous_epoch();
|
||||
let current_epoch = state.current_epoch();
|
||||
let next_epoch = state.next_epoch();
|
||||
|
||||
for (i, mut v) in state.validators.iter_mut().enumerate() {
|
||||
let epoch = if i < validator_count / 4 {
|
||||
previous_epoch
|
||||
} else if i < validator_count / 2 {
|
||||
current_epoch
|
||||
} else {
|
||||
next_epoch
|
||||
};
|
||||
|
||||
v.activation_epoch = epoch;
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
get_active_validator_count(&state.validators, previous_epoch),
|
||||
validator_count / 4
|
||||
);
|
||||
assert_eq!(
|
||||
get_active_validator_count(&state.validators, current_epoch),
|
||||
validator_count / 2
|
||||
);
|
||||
assert_eq!(
|
||||
get_active_validator_count(&state.validators, next_epoch),
|
||||
validator_count
|
||||
);
|
||||
|
||||
let previous_shards = ExcessShardsEthSpec::get_committee_count(
|
||||
get_active_validator_count(&state.validators, previous_epoch),
|
||||
spec.target_committee_size,
|
||||
);
|
||||
let current_shards = ExcessShardsEthSpec::get_committee_count(
|
||||
get_active_validator_count(&state.validators, current_epoch),
|
||||
spec.target_committee_size,
|
||||
);
|
||||
let next_shards = ExcessShardsEthSpec::get_committee_count(
|
||||
get_active_validator_count(&state.validators, next_epoch),
|
||||
spec.target_committee_size,
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
previous_shards as usize,
|
||||
ExcessShardsEthSpec::shard_count() / 4
|
||||
);
|
||||
assert_eq!(
|
||||
current_shards as usize,
|
||||
ExcessShardsEthSpec::shard_count() / 2
|
||||
);
|
||||
assert_eq!(next_shards as usize, ExcessShardsEthSpec::shard_count());
|
||||
|
||||
let shard_count = ExcessShardsEthSpec::shard_count();
|
||||
for i in 0..ExcessShardsEthSpec::shard_count() {
|
||||
state.start_shard = i as u64;
|
||||
|
||||
let cache = CommitteeCache::initialized(&state, state.current_epoch(), spec).unwrap();
|
||||
assert_eq!(cache.shuffling_start_shard as usize, i);
|
||||
|
||||
let cache = CommitteeCache::initialized(&state, state.previous_epoch(), spec).unwrap();
|
||||
assert_eq!(
|
||||
cache.shuffling_start_shard as usize,
|
||||
(i + shard_count - previous_shards) % shard_count
|
||||
);
|
||||
|
||||
let cache = CommitteeCache::initialized(&state, state.next_epoch(), spec).unwrap();
|
||||
assert_eq!(
|
||||
cache.shuffling_start_shard as usize,
|
||||
(i + current_shards) % shard_count
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#![cfg(test)]
|
||||
use super::*;
|
||||
use crate::test_utils::*;
|
||||
use std::ops::RangeInclusive;
|
||||
|
||||
ssz_tests!(FoundationBeaconState);
|
||||
|
||||
@@ -19,34 +18,52 @@ fn test_beacon_proposer_index<T: EthSpec>() {
|
||||
state
|
||||
};
|
||||
|
||||
// Get the i'th candidate proposer for the given state and slot
|
||||
let ith_candidate = |state: &BeaconState<T>, slot: Slot, i: usize| {
|
||||
let epoch = slot.epoch(T::slots_per_epoch());
|
||||
let seed = state.get_beacon_proposer_seed(slot, &spec).unwrap();
|
||||
let active_validators = state.get_active_validator_indices(epoch);
|
||||
active_validators[compute_shuffled_index(
|
||||
i,
|
||||
active_validators.len(),
|
||||
&seed,
|
||||
spec.shuffle_round_count,
|
||||
)
|
||||
.unwrap()]
|
||||
};
|
||||
|
||||
// Run a test on the state.
|
||||
let test = |state: &BeaconState<T>, slot: Slot, shuffling_index: usize| {
|
||||
let shuffling = state.get_shuffling(relative_epoch).unwrap();
|
||||
let test = |state: &BeaconState<T>, slot: Slot, candidate_index: usize| {
|
||||
assert_eq!(
|
||||
state.get_beacon_proposer_index(slot, relative_epoch, &spec),
|
||||
Ok(shuffling[shuffling_index])
|
||||
state.get_beacon_proposer_index(slot, &spec),
|
||||
Ok(ith_candidate(state, slot, candidate_index))
|
||||
);
|
||||
};
|
||||
|
||||
// Test where we have one validator per slot
|
||||
// Test where we have one validator per slot.
|
||||
// 0th candidate should be chosen every time.
|
||||
let state = build_state(T::slots_per_epoch() as usize);
|
||||
for i in 0..T::slots_per_epoch() {
|
||||
test(&state, Slot::from(i), i as usize);
|
||||
println!("i = {}", i);
|
||||
test(&state, Slot::from(i), 0);
|
||||
}
|
||||
|
||||
// Test where we have two validators per slot
|
||||
// Test where we have two validators per slot.
|
||||
// 0th candidate should be chosen every time.
|
||||
let state = build_state(T::slots_per_epoch() as usize * 2);
|
||||
for i in 0..T::slots_per_epoch() {
|
||||
test(&state, Slot::from(i), i as usize * 2);
|
||||
println!("i = {}", i);
|
||||
test(&state, Slot::from(i), 0);
|
||||
}
|
||||
|
||||
// Test with two validators per slot, first validator has zero balance.
|
||||
let mut state = build_state(T::slots_per_epoch() as usize * 2);
|
||||
let shuffling = state.get_shuffling(relative_epoch).unwrap().to_vec();
|
||||
state.validators[shuffling[0]].effective_balance = 0;
|
||||
let slot0_candidate0 = ith_candidate(&state, Slot::new(0), 0);
|
||||
state.validators[slot0_candidate0].effective_balance = 0;
|
||||
test(&state, Slot::new(0), 1);
|
||||
for i in 1..T::slots_per_epoch() {
|
||||
test(&state, Slot::from(i), i as usize * 2);
|
||||
println!("i = {}", i);
|
||||
test(&state, Slot::from(i), 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,72 +72,6 @@ fn beacon_proposer_index() {
|
||||
test_beacon_proposer_index::<MinimalEthSpec>();
|
||||
}
|
||||
|
||||
/// Should produce (note the set notation brackets):
|
||||
///
|
||||
/// (current_epoch - LATEST_ACTIVE_INDEX_ROOTS_LENGTH + ACTIVATION_EXIT_DELAY, current_epoch +
|
||||
/// ACTIVATION_EXIT_DELAY]
|
||||
fn active_index_range<T: EthSpec>(current_epoch: Epoch) -> RangeInclusive<Epoch> {
|
||||
let delay = T::default_spec().activation_exit_delay;
|
||||
|
||||
let start: i32 =
|
||||
current_epoch.as_u64() as i32 - T::epochs_per_historical_vector() as i32 + delay as i32;
|
||||
let end = current_epoch + delay;
|
||||
|
||||
let start: Epoch = if start < 0 {
|
||||
Epoch::new(0)
|
||||
} else {
|
||||
Epoch::from(start as u64 + 1)
|
||||
};
|
||||
|
||||
start..=end
|
||||
}
|
||||
|
||||
/// Test getting an active index root at the start and end of the valid range, and one either side
|
||||
/// of that range.
|
||||
fn test_active_index<T: EthSpec>(state_slot: Slot) {
|
||||
let spec = T::default_spec();
|
||||
let builder: TestingBeaconStateBuilder<T> =
|
||||
TestingBeaconStateBuilder::from_default_keypairs_file_if_exists(16, &spec);
|
||||
let (mut state, _keypairs) = builder.build();
|
||||
state.slot = state_slot;
|
||||
|
||||
let range = active_index_range::<T>(state.current_epoch());
|
||||
|
||||
let modulo = |epoch: Epoch| epoch.as_usize() % T::epochs_per_historical_vector();
|
||||
|
||||
// Test the start and end of the range.
|
||||
assert_eq!(
|
||||
state.get_active_index_root_index(*range.start(), &spec, AllowNextEpoch::False),
|
||||
Ok(modulo(*range.start()))
|
||||
);
|
||||
assert_eq!(
|
||||
state.get_active_index_root_index(*range.end(), &spec, AllowNextEpoch::False),
|
||||
Ok(modulo(*range.end()))
|
||||
);
|
||||
|
||||
// One either side of the range.
|
||||
if state.current_epoch() > 0 {
|
||||
// Test is invalid on epoch zero, cannot subtract from zero.
|
||||
assert_eq!(
|
||||
state.get_active_index_root_index(*range.start() - 1, &spec, AllowNextEpoch::False),
|
||||
Err(Error::EpochOutOfBounds)
|
||||
);
|
||||
}
|
||||
assert_eq!(
|
||||
state.get_active_index_root_index(*range.end() + 1, &spec, AllowNextEpoch::False),
|
||||
Err(Error::EpochOutOfBounds)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get_active_index_root_index() {
|
||||
test_active_index::<MainnetEthSpec>(Slot::new(0));
|
||||
|
||||
let epoch = Epoch::from(MainnetEthSpec::epochs_per_historical_vector() * 4);
|
||||
let slot = epoch.start_slot(MainnetEthSpec::slots_per_epoch());
|
||||
test_active_index::<MainnetEthSpec>(slot);
|
||||
}
|
||||
|
||||
/// Test that
|
||||
///
|
||||
/// 1. Using the cache before it's built fails.
|
||||
@@ -138,28 +89,26 @@ fn test_cache_initialization<'a, T: EthSpec>(
|
||||
// Assuming the cache isn't already built, assert that a call to a cache-using function fails.
|
||||
assert_eq!(
|
||||
state.get_attestation_duties(0, relative_epoch),
|
||||
Err(BeaconStateError::CommitteeCacheUninitialized(
|
||||
Err(BeaconStateError::CommitteeCacheUninitialized(Some(
|
||||
relative_epoch
|
||||
))
|
||||
)))
|
||||
);
|
||||
|
||||
// Build the cache.
|
||||
state.build_committee_cache(relative_epoch, spec).unwrap();
|
||||
|
||||
// Assert a call to a cache-using function passes.
|
||||
let _ = state
|
||||
.get_beacon_proposer_index(slot, relative_epoch, spec)
|
||||
.unwrap();
|
||||
let _ = state.get_beacon_proposer_index(slot, spec).unwrap();
|
||||
|
||||
// Drop the cache.
|
||||
state.drop_committee_cache(relative_epoch);
|
||||
|
||||
// Assert a call to a cache-using function fail.
|
||||
assert_eq!(
|
||||
state.get_beacon_proposer_index(slot, relative_epoch, spec),
|
||||
Err(BeaconStateError::CommitteeCacheUninitialized(
|
||||
state.get_beacon_committee(slot, 0),
|
||||
Err(BeaconStateError::CommitteeCacheUninitialized(Some(
|
||||
relative_epoch
|
||||
))
|
||||
)))
|
||||
);
|
||||
}
|
||||
|
||||
@@ -212,10 +161,8 @@ mod committees {
|
||||
spec: &ChainSpec,
|
||||
) {
|
||||
let active_indices: Vec<usize> = (0..validator_count).collect();
|
||||
let seed = state.get_seed(epoch, spec).unwrap();
|
||||
let seed = state.get_seed(epoch, Domain::BeaconAttester, spec).unwrap();
|
||||
let relative_epoch = RelativeEpoch::from_epoch(state.current_epoch(), epoch).unwrap();
|
||||
let start_shard =
|
||||
CommitteeCache::compute_start_shard(&state, relative_epoch, active_indices.len(), spec);
|
||||
|
||||
let mut ordered_indices = state
|
||||
.get_cached_active_validator_indices(relative_epoch)
|
||||
@@ -231,34 +178,27 @@ mod committees {
|
||||
shuffle_list(active_indices, spec.shuffle_round_count, &seed[..], false).unwrap();
|
||||
|
||||
let mut expected_indices_iter = shuffling.iter();
|
||||
let mut expected_shards_iter =
|
||||
(0..T::ShardCount::to_u64()).map(|i| (start_shard + i) % T::ShardCount::to_u64());
|
||||
|
||||
// Loop through all slots in the epoch being tested.
|
||||
for slot in epoch.slot_iter(T::slots_per_epoch()) {
|
||||
let crosslink_committees = state.get_crosslink_committees_at_slot(slot).unwrap();
|
||||
let beacon_committees = state.get_beacon_committees_at_slot(slot).unwrap();
|
||||
|
||||
// Assert that the number of committees in this slot is consistent with the reported number
|
||||
// of committees in an epoch.
|
||||
assert_eq!(
|
||||
crosslink_committees.len() as u64,
|
||||
state.get_committee_count(relative_epoch).unwrap() / T::slots_per_epoch()
|
||||
beacon_committees.len() as u64,
|
||||
state.get_epoch_committee_count(relative_epoch).unwrap() / T::slots_per_epoch()
|
||||
);
|
||||
|
||||
for cc in crosslink_committees {
|
||||
// Assert that shards are assigned contiguously across committees.
|
||||
assert_eq!(expected_shards_iter.next().unwrap(), cc.shard);
|
||||
for (committee_index, bc) in beacon_committees.iter().enumerate() {
|
||||
// Assert that indices are assigned sequentially across committees.
|
||||
assert_eq!(committee_index as u64, bc.index);
|
||||
// Assert that a committee lookup via slot is identical to a committee lookup via
|
||||
// shard.
|
||||
assert_eq!(
|
||||
state
|
||||
.get_crosslink_committee_for_shard(cc.shard, relative_epoch)
|
||||
.unwrap(),
|
||||
cc
|
||||
);
|
||||
// index.
|
||||
assert_eq!(state.get_beacon_committee(bc.slot, bc.index).unwrap(), *bc);
|
||||
|
||||
// Loop through each validator in the committee.
|
||||
for (committee_i, validator_i) in cc.committee.iter().enumerate() {
|
||||
for (committee_i, validator_i) in bc.committee.iter().enumerate() {
|
||||
// Assert the validators are assigned contiguously across committees.
|
||||
assert_eq!(
|
||||
*validator_i,
|
||||
@@ -266,24 +206,21 @@ mod committees {
|
||||
"Non-sequential validators."
|
||||
);
|
||||
// Assert a call to `get_attestation_duties` is consistent with a call to
|
||||
// `get_crosslink_committees_at_slot`
|
||||
// `get_beacon_committees_at_slot`
|
||||
let attestation_duty = state
|
||||
.get_attestation_duties(*validator_i, relative_epoch)
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
assert_eq!(attestation_duty.slot, slot);
|
||||
assert_eq!(attestation_duty.shard, cc.shard);
|
||||
assert_eq!(attestation_duty.committee_index, committee_i);
|
||||
assert_eq!(attestation_duty.committee_len, cc.committee.len());
|
||||
assert_eq!(attestation_duty.index, bc.index);
|
||||
assert_eq!(attestation_duty.committee_position, committee_i);
|
||||
assert_eq!(attestation_duty.committee_len, bc.committee.len());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Assert that all validators were assigned to a committee.
|
||||
assert!(expected_indices_iter.next().is_none());
|
||||
|
||||
// Assert that all shards were assigned to a committee.
|
||||
assert!(expected_shards_iter.next().is_none());
|
||||
}
|
||||
|
||||
fn committee_consistency_test<T: EthSpec>(
|
||||
@@ -327,7 +264,10 @@ mod committees {
|
||||
fn committee_consistency_test_suite<T: EthSpec>(cached_epoch: RelativeEpoch) {
|
||||
let spec = T::default_spec();
|
||||
|
||||
let validator_count = (T::shard_count() * spec.target_committee_size) + 1;
|
||||
let validator_count = spec.max_committees_per_slot
|
||||
* T::slots_per_epoch() as usize
|
||||
* spec.target_committee_size
|
||||
+ 1;
|
||||
|
||||
committee_consistency_test::<T>(validator_count as usize, Epoch::new(0), cached_epoch);
|
||||
|
||||
|
||||
@@ -5,19 +5,18 @@ use utils::{u8_from_hex_str, u8_to_hex_str};
|
||||
|
||||
/// Each of the BLS signature domains.
|
||||
///
|
||||
/// Spec v0.8.1
|
||||
/// Spec v0.9.0
|
||||
pub enum Domain {
|
||||
BeaconProposer,
|
||||
BeaconAttester,
|
||||
Randao,
|
||||
Attestation,
|
||||
Deposit,
|
||||
VoluntaryExit,
|
||||
Transfer,
|
||||
}
|
||||
|
||||
/// Holds all the "constants" for a BeaconChain.
|
||||
///
|
||||
/// Spec v0.8.1
|
||||
/// Spec v0.9.0
|
||||
#[derive(PartialEq, Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(default)]
|
||||
pub struct ChainSpec {
|
||||
@@ -33,6 +32,7 @@ pub struct ChainSpec {
|
||||
/*
|
||||
* Misc
|
||||
*/
|
||||
pub max_committees_per_slot: usize,
|
||||
pub target_committee_size: usize,
|
||||
pub min_per_epoch_churn_limit: u64,
|
||||
pub churn_limit_quotient: u64,
|
||||
@@ -61,10 +61,9 @@ pub struct ChainSpec {
|
||||
pub milliseconds_per_slot: u64,
|
||||
pub min_attestation_inclusion_delay: u64,
|
||||
pub min_seed_lookahead: Epoch,
|
||||
pub activation_exit_delay: u64,
|
||||
pub max_seed_lookahead: Epoch,
|
||||
pub min_validator_withdrawability_delay: Epoch,
|
||||
pub persistent_committee_period: u64,
|
||||
pub max_epochs_per_crosslink: u64,
|
||||
pub min_epochs_to_inactivity_penalty: u64,
|
||||
|
||||
/*
|
||||
@@ -78,36 +77,36 @@ pub struct ChainSpec {
|
||||
|
||||
/*
|
||||
* Signature domains
|
||||
*
|
||||
* Fields should be private to prevent accessing a domain that hasn't been modified to suit
|
||||
* some `Fork`.
|
||||
*
|
||||
* Use `ChainSpec::get_domain(..)` to access these values.
|
||||
*/
|
||||
domain_beacon_proposer: u32,
|
||||
domain_beacon_attester: u32,
|
||||
domain_randao: u32,
|
||||
domain_attestation: u32,
|
||||
domain_deposit: u32,
|
||||
domain_voluntary_exit: u32,
|
||||
domain_transfer: u32,
|
||||
|
||||
pub boot_nodes: Vec<String>,
|
||||
pub network_id: u8,
|
||||
}
|
||||
|
||||
impl ChainSpec {
|
||||
/// Get the domain number that represents the fork meta and signature domain.
|
||||
/// Get the domain number, unmodified by the fork.
|
||||
///
|
||||
/// Spec v0.8.1
|
||||
pub fn get_domain(&self, epoch: Epoch, domain: Domain, fork: &Fork) -> u64 {
|
||||
let domain_constant = match domain {
|
||||
/// Spec v0.9.0
|
||||
pub fn get_domain_constant(&self, domain: Domain) -> u32 {
|
||||
match domain {
|
||||
Domain::BeaconProposer => self.domain_beacon_proposer,
|
||||
Domain::BeaconAttester => self.domain_beacon_attester,
|
||||
Domain::Randao => self.domain_randao,
|
||||
Domain::Attestation => self.domain_attestation,
|
||||
Domain::Deposit => self.domain_deposit,
|
||||
Domain::VoluntaryExit => self.domain_voluntary_exit,
|
||||
Domain::Transfer => self.domain_transfer,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the domain number that represents the fork meta and signature domain.
|
||||
///
|
||||
/// Spec v0.9.0
|
||||
pub fn get_domain(&self, epoch: Epoch, domain: Domain, fork: &Fork) -> u64 {
|
||||
let domain_constant = self.get_domain_constant(domain);
|
||||
|
||||
let mut bytes: Vec<u8> = int_to_bytes4(domain_constant);
|
||||
bytes.append(&mut fork.get_fork_version(epoch).to_vec());
|
||||
@@ -120,20 +119,21 @@ impl ChainSpec {
|
||||
|
||||
/// Returns a `ChainSpec` compatible with the Ethereum Foundation specification.
|
||||
///
|
||||
/// Spec v0.8.1
|
||||
/// Spec v0.9.0
|
||||
pub fn mainnet() -> Self {
|
||||
Self {
|
||||
/*
|
||||
* Constants
|
||||
*/
|
||||
far_future_epoch: Epoch::new(u64::max_value()),
|
||||
base_rewards_per_epoch: 5,
|
||||
base_rewards_per_epoch: 4,
|
||||
deposit_contract_tree_depth: 32,
|
||||
seconds_per_day: 86400,
|
||||
|
||||
/*
|
||||
* Misc
|
||||
*/
|
||||
max_committees_per_slot: 64,
|
||||
target_committee_size: 128,
|
||||
min_per_epoch_churn_limit: 4,
|
||||
churn_limit_quotient: 65_536,
|
||||
@@ -158,13 +158,12 @@ impl ChainSpec {
|
||||
/*
|
||||
* Time parameters
|
||||
*/
|
||||
milliseconds_per_slot: 6_000,
|
||||
milliseconds_per_slot: 12_000,
|
||||
min_attestation_inclusion_delay: 1,
|
||||
min_seed_lookahead: Epoch::new(1),
|
||||
activation_exit_delay: 4,
|
||||
max_seed_lookahead: Epoch::new(4),
|
||||
min_validator_withdrawability_delay: Epoch::new(256),
|
||||
persistent_committee_period: 2_048,
|
||||
max_epochs_per_crosslink: 64,
|
||||
min_epochs_to_inactivity_penalty: 4,
|
||||
|
||||
/*
|
||||
@@ -180,11 +179,10 @@ impl ChainSpec {
|
||||
* Signature domains
|
||||
*/
|
||||
domain_beacon_proposer: 0,
|
||||
domain_randao: 1,
|
||||
domain_attestation: 2,
|
||||
domain_beacon_attester: 1,
|
||||
domain_randao: 2,
|
||||
domain_deposit: 3,
|
||||
domain_voluntary_exit: 4,
|
||||
domain_transfer: 5,
|
||||
|
||||
/*
|
||||
* Network specific
|
||||
@@ -198,7 +196,7 @@ impl ChainSpec {
|
||||
///
|
||||
/// https://github.com/ethereum/eth2.0-specs/blob/v0.8.1/configs/constant_presets/minimal.yaml
|
||||
///
|
||||
/// Spec v0.8.1
|
||||
/// Spec v0.9.0
|
||||
pub fn minimal() -> Self {
|
||||
// Note: bootnodes to be updated when static nodes exist.
|
||||
let boot_nodes = vec![];
|
||||
@@ -207,7 +205,6 @@ impl ChainSpec {
|
||||
target_committee_size: 4,
|
||||
shuffle_round_count: 10,
|
||||
min_genesis_active_validator_count: 64,
|
||||
max_epochs_per_crosslink: 4,
|
||||
network_id: 2, // lighthouse testnet network id
|
||||
boot_nodes,
|
||||
..ChainSpec::mainnet()
|
||||
@@ -264,10 +261,9 @@ mod tests {
|
||||
let spec = ChainSpec::mainnet();
|
||||
|
||||
test_domain(Domain::BeaconProposer, spec.domain_beacon_proposer, &spec);
|
||||
test_domain(Domain::BeaconAttester, spec.domain_beacon_attester, &spec);
|
||||
test_domain(Domain::Randao, spec.domain_randao, &spec);
|
||||
test_domain(Domain::Attestation, spec.domain_attestation, &spec);
|
||||
test_domain(Domain::Deposit, spec.domain_deposit, &spec);
|
||||
test_domain(Domain::VoluntaryExit, spec.domain_voluntary_exit, &spec);
|
||||
test_domain(Domain::Transfer, spec.domain_transfer, &spec);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ use tree_hash_derive::TreeHash;
|
||||
|
||||
/// Casper FFG checkpoint, used in attestations.
|
||||
///
|
||||
/// Spec v0.8.0
|
||||
/// Spec v0.9.0
|
||||
#[derive(
|
||||
Debug,
|
||||
Clone,
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
use crate::test_utils::TestRandom;
|
||||
use crate::{EthSpec, PublicKey};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use ssz_derive::{Decode, Encode};
|
||||
use ssz_types::VariableList;
|
||||
use test_random_derive::TestRandom;
|
||||
use tree_hash_derive::TreeHash;
|
||||
|
||||
/// Spec v0.8.0
|
||||
#[derive(Clone, Debug, PartialEq, TreeHash, Encode, Decode, Serialize, Deserialize, TestRandom)]
|
||||
#[serde(bound = "T: EthSpec")]
|
||||
pub struct CompactCommittee<T: EthSpec> {
|
||||
pub pubkeys: VariableList<PublicKey, T::MaxValidatorsPerCommittee>,
|
||||
pub compact_validators: VariableList<u64, T::MaxValidatorsPerCommittee>,
|
||||
}
|
||||
|
||||
impl<T: EthSpec> Default for CompactCommittee<T> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
pubkeys: VariableList::empty(),
|
||||
compact_validators: VariableList::empty(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
use crate::test_utils::TestRandom;
|
||||
use crate::{Epoch, Hash256};
|
||||
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use ssz_derive::{Decode, Encode};
|
||||
use test_random_derive::TestRandom;
|
||||
use tree_hash_derive::TreeHash;
|
||||
|
||||
/// Specifies the block hash for a shard at an epoch.
|
||||
///
|
||||
/// Spec v0.8.0
|
||||
#[derive(
|
||||
Debug,
|
||||
Clone,
|
||||
PartialEq,
|
||||
Eq,
|
||||
Default,
|
||||
Serialize,
|
||||
Deserialize,
|
||||
Hash,
|
||||
Encode,
|
||||
Decode,
|
||||
TreeHash,
|
||||
TestRandom,
|
||||
)]
|
||||
pub struct Crosslink {
|
||||
pub shard: u64,
|
||||
pub parent_root: Hash256,
|
||||
// Crosslinking data
|
||||
pub start_epoch: Epoch,
|
||||
pub end_epoch: Epoch,
|
||||
pub data_root: Hash256,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
ssz_tests!(Crosslink);
|
||||
}
|
||||
@@ -9,7 +9,7 @@ use tree_hash_derive::TreeHash;
|
||||
|
||||
/// A deposit to potentially become a beacon chain validator.
|
||||
///
|
||||
/// Spec v0.8.0
|
||||
/// Spec v0.9.0
|
||||
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom)]
|
||||
pub struct Deposit {
|
||||
pub proof: FixedVector<Hash256, U33>,
|
||||
|
||||
@@ -11,7 +11,7 @@ use tree_hash_derive::{SignedRoot, TreeHash};
|
||||
|
||||
/// The data supplied by the user to the deposit contract.
|
||||
///
|
||||
/// Spec v0.8.0
|
||||
/// Spec v0.9.0
|
||||
#[derive(
|
||||
Debug,
|
||||
PartialEq,
|
||||
@@ -35,7 +35,7 @@ pub struct DepositData {
|
||||
impl DepositData {
|
||||
/// Generate the signature for a given DepositData details.
|
||||
///
|
||||
/// Spec v0.8.1
|
||||
/// Spec v0.9.0
|
||||
pub fn create_signature(
|
||||
&self,
|
||||
secret_key: &SecretKey,
|
||||
|
||||
@@ -8,7 +8,7 @@ use tree_hash_derive::TreeHash;
|
||||
|
||||
/// Contains data obtained from the Eth1 chain.
|
||||
///
|
||||
/// Spec v0.8.1
|
||||
/// Spec v0.9.0
|
||||
#[derive(
|
||||
Debug, PartialEq, Clone, Default, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom,
|
||||
)]
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use crate::*;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use ssz_types::typenum::{
|
||||
Unsigned, U0, U1, U1024, U1099511627776, U128, U16, U16777216, U4, U4096, U64, U65536, U8,
|
||||
U8192,
|
||||
Unsigned, U0, U1, U1024, U1099511627776, U128, U16, U16777216, U2048, U32, U4, U4096, U64,
|
||||
U65536, U8, U8192,
|
||||
};
|
||||
use std::fmt::Debug;
|
||||
|
||||
@@ -14,7 +14,6 @@ pub trait EthSpec: 'static + Default + Sync + Send + Clone + Debug + PartialEq {
|
||||
/*
|
||||
* Misc
|
||||
*/
|
||||
type ShardCount: Unsigned + Clone + Sync + Send + Debug + PartialEq;
|
||||
type MaxValidatorsPerCommittee: Unsigned + Clone + Sync + Send + Debug + PartialEq;
|
||||
/*
|
||||
* Initial values
|
||||
@@ -41,7 +40,6 @@ pub trait EthSpec: 'static + Default + Sync + Send + Clone + Debug + PartialEq {
|
||||
type MaxAttestations: Unsigned + Clone + Sync + Send + Debug + PartialEq;
|
||||
type MaxDeposits: Unsigned + Clone + Sync + Send + Debug + PartialEq;
|
||||
type MaxVoluntaryExits: Unsigned + Clone + Sync + Send + Debug + PartialEq;
|
||||
type MaxTransfers: Unsigned + Clone + Sync + Send + Debug + PartialEq;
|
||||
/*
|
||||
* Derived values (set these CAREFULLY)
|
||||
*/
|
||||
@@ -58,29 +56,21 @@ pub trait EthSpec: 'static + Default + Sync + Send + Clone + Debug + PartialEq {
|
||||
Epoch::new(Self::GenesisEpoch::to_u64())
|
||||
}
|
||||
|
||||
/// Return the number of committees in one epoch.
|
||||
/// Return the number of committees per slot.
|
||||
///
|
||||
/// Spec v0.8.1
|
||||
fn get_committee_count(active_validator_count: usize, target_committee_size: usize) -> usize {
|
||||
let shard_count = Self::shard_count();
|
||||
let slots_per_epoch = Self::slots_per_epoch() as usize;
|
||||
/// Note: the number of committees per slot is constant in each epoch, and depends only on
|
||||
/// the `active_validator_count` during the slot's epoch.
|
||||
///
|
||||
/// Spec v0.9.0
|
||||
fn get_committee_count_per_slot(active_validator_count: usize, spec: &ChainSpec) -> usize {
|
||||
let slots_per_epoch = Self::SlotsPerEpoch::to_usize();
|
||||
|
||||
std::cmp::max(
|
||||
1,
|
||||
std::cmp::min(
|
||||
shard_count / slots_per_epoch,
|
||||
active_validator_count / slots_per_epoch / target_committee_size,
|
||||
spec.max_committees_per_slot,
|
||||
active_validator_count / slots_per_epoch / spec.target_committee_size,
|
||||
),
|
||||
) * slots_per_epoch
|
||||
}
|
||||
|
||||
/// Return the number of shards to increment `state.start_shard` by in a given epoch.
|
||||
///
|
||||
/// Spec v0.8.1
|
||||
fn get_shard_delta(active_validator_count: usize, target_committee_size: usize) -> u64 {
|
||||
std::cmp::min(
|
||||
Self::get_committee_count(active_validator_count, target_committee_size) as u64,
|
||||
Self::ShardCount::to_u64() - Self::ShardCount::to_u64() / Self::slots_per_epoch(),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -95,35 +85,28 @@ pub trait EthSpec: 'static + Default + Sync + Send + Clone + Debug + PartialEq {
|
||||
|
||||
/// Returns the `SLOTS_PER_EPOCH` constant for this specification.
|
||||
///
|
||||
/// Spec v0.8.1
|
||||
/// Spec v0.9.0
|
||||
fn slots_per_epoch() -> u64 {
|
||||
Self::SlotsPerEpoch::to_u64()
|
||||
}
|
||||
|
||||
/// Returns the `SHARD_COUNT` constant for this specification.
|
||||
///
|
||||
/// Spec v0.8.1
|
||||
fn shard_count() -> usize {
|
||||
Self::ShardCount::to_usize()
|
||||
}
|
||||
|
||||
/// Returns the `SLOTS_PER_HISTORICAL_ROOT` constant for this specification.
|
||||
///
|
||||
/// Spec v0.8.1
|
||||
/// Spec v0.9.0
|
||||
fn slots_per_historical_root() -> usize {
|
||||
Self::SlotsPerHistoricalRoot::to_usize()
|
||||
}
|
||||
|
||||
/// Returns the `EPOCHS_PER_HISTORICAL_VECTOR` constant for this specification.
|
||||
///
|
||||
/// Spec v0.8.1
|
||||
/// Spec v0.9.0
|
||||
fn epochs_per_historical_vector() -> usize {
|
||||
Self::EpochsPerHistoricalVector::to_usize()
|
||||
}
|
||||
|
||||
/// Returns the `SLOTS_PER_ETH1_VOTING_PERIOD` constant for this specification.
|
||||
///
|
||||
/// Spec v0.8.1
|
||||
/// Spec v0.9.0
|
||||
fn slots_per_eth1_voting_period() -> usize {
|
||||
Self::EpochsPerHistoricalVector::to_usize()
|
||||
}
|
||||
@@ -139,16 +122,15 @@ macro_rules! params_from_eth_spec {
|
||||
|
||||
/// Ethereum Foundation specifications.
|
||||
///
|
||||
/// Spec v0.8.0
|
||||
/// Spec v0.9.0
|
||||
#[derive(Clone, PartialEq, Debug, Default, Serialize, Deserialize)]
|
||||
pub struct MainnetEthSpec;
|
||||
|
||||
impl EthSpec for MainnetEthSpec {
|
||||
type JustificationBitsLength = U4;
|
||||
type ShardCount = U1024;
|
||||
type MaxValidatorsPerCommittee = U4096;
|
||||
type MaxValidatorsPerCommittee = U2048;
|
||||
type GenesisEpoch = U0;
|
||||
type SlotsPerEpoch = U64;
|
||||
type SlotsPerEpoch = U32;
|
||||
type SlotsPerEth1VotingPeriod = U1024;
|
||||
type SlotsPerHistoricalRoot = U8192;
|
||||
type EpochsPerHistoricalVector = U65536;
|
||||
@@ -160,8 +142,7 @@ impl EthSpec for MainnetEthSpec {
|
||||
type MaxAttestations = U128;
|
||||
type MaxDeposits = U16;
|
||||
type MaxVoluntaryExits = U16;
|
||||
type MaxTransfers = U0;
|
||||
type MaxPendingAttestations = U8192; // 128 max attestations * 64 slots per epoch
|
||||
type MaxPendingAttestations = U4096; // 128 max attestations * 32 slots per epoch
|
||||
|
||||
fn default_spec() -> ChainSpec {
|
||||
ChainSpec::mainnet()
|
||||
@@ -174,12 +155,11 @@ pub type FoundationBeaconState = BeaconState<MainnetEthSpec>;
|
||||
///
|
||||
/// https://github.com/ethereum/eth2.0-specs/blob/v0.8.0/configs/constant_presets/minimal.yaml
|
||||
///
|
||||
/// Spec v0.8.0
|
||||
/// Spec v0.9.0
|
||||
#[derive(Clone, PartialEq, Debug, Default, Serialize, Deserialize)]
|
||||
pub struct MinimalEthSpec;
|
||||
|
||||
impl EthSpec for MinimalEthSpec {
|
||||
type ShardCount = U8;
|
||||
type SlotsPerEpoch = U8;
|
||||
type SlotsPerEth1VotingPeriod = U16;
|
||||
type SlotsPerHistoricalRoot = U64;
|
||||
@@ -197,8 +177,7 @@ impl EthSpec for MinimalEthSpec {
|
||||
MaxAttesterSlashings,
|
||||
MaxAttestations,
|
||||
MaxDeposits,
|
||||
MaxVoluntaryExits,
|
||||
MaxTransfers
|
||||
MaxVoluntaryExits
|
||||
});
|
||||
|
||||
fn default_spec() -> ChainSpec {
|
||||
@@ -213,7 +192,6 @@ pub type MinimalBeaconState = BeaconState<MinimalEthSpec>;
|
||||
pub struct InteropEthSpec;
|
||||
|
||||
impl EthSpec for InteropEthSpec {
|
||||
type ShardCount = U8;
|
||||
type SlotsPerEpoch = U8;
|
||||
type SlotsPerHistoricalRoot = U64;
|
||||
type SlotsPerEth1VotingPeriod = U16;
|
||||
@@ -231,8 +209,7 @@ impl EthSpec for InteropEthSpec {
|
||||
MaxAttesterSlashings,
|
||||
MaxAttestations,
|
||||
MaxDeposits,
|
||||
MaxVoluntaryExits,
|
||||
MaxTransfers
|
||||
MaxVoluntaryExits
|
||||
});
|
||||
|
||||
fn default_spec() -> ChainSpec {
|
||||
|
||||
@@ -9,7 +9,7 @@ use tree_hash_derive::TreeHash;
|
||||
|
||||
/// Specifies a fork of the `BeaconChain`, to prevent replay attacks.
|
||||
///
|
||||
/// Spec v0.8.1
|
||||
/// Spec v0.9.0
|
||||
#[derive(
|
||||
Debug, Clone, PartialEq, Default, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom,
|
||||
)]
|
||||
@@ -30,7 +30,7 @@ pub struct Fork {
|
||||
impl Fork {
|
||||
/// Initialize the `Fork` from the genesis parameters in the `spec`.
|
||||
///
|
||||
/// Spec v0.8.1
|
||||
/// Spec v0.9.0
|
||||
pub fn genesis(genesis_epoch: Epoch) -> Self {
|
||||
Self {
|
||||
previous_version: [0; 4],
|
||||
@@ -41,7 +41,7 @@ impl Fork {
|
||||
|
||||
/// Return the fork version of the given ``epoch``.
|
||||
///
|
||||
/// Spec v0.8.1
|
||||
/// Spec v0.9.0
|
||||
pub fn get_fork_version(&self, epoch: Epoch) -> [u8; 4] {
|
||||
if epoch < self.epoch {
|
||||
return self.previous_version;
|
||||
|
||||
@@ -9,7 +9,7 @@ use tree_hash_derive::TreeHash;
|
||||
|
||||
/// Historical block and state roots.
|
||||
///
|
||||
/// Spec v0.8.1
|
||||
/// Spec v0.9.0
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom)]
|
||||
pub struct HistoricalBatch<T: EthSpec> {
|
||||
pub block_roots: FixedVector<Hash256, T::SlotsPerHistoricalRoot>,
|
||||
|
||||
@@ -9,7 +9,7 @@ use tree_hash_derive::{SignedRoot, TreeHash};
|
||||
///
|
||||
/// To be included in an `AttesterSlashing`.
|
||||
///
|
||||
/// Spec v0.8.0
|
||||
/// Spec v0.9.0
|
||||
#[derive(
|
||||
Debug,
|
||||
PartialEq,
|
||||
@@ -35,14 +35,14 @@ pub struct IndexedAttestation<T: EthSpec> {
|
||||
impl<T: EthSpec> IndexedAttestation<T> {
|
||||
/// Check if ``attestation_data_1`` and ``attestation_data_2`` have the same target.
|
||||
///
|
||||
/// Spec v0.8.0
|
||||
/// Spec v0.9.0
|
||||
pub fn is_double_vote(&self, other: &Self) -> bool {
|
||||
self.data.target.epoch == other.data.target.epoch && self.data != other.data
|
||||
}
|
||||
|
||||
/// Check if ``attestation_data_1`` surrounds ``attestation_data_2``.
|
||||
///
|
||||
/// Spec v0.8.0
|
||||
/// Spec v0.9.0
|
||||
pub fn is_surround_vote(&self, other: &Self) -> bool {
|
||||
self.data.source.epoch < other.data.source.epoch
|
||||
&& other.data.target.epoch < self.data.target.epoch
|
||||
|
||||
@@ -14,12 +14,10 @@ pub mod attester_slashing;
|
||||
pub mod beacon_block;
|
||||
pub mod beacon_block_body;
|
||||
pub mod beacon_block_header;
|
||||
pub mod beacon_committee;
|
||||
pub mod beacon_state;
|
||||
pub mod chain_spec;
|
||||
pub mod checkpoint;
|
||||
pub mod compact_committee;
|
||||
pub mod crosslink;
|
||||
pub mod crosslink_committee;
|
||||
pub mod deposit;
|
||||
pub mod deposit_data;
|
||||
pub mod eth1_data;
|
||||
@@ -30,7 +28,6 @@ pub mod historical_batch;
|
||||
pub mod indexed_attestation;
|
||||
pub mod pending_attestation;
|
||||
pub mod proposer_slashing;
|
||||
pub mod transfer;
|
||||
pub mod utils;
|
||||
pub mod voluntary_exit;
|
||||
#[macro_use]
|
||||
@@ -41,8 +38,7 @@ pub mod slot_height;
|
||||
mod tree_hash_impls;
|
||||
pub mod validator;
|
||||
|
||||
use ethereum_types::{H160, H256, U256};
|
||||
use std::collections::HashMap;
|
||||
use ethereum_types::{H160, H256};
|
||||
|
||||
pub use crate::attestation::Attestation;
|
||||
pub use crate::attestation_data::AttestationData;
|
||||
@@ -52,12 +48,10 @@ pub use crate::attester_slashing::AttesterSlashing;
|
||||
pub use crate::beacon_block::BeaconBlock;
|
||||
pub use crate::beacon_block_body::BeaconBlockBody;
|
||||
pub use crate::beacon_block_header::BeaconBlockHeader;
|
||||
pub use crate::beacon_committee::{BeaconCommittee, OwnedBeaconCommittee};
|
||||
pub use crate::beacon_state::{Error as BeaconStateError, *};
|
||||
pub use crate::chain_spec::{ChainSpec, Domain};
|
||||
pub use crate::checkpoint::Checkpoint;
|
||||
pub use crate::compact_committee::CompactCommittee;
|
||||
pub use crate::crosslink::Crosslink;
|
||||
pub use crate::crosslink_committee::{CrosslinkCommittee, OwnedCrosslinkCommittee};
|
||||
pub use crate::deposit::Deposit;
|
||||
pub use crate::deposit_data::DepositData;
|
||||
pub use crate::eth1_data::Eth1Data;
|
||||
@@ -70,23 +64,12 @@ pub use crate::proposer_slashing::ProposerSlashing;
|
||||
pub use crate::relative_epoch::{Error as RelativeEpochError, RelativeEpoch};
|
||||
pub use crate::slot_epoch::{Epoch, Slot};
|
||||
pub use crate::slot_height::SlotHeight;
|
||||
pub use crate::transfer::Transfer;
|
||||
pub use crate::validator::Validator;
|
||||
pub use crate::voluntary_exit::VoluntaryExit;
|
||||
|
||||
pub type Shard = u64;
|
||||
pub type Committee = Vec<usize>;
|
||||
pub type CrosslinkCommittees = Vec<(Committee, u64)>;
|
||||
|
||||
pub type CommitteeIndex = u64;
|
||||
pub type Hash256 = H256;
|
||||
pub type Address = H160;
|
||||
pub type EthBalance = U256;
|
||||
|
||||
/// Maps a (slot, shard_id) to attestation_indices.
|
||||
pub type AttesterMap = HashMap<(u64, u64), Vec<usize>>;
|
||||
|
||||
/// Maps a slot to a block proposer.
|
||||
pub type ProposerMap = HashMap<u64, usize>;
|
||||
|
||||
pub use bls::{
|
||||
AggregatePublicKey, AggregateSignature, Keypair, PublicKey, PublicKeyBytes, SecretKey,
|
||||
|
||||
@@ -8,7 +8,7 @@ use tree_hash_derive::TreeHash;
|
||||
|
||||
/// An attestation that has been included in the state but not yet fully processed.
|
||||
///
|
||||
/// Spec v0.8.0
|
||||
/// Spec v0.9.0
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom)]
|
||||
pub struct PendingAttestation<T: EthSpec> {
|
||||
pub aggregation_bits: BitList<T::MaxValidatorsPerCommittee>,
|
||||
|
||||
@@ -8,7 +8,7 @@ use tree_hash_derive::TreeHash;
|
||||
|
||||
/// Two conflicting proposals from the same proposer (validator).
|
||||
///
|
||||
/// Spec v0.8.1
|
||||
/// Spec v0.9.0
|
||||
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom)]
|
||||
pub struct ProposerSlashing {
|
||||
pub proposer_index: u64,
|
||||
|
||||
@@ -9,7 +9,7 @@ pub enum Error {
|
||||
/// Defines the epochs relative to some epoch. Most useful when referring to the committees prior
|
||||
/// to and following some epoch.
|
||||
///
|
||||
/// Spec v0.8.1
|
||||
/// Spec v0.9.0
|
||||
#[derive(Debug, PartialEq, Clone, Copy)]
|
||||
pub enum RelativeEpoch {
|
||||
/// The prior epoch.
|
||||
@@ -23,7 +23,7 @@ pub enum RelativeEpoch {
|
||||
impl RelativeEpoch {
|
||||
/// Returns the `epoch` that `self` refers to, with respect to the `base` epoch.
|
||||
///
|
||||
/// Spec v0.8.1
|
||||
/// Spec v0.9.0
|
||||
pub fn into_epoch(self, base: Epoch) -> Epoch {
|
||||
match self {
|
||||
// Due to saturating nature of epoch, check for current first.
|
||||
@@ -40,7 +40,7 @@ impl RelativeEpoch {
|
||||
/// - `EpochTooLow` when `other` is more than 1 prior to `base`.
|
||||
/// - `EpochTooHigh` when `other` is more than 1 after `base`.
|
||||
///
|
||||
/// Spec v0.8.1
|
||||
/// Spec v0.9.0
|
||||
pub fn from_epoch(base: Epoch, other: Epoch) -> Result<Self, Error> {
|
||||
// Due to saturating nature of epoch, check for current first.
|
||||
if other == base {
|
||||
|
||||
@@ -6,7 +6,6 @@ mod testing_beacon_state_builder;
|
||||
mod testing_deposit_builder;
|
||||
mod testing_pending_attestation_builder;
|
||||
mod testing_proposer_slashing_builder;
|
||||
mod testing_transfer_builder;
|
||||
mod testing_voluntary_exit_builder;
|
||||
|
||||
pub use testing_attestation_builder::*;
|
||||
@@ -17,5 +16,4 @@ pub use testing_beacon_state_builder::*;
|
||||
pub use testing_deposit_builder::*;
|
||||
pub use testing_pending_attestation_builder::*;
|
||||
pub use testing_proposer_slashing_builder::*;
|
||||
pub use testing_transfer_builder::*;
|
||||
pub use testing_voluntary_exit_builder::*;
|
||||
|
||||
@@ -12,14 +12,8 @@ pub struct TestingAttestationBuilder<T: EthSpec> {
|
||||
|
||||
impl<T: EthSpec> TestingAttestationBuilder<T> {
|
||||
/// Create a new attestation builder.
|
||||
pub fn new(
|
||||
state: &BeaconState<T>,
|
||||
committee: &[usize],
|
||||
slot: Slot,
|
||||
shard: u64,
|
||||
spec: &ChainSpec,
|
||||
) -> Self {
|
||||
let data_builder = TestingAttestationDataBuilder::new(state, shard, slot, spec);
|
||||
pub fn new(state: &BeaconState<T>, committee: &[usize], slot: Slot, index: u64) -> Self {
|
||||
let data_builder = TestingAttestationDataBuilder::new(state, index, slot);
|
||||
|
||||
let mut aggregation_bits = BitList::with_capacity(committee.len()).unwrap();
|
||||
let mut custody_bits = BitList::with_capacity(committee.len()).unwrap();
|
||||
@@ -87,7 +81,7 @@ impl<T: EthSpec> TestingAttestationBuilder<T> {
|
||||
|
||||
let domain = spec.get_domain(
|
||||
self.attestation.data.target.epoch,
|
||||
Domain::Attestation,
|
||||
Domain::BeaconAttester,
|
||||
fork,
|
||||
);
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
use crate::*;
|
||||
use tree_hash::TreeHash;
|
||||
|
||||
/// Builds an `AttestationData` to be used for testing purposes.
|
||||
///
|
||||
@@ -11,12 +10,7 @@ pub struct TestingAttestationDataBuilder {
|
||||
impl TestingAttestationDataBuilder {
|
||||
/// Configures a new `AttestationData` which attests to all of the same parameters as the
|
||||
/// state.
|
||||
pub fn new<T: EthSpec>(
|
||||
state: &BeaconState<T>,
|
||||
shard: u64,
|
||||
slot: Slot,
|
||||
spec: &ChainSpec,
|
||||
) -> Self {
|
||||
pub fn new<T: EthSpec>(state: &BeaconState<T>, index: u64, slot: Slot) -> Self {
|
||||
let current_epoch = state.current_epoch();
|
||||
let previous_epoch = state.previous_epoch();
|
||||
|
||||
@@ -44,33 +38,16 @@ impl TestingAttestationDataBuilder {
|
||||
}
|
||||
};
|
||||
|
||||
let parent_crosslink = if is_previous_epoch {
|
||||
state.get_previous_crosslink(shard).unwrap()
|
||||
} else {
|
||||
state.get_current_crosslink(shard).unwrap()
|
||||
};
|
||||
|
||||
let crosslink = Crosslink {
|
||||
shard,
|
||||
parent_root: Hash256::from_slice(&parent_crosslink.tree_hash_root()),
|
||||
start_epoch: parent_crosslink.end_epoch,
|
||||
end_epoch: std::cmp::min(
|
||||
target.epoch,
|
||||
parent_crosslink.end_epoch + spec.max_epochs_per_crosslink,
|
||||
),
|
||||
data_root: Hash256::zero(),
|
||||
};
|
||||
|
||||
let data = AttestationData {
|
||||
slot,
|
||||
index,
|
||||
|
||||
// LMD GHOST vote
|
||||
beacon_block_root: *state.get_block_root(slot).unwrap(),
|
||||
|
||||
// FFG Vote
|
||||
source,
|
||||
target,
|
||||
|
||||
// Crosslink vote
|
||||
crosslink,
|
||||
};
|
||||
|
||||
Self { data }
|
||||
|
||||
@@ -21,7 +21,8 @@ impl TestingAttesterSlashingBuilder {
|
||||
where
|
||||
F: Fn(u64, &[u8], Epoch, Domain) -> Signature,
|
||||
{
|
||||
let shard = 0;
|
||||
let slot = Slot::new(1);
|
||||
let index = 0;
|
||||
let epoch_1 = Epoch::new(1);
|
||||
let epoch_2 = Epoch::new(2);
|
||||
let hash_1 = Hash256::from_low_u64_le(1);
|
||||
@@ -34,19 +35,13 @@ impl TestingAttesterSlashingBuilder {
|
||||
epoch: epoch_1,
|
||||
root: hash_2,
|
||||
};
|
||||
let crosslink = Crosslink {
|
||||
shard,
|
||||
parent_root: hash_1,
|
||||
start_epoch: epoch_1,
|
||||
end_epoch: epoch_2,
|
||||
data_root: hash_1,
|
||||
};
|
||||
|
||||
let data_1 = AttestationData {
|
||||
slot,
|
||||
index,
|
||||
beacon_block_root: hash_1,
|
||||
source: checkpoint_1.clone(),
|
||||
target: checkpoint_1,
|
||||
crosslink,
|
||||
};
|
||||
|
||||
let data_2 = AttestationData {
|
||||
@@ -77,8 +72,12 @@ impl TestingAttesterSlashingBuilder {
|
||||
let message = attestation_data_and_custody_bit.tree_hash_root();
|
||||
|
||||
for validator_index in validator_indices {
|
||||
let signature =
|
||||
signer(*validator_index, &message[..], epoch_2, Domain::Attestation);
|
||||
let signature = signer(
|
||||
*validator_index,
|
||||
&message[..],
|
||||
epoch_2,
|
||||
Domain::BeaconAttester,
|
||||
);
|
||||
attestation.signature.add(&signature);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use crate::{
|
||||
test_utils::{
|
||||
TestingAttestationBuilder, TestingAttesterSlashingBuilder, TestingDepositBuilder,
|
||||
TestingProposerSlashingBuilder, TestingTransferBuilder, TestingVoluntaryExitBuilder,
|
||||
TestingProposerSlashingBuilder, TestingVoluntaryExitBuilder,
|
||||
},
|
||||
*,
|
||||
};
|
||||
@@ -116,7 +116,7 @@ impl<T: EthSpec> TestingBeaconBlockBuilder<T> {
|
||||
// - The slot of the committee.
|
||||
// - A list of all validators in the committee.
|
||||
// - A list of all validators in the committee that should sign the attestation.
|
||||
// - The shard of the committee.
|
||||
// - The index of the committee.
|
||||
let mut committees: Vec<(Slot, Vec<usize>, Vec<usize>, u64)> = vec![];
|
||||
|
||||
if slot < T::slots_per_epoch() {
|
||||
@@ -132,16 +132,16 @@ impl<T: EthSpec> TestingBeaconBlockBuilder<T> {
|
||||
break;
|
||||
}
|
||||
|
||||
for crosslink_committee in state.get_crosslink_committees_at_slot(slot)? {
|
||||
for beacon_committee in state.get_beacon_committees_at_slot(slot)? {
|
||||
if attestations_added >= num_attestations {
|
||||
break;
|
||||
}
|
||||
|
||||
committees.push((
|
||||
slot,
|
||||
crosslink_committee.committee.to_vec(),
|
||||
crosslink_committee.committee.to_vec(),
|
||||
crosslink_committee.shard,
|
||||
beacon_committee.committee.to_vec(),
|
||||
beacon_committee.committee.to_vec(),
|
||||
beacon_committee.index,
|
||||
));
|
||||
|
||||
attestations_added += 1;
|
||||
@@ -157,26 +157,25 @@ impl<T: EthSpec> TestingBeaconBlockBuilder<T> {
|
||||
break;
|
||||
}
|
||||
|
||||
for index in 0..committees.len() {
|
||||
for i in 0..committees.len() {
|
||||
if committees.len() >= num_attestations as usize {
|
||||
break;
|
||||
}
|
||||
|
||||
let (slot, committee, mut signing_validators, shard) = committees[index].clone();
|
||||
let (slot, committee, mut signing_validators, index) = committees[i].clone();
|
||||
|
||||
let new_signing_validators =
|
||||
signing_validators.split_off(signing_validators.len() / 2);
|
||||
|
||||
committees[index] = (slot, committee.clone(), signing_validators, shard);
|
||||
committees.push((slot, committee, new_signing_validators, shard));
|
||||
committees[i] = (slot, committee.clone(), signing_validators, index);
|
||||
committees.push((slot, committee, new_signing_validators, index));
|
||||
}
|
||||
}
|
||||
|
||||
let attestations: Vec<_> = committees
|
||||
.par_iter()
|
||||
.map(|(slot, committee, signing_validators, shard)| {
|
||||
let mut builder =
|
||||
TestingAttestationBuilder::new(state, committee, *slot, *shard, spec);
|
||||
.map(|(slot, committee, signing_validators, index)| {
|
||||
let mut builder = TestingAttestationBuilder::new(state, committee, *slot, *index);
|
||||
|
||||
let signing_secret_keys: Vec<&SecretKey> = signing_validators
|
||||
.iter()
|
||||
@@ -245,25 +244,6 @@ impl<T: EthSpec> TestingBeaconBlockBuilder<T> {
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
/// Insert a `Valid` transfer into the state.
|
||||
///
|
||||
/// Note: this will set the validator to be withdrawable by directly modifying the state
|
||||
/// validator registry. This _may_ cause problems historic hashes, etc.
|
||||
pub fn insert_transfer(
|
||||
&mut self,
|
||||
state: &BeaconState<T>,
|
||||
from: u64,
|
||||
to: u64,
|
||||
amount: u64,
|
||||
keypair: Keypair,
|
||||
spec: &ChainSpec,
|
||||
) {
|
||||
let mut builder = TestingTransferBuilder::new(from, to, amount, state.slot);
|
||||
builder.sign::<T>(keypair, &state.fork, spec);
|
||||
|
||||
self.block.body.transfers.push(builder.build()).unwrap()
|
||||
}
|
||||
|
||||
/// Signs and returns the block, consuming the builder.
|
||||
pub fn build(mut self, sk: &SecretKey, fork: &Fork, spec: &ChainSpec) -> BeaconBlock<T> {
|
||||
self.sign(sk, fork, spec);
|
||||
|
||||
@@ -181,8 +181,6 @@ impl<T: EthSpec> TestingBeaconStateBuilder<T> {
|
||||
|
||||
state.slot = slot;
|
||||
|
||||
// NOTE: we could update the latest start shard here
|
||||
|
||||
state.previous_justified_checkpoint.epoch = epoch - 3;
|
||||
state.current_justified_checkpoint.epoch = epoch - 2;
|
||||
state.justification_bits = BitVector::from_bytes(vec![0b0000_1111]).unwrap();
|
||||
@@ -215,22 +213,22 @@ impl<T: EthSpec> TestingBeaconStateBuilder<T> {
|
||||
for slot in first_slot..=last_slot {
|
||||
let slot = Slot::from(slot);
|
||||
|
||||
let committees: Vec<OwnedCrosslinkCommittee> = state
|
||||
.get_crosslink_committees_at_slot(slot)
|
||||
let committees: Vec<OwnedBeaconCommittee> = state
|
||||
.get_beacon_committees_at_slot(slot)
|
||||
.unwrap()
|
||||
.into_iter()
|
||||
.map(|c| c.clone().into_owned())
|
||||
.collect();
|
||||
|
||||
for crosslink_committee in committees {
|
||||
for beacon_committee in committees {
|
||||
let mut builder = TestingPendingAttestationBuilder::new(
|
||||
state,
|
||||
crosslink_committee.shard,
|
||||
beacon_committee.index,
|
||||
slot,
|
||||
spec,
|
||||
);
|
||||
// The entire committee should have signed the pending attestation.
|
||||
let signers = vec![true; crosslink_committee.committee.len()];
|
||||
let signers = vec![true; beacon_committee.committee.len()];
|
||||
builder.add_committee_participation(signers);
|
||||
let attestation = builder.build();
|
||||
|
||||
|
||||
@@ -15,15 +15,10 @@ impl<T: EthSpec> TestingPendingAttestationBuilder<T> {
|
||||
///
|
||||
/// * The aggregation and custody bitfields will all be empty, they need to be set with
|
||||
/// `Self::add_committee_participation`.
|
||||
pub fn new(state: &BeaconState<T>, shard: u64, slot: Slot, spec: &ChainSpec) -> Self {
|
||||
let data_builder = TestingAttestationDataBuilder::new(state, shard, slot, spec);
|
||||
pub fn new(state: &BeaconState<T>, index: u64, slot: Slot, spec: &ChainSpec) -> Self {
|
||||
let data_builder = TestingAttestationDataBuilder::new(state, index, slot);
|
||||
|
||||
let relative_epoch =
|
||||
RelativeEpoch::from_epoch(state.current_epoch(), slot.epoch(T::slots_per_epoch()))
|
||||
.expect("epoch out of bounds");
|
||||
let proposer_index = state
|
||||
.get_beacon_proposer_index(slot, relative_epoch, spec)
|
||||
.unwrap() as u64;
|
||||
let proposer_index = state.get_beacon_proposer_index(slot, spec).unwrap() as u64;
|
||||
|
||||
let pending_attestation = PendingAttestation {
|
||||
aggregation_bits: BitList::with_capacity(T::MaxValidatorsPerCommittee::to_usize())
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
use crate::*;
|
||||
use tree_hash::SignedRoot;
|
||||
|
||||
/// Builds a transfer to be used for testing purposes.
|
||||
///
|
||||
/// This struct should **never be used for production purposes.**
|
||||
pub struct TestingTransferBuilder {
|
||||
transfer: Transfer,
|
||||
}
|
||||
|
||||
impl TestingTransferBuilder {
|
||||
/// Instantiates a new builder.
|
||||
pub fn new(sender: u64, recipient: u64, amount: u64, slot: Slot) -> Self {
|
||||
let keypair = Keypair::random();
|
||||
|
||||
let transfer = Transfer {
|
||||
sender,
|
||||
recipient,
|
||||
amount,
|
||||
fee: 0,
|
||||
slot,
|
||||
pubkey: keypair.pk,
|
||||
signature: Signature::empty_signature(),
|
||||
};
|
||||
|
||||
Self { transfer }
|
||||
}
|
||||
|
||||
/// Signs the transfer.
|
||||
///
|
||||
/// The keypair must match that of the `from` validator index.
|
||||
pub fn sign<T: EthSpec>(&mut self, keypair: Keypair, fork: &Fork, spec: &ChainSpec) {
|
||||
self.transfer.pubkey = keypair.pk;
|
||||
let message = self.transfer.signed_root();
|
||||
let epoch = self.transfer.slot.epoch(T::slots_per_epoch());
|
||||
let domain = spec.get_domain(epoch, Domain::Transfer, fork);
|
||||
|
||||
self.transfer.signature = Signature::new(&message, domain, &keypair.sk);
|
||||
}
|
||||
|
||||
/// Builds the transfer, consuming the builder.
|
||||
pub fn build(self) -> Transfer {
|
||||
self.transfer
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
use super::Slot;
|
||||
use crate::test_utils::TestRandom;
|
||||
use bls::{PublicKey, Signature};
|
||||
use derivative::Derivative;
|
||||
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use ssz_derive::{Decode, Encode};
|
||||
use test_random_derive::TestRandom;
|
||||
use tree_hash::TreeHash;
|
||||
use tree_hash_derive::{SignedRoot, TreeHash};
|
||||
|
||||
/// The data submitted to the deposit contract.
|
||||
///
|
||||
/// Spec v0.8.0
|
||||
#[derive(
|
||||
Debug,
|
||||
Clone,
|
||||
Serialize,
|
||||
Deserialize,
|
||||
Encode,
|
||||
Decode,
|
||||
TreeHash,
|
||||
TestRandom,
|
||||
SignedRoot,
|
||||
Derivative,
|
||||
)]
|
||||
#[derivative(PartialEq, Eq, Hash)]
|
||||
pub struct Transfer {
|
||||
pub sender: u64,
|
||||
pub recipient: u64,
|
||||
pub amount: u64,
|
||||
pub fee: u64,
|
||||
pub slot: Slot,
|
||||
pub pubkey: PublicKey,
|
||||
#[derivative(Hash = "ignore")]
|
||||
#[signed_root(skip_hashing)]
|
||||
pub signature: Signature,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
ssz_tests!(Transfer);
|
||||
}
|
||||
@@ -7,7 +7,7 @@ use tree_hash_derive::TreeHash;
|
||||
|
||||
/// Information about a `BeaconChain` validator.
|
||||
///
|
||||
/// Spec v0.8.0
|
||||
/// Spec v0.9.0
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Encode, Decode, TestRandom, TreeHash)]
|
||||
pub struct Validator {
|
||||
pub pubkey: PublicKey,
|
||||
|
||||
@@ -9,7 +9,7 @@ use tree_hash_derive::{SignedRoot, TreeHash};
|
||||
|
||||
/// An exit voluntarily submitted a validator who wishes to withdraw.
|
||||
///
|
||||
/// Spec v0.8.0
|
||||
/// Spec v0.9.0
|
||||
#[derive(
|
||||
Debug,
|
||||
PartialEq,
|
||||
|
||||
Reference in New Issue
Block a user