Update to spec v0.9.0

This commit is contained in:
Michael Sproul
2019-11-11 15:00:10 +11:00
parent 613fdbeda6
commit aaa5f2042f
93 changed files with 651 additions and 2203 deletions

View File

@@ -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,

View File

@@ -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)]

View File

@@ -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,

View File

@@ -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,
}

View File

@@ -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> {

View File

@@ -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(),

View File

@@ -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)]

View File

@@ -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,

View File

@@ -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>,
}

View File

@@ -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)))
}
}

View File

@@ -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()
}
*/

View File

@@ -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
);
}
}

View File

@@ -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);

View File

@@ -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);
}
}

View File

@@ -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,

View File

@@ -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(),
}
}
}

View File

@@ -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);
}

View File

@@ -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>,

View File

@@ -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,

View File

@@ -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,
)]

View File

@@ -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 {

View File

@@ -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;

View File

@@ -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>,

View File

@@ -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

View File

@@ -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,

View File

@@ -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>,

View File

@@ -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,

View File

@@ -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 {

View File

@@ -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::*;

View File

@@ -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,
);

View File

@@ -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 }

View File

@@ -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);
}
};

View File

@@ -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);

View File

@@ -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();

View File

@@ -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())

View File

@@ -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
}
}

View File

@@ -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);
}

View File

@@ -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,

View File

@@ -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,