From a13dd0d871272884f2533a79c07bf8ee1c5d108c Mon Sep 17 00:00:00 2001 From: Paul Hauner Date: Mon, 11 Feb 2019 14:02:59 +1100 Subject: [PATCH] Update `types` defs, move processing out. - Update `types` to v0.2.0 spec (not all functions upgraded too). - Move slot/block/epoch processing out to a separate crate (not included yet, it doesn't compile) --- eth2/types/src/attestation.rs | 21 +- eth2/types/src/attestation_data.rs | 41 +- .../src/attestation_data_and_custody_bit.rs | 2 +- eth2/types/src/attester_slashing.rs | 80 ++ eth2/types/src/beacon_block_body.rs | 38 +- eth2/types/src/beacon_state.rs | 1271 ++++------------- eth2/types/src/crosslink.rs | 18 +- eth2/types/src/deposit.rs | 24 +- eth2/types/src/exit.rs | 16 +- eth2/types/src/fork.rs | 38 +- eth2/types/src/lib.rs | 8 +- eth2/types/src/pending_attestation.rs | 20 +- eth2/types/src/proposer_slashing.rs | 2 +- eth2/types/src/shard_committee.rs | 74 - eth2/types/src/slashable_attestation.rs | 92 ++ eth2/types/src/slot_epoch.rs | 24 + eth2/types/src/spec/foundation.rs | 129 +- eth2/types/src/spec/mod.rs | 57 +- eth2/types/src/special_record.rs | 142 -- eth2/types/src/validator.rs | 106 +- eth2/types/src/validator_registry.rs | 82 +- 21 files changed, 766 insertions(+), 1519 deletions(-) create mode 100644 eth2/types/src/attester_slashing.rs delete mode 100644 eth2/types/src/shard_committee.rs create mode 100644 eth2/types/src/slashable_attestation.rs delete mode 100644 eth2/types/src/special_record.rs diff --git a/eth2/types/src/attestation.rs b/eth2/types/src/attestation.rs index 73ea5eec1..7938ae657 100644 --- a/eth2/types/src/attestation.rs +++ b/eth2/types/src/attestation.rs @@ -7,8 +7,8 @@ use ssz::{hash, Decodable, DecodeError, Encodable, SszStream, TreeHash}; #[derive(Debug, Clone, PartialEq, Serialize)] pub struct Attestation { - pub data: AttestationData, pub aggregation_bitfield: Bitfield, + pub data: AttestationData, pub custody_bitfield: Bitfield, pub aggregate_signature: AggregateSignature, } @@ -25,8 +25,8 @@ impl Attestation { impl Encodable for Attestation { fn ssz_append(&self, s: &mut SszStream) { - s.append(&self.data); s.append(&self.aggregation_bitfield); + s.append(&self.data); s.append(&self.custody_bitfield); s.append(&self.aggregate_signature); } @@ -34,14 +34,14 @@ impl Encodable for Attestation { impl Decodable for Attestation { fn ssz_decode(bytes: &[u8], i: usize) -> Result<(Self, usize), DecodeError> { - let (data, i) = AttestationData::ssz_decode(bytes, i)?; let (aggregation_bitfield, i) = Bitfield::ssz_decode(bytes, i)?; + let (data, i) = AttestationData::ssz_decode(bytes, i)?; let (custody_bitfield, i) = Bitfield::ssz_decode(bytes, i)?; let (aggregate_signature, i) = AggregateSignature::ssz_decode(bytes, i)?; let attestation_record = Self { - data, aggregation_bitfield, + data, custody_bitfield, aggregate_signature, }; @@ -49,22 +49,11 @@ impl Decodable for Attestation { } } -impl Attestation { - pub fn zero() -> Self { - Self { - data: AttestationData::zero(), - aggregation_bitfield: Bitfield::new(), - custody_bitfield: Bitfield::new(), - aggregate_signature: AggregateSignature::new(), - } - } -} - impl TreeHash for Attestation { fn hash_tree_root(&self) -> Vec { let mut result: Vec = vec![]; - result.append(&mut self.data.hash_tree_root()); result.append(&mut self.aggregation_bitfield.hash_tree_root()); + result.append(&mut self.data.hash_tree_root()); result.append(&mut self.custody_bitfield.hash_tree_root()); result.append(&mut self.aggregate_signature.hash_tree_root()); hash(&result) diff --git a/eth2/types/src/attestation_data.rs b/eth2/types/src/attestation_data.rs index bf8bc708e..702bba416 100644 --- a/eth2/types/src/attestation_data.rs +++ b/eth2/types/src/attestation_data.rs @@ -1,5 +1,5 @@ use crate::test_utils::TestRandom; -use crate::{AttestationDataAndCustodyBit, Hash256, Slot}; +use crate::{AttestationDataAndCustodyBit, Crosslink, Epoch, Hash256, Slot}; use rand::RngCore; use serde_derive::Serialize; use ssz::{hash, Decodable, DecodeError, Encodable, SszStream, TreeHash}; @@ -11,7 +11,7 @@ pub const SSZ_ATTESTION_DATA_LENGTH: usize = { 32 + // epoch_boundary_root 32 + // shard_block_hash 32 + // latest_crosslink_hash - 8 + // justified_slot + 8 + // justified_epoch 32 // justified_block_root }; @@ -22,27 +22,14 @@ pub struct AttestationData { pub beacon_block_root: Hash256, pub epoch_boundary_root: Hash256, pub shard_block_root: Hash256, - pub latest_crosslink_root: Hash256, - pub justified_slot: Slot, + pub latest_crosslink: Crosslink, + pub justified_epoch: Epoch, pub justified_block_root: Hash256, } impl Eq for AttestationData {} impl AttestationData { - pub fn zero() -> Self { - Self { - slot: Slot::from(0_u64), - shard: 0, - beacon_block_root: Hash256::zero(), - epoch_boundary_root: Hash256::zero(), - shard_block_root: Hash256::zero(), - latest_crosslink_root: Hash256::zero(), - justified_slot: Slot::from(0_u64), - justified_block_root: Hash256::zero(), - } - } - pub fn canonical_root(&self) -> Hash256 { Hash256::from(&self.hash_tree_root()[..]) } @@ -63,8 +50,8 @@ impl Encodable for AttestationData { s.append(&self.beacon_block_root); s.append(&self.epoch_boundary_root); s.append(&self.shard_block_root); - s.append(&self.latest_crosslink_root); - s.append(&self.justified_slot); + s.append(&self.latest_crosslink); + s.append(&self.justified_epoch); s.append(&self.justified_block_root); } } @@ -76,8 +63,8 @@ impl Decodable for AttestationData { let (beacon_block_root, i) = <_>::ssz_decode(bytes, i)?; let (epoch_boundary_root, i) = <_>::ssz_decode(bytes, i)?; let (shard_block_root, i) = <_>::ssz_decode(bytes, i)?; - let (latest_crosslink_root, i) = <_>::ssz_decode(bytes, i)?; - let (justified_slot, i) = <_>::ssz_decode(bytes, i)?; + let (latest_crosslink, i) = <_>::ssz_decode(bytes, i)?; + let (justified_epoch, i) = <_>::ssz_decode(bytes, i)?; let (justified_block_root, i) = <_>::ssz_decode(bytes, i)?; let attestation_data = AttestationData { @@ -86,8 +73,8 @@ impl Decodable for AttestationData { beacon_block_root, epoch_boundary_root, shard_block_root, - latest_crosslink_root, - justified_slot, + latest_crosslink, + justified_epoch, justified_block_root, }; Ok((attestation_data, i)) @@ -102,8 +89,8 @@ impl TreeHash for AttestationData { result.append(&mut self.beacon_block_root.hash_tree_root()); result.append(&mut self.epoch_boundary_root.hash_tree_root()); result.append(&mut self.shard_block_root.hash_tree_root()); - result.append(&mut self.latest_crosslink_root.hash_tree_root()); - result.append(&mut self.justified_slot.hash_tree_root()); + result.append(&mut self.latest_crosslink.hash_tree_root()); + result.append(&mut self.justified_epoch.hash_tree_root()); result.append(&mut self.justified_block_root.hash_tree_root()); hash(&result) } @@ -117,8 +104,8 @@ impl TestRandom for AttestationData { beacon_block_root: <_>::random_for_test(rng), epoch_boundary_root: <_>::random_for_test(rng), shard_block_root: <_>::random_for_test(rng), - latest_crosslink_root: <_>::random_for_test(rng), - justified_slot: <_>::random_for_test(rng), + latest_crosslink: <_>::random_for_test(rng), + justified_epoch: <_>::random_for_test(rng), justified_block_root: <_>::random_for_test(rng), } } diff --git a/eth2/types/src/attestation_data_and_custody_bit.rs b/eth2/types/src/attestation_data_and_custody_bit.rs index 8200abf30..4e93dd893 100644 --- a/eth2/types/src/attestation_data_and_custody_bit.rs +++ b/eth2/types/src/attestation_data_and_custody_bit.rs @@ -2,7 +2,7 @@ use super::AttestationData; use crate::test_utils::TestRandom; use rand::RngCore; use serde_derive::Serialize; -use ssz::{hash, Decodable, DecodeError, Encodable, SszStream, TreeHash}; +use ssz::{Decodable, DecodeError, Encodable, SszStream, TreeHash}; #[derive(Debug, Clone, PartialEq, Default, Serialize)] pub struct AttestationDataAndCustodyBit { diff --git a/eth2/types/src/attester_slashing.rs b/eth2/types/src/attester_slashing.rs new file mode 100644 index 000000000..0b27d2030 --- /dev/null +++ b/eth2/types/src/attester_slashing.rs @@ -0,0 +1,80 @@ +use crate::{test_utils::TestRandom, SlashableAttestation}; +use rand::RngCore; +use serde_derive::Serialize; +use ssz::{hash, Decodable, DecodeError, Encodable, SszStream, TreeHash}; + +#[derive(Debug, PartialEq, Clone, Serialize)] +pub struct AttesterSlashing { + pub slashable_attestation_1: SlashableAttestation, + pub slashable_attestation_2: SlashableAttestation, +} + +impl Encodable for AttesterSlashing { + fn ssz_append(&self, s: &mut SszStream) { + s.append(&self.slashable_attestation_1); + s.append(&self.slashable_attestation_2); + } +} + +impl Decodable for AttesterSlashing { + fn ssz_decode(bytes: &[u8], i: usize) -> Result<(Self, usize), DecodeError> { + let (slashable_attestation_1, i) = <_>::ssz_decode(bytes, i)?; + let (slashable_attestation_2, i) = <_>::ssz_decode(bytes, i)?; + + Ok(( + AttesterSlashing { + slashable_attestation_1, + slashable_attestation_2, + }, + i, + )) + } +} + +impl TreeHash for AttesterSlashing { + fn hash_tree_root(&self) -> Vec { + let mut result: Vec = vec![]; + result.append(&mut self.slashable_attestation_1.hash_tree_root()); + result.append(&mut self.slashable_attestation_2.hash_tree_root()); + hash(&result) + } +} + +impl TestRandom for AttesterSlashing { + fn random_for_test(rng: &mut T) -> Self { + Self { + slashable_attestation_1: <_>::random_for_test(rng), + slashable_attestation_2: <_>::random_for_test(rng), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::test_utils::{SeedableRng, TestRandom, XorShiftRng}; + use ssz::ssz_encode; + + #[test] + pub fn test_ssz_round_trip() { + let mut rng = XorShiftRng::from_seed([42; 16]); + let original = AttesterSlashing::random_for_test(&mut rng); + + let bytes = ssz_encode(&original); + let (decoded, _) = <_>::ssz_decode(&bytes, 0).unwrap(); + + assert_eq!(original, decoded); + } + + #[test] + pub fn test_hash_tree_root() { + let mut rng = XorShiftRng::from_seed([42; 16]); + let original = AttesterSlashing::random_for_test(&mut rng); + + let result = original.hash_tree_root(); + + assert_eq!(result.len(), 32); + // TODO: Add further tests + // https://github.com/sigp/lighthouse/issues/170 + } +} diff --git a/eth2/types/src/beacon_block_body.rs b/eth2/types/src/beacon_block_body.rs index ad9ec7ea6..d3a61f7ba 100644 --- a/eth2/types/src/beacon_block_body.rs +++ b/eth2/types/src/beacon_block_body.rs @@ -1,23 +1,14 @@ -use super::{Attestation, CasperSlashing, Deposit, Exit, ProposerSlashing}; +use super::{Attestation, AttesterSlashing, Deposit, Exit, ProposerSlashing}; use crate::test_utils::TestRandom; use rand::RngCore; use serde_derive::Serialize; use ssz::{hash, Decodable, DecodeError, Encodable, SszStream, TreeHash}; -// The following types are just dummy classes as they will not be defined until -// Phase 1 (Sharding phase) -type CustodyReseed = usize; -type CustodyChallenge = usize; -type CustodyResponse = usize; - #[derive(Debug, PartialEq, Clone, Default, Serialize)] pub struct BeaconBlockBody { pub proposer_slashings: Vec, - pub casper_slashings: Vec, + pub attester_slashings: Vec, pub attestations: Vec, - pub custody_reseeds: Vec, - pub custody_challenges: Vec, - pub custody_responses: Vec, pub deposits: Vec, pub exits: Vec, } @@ -25,11 +16,8 @@ pub struct BeaconBlockBody { impl Encodable for BeaconBlockBody { fn ssz_append(&self, s: &mut SszStream) { s.append_vec(&self.proposer_slashings); - s.append_vec(&self.casper_slashings); + s.append_vec(&self.attester_slashings); s.append_vec(&self.attestations); - s.append_vec(&self.custody_reseeds); - s.append_vec(&self.custody_challenges); - s.append_vec(&self.custody_responses); s.append_vec(&self.deposits); s.append_vec(&self.exits); } @@ -38,22 +26,16 @@ impl Encodable for BeaconBlockBody { impl Decodable for BeaconBlockBody { fn ssz_decode(bytes: &[u8], i: usize) -> Result<(Self, usize), DecodeError> { let (proposer_slashings, i) = <_>::ssz_decode(bytes, i)?; - let (casper_slashings, i) = <_>::ssz_decode(bytes, i)?; + let (attester_slashings, i) = <_>::ssz_decode(bytes, i)?; let (attestations, i) = <_>::ssz_decode(bytes, i)?; - let (custody_reseeds, i) = <_>::ssz_decode(bytes, i)?; - let (custody_challenges, i) = <_>::ssz_decode(bytes, i)?; - let (custody_responses, i) = <_>::ssz_decode(bytes, i)?; let (deposits, i) = <_>::ssz_decode(bytes, i)?; let (exits, i) = <_>::ssz_decode(bytes, i)?; Ok(( Self { proposer_slashings, - casper_slashings, + attester_slashings, attestations, - custody_reseeds, - custody_challenges, - custody_responses, deposits, exits, }, @@ -66,11 +48,8 @@ impl TreeHash for BeaconBlockBody { fn hash_tree_root(&self) -> Vec { let mut result: Vec = vec![]; result.append(&mut self.proposer_slashings.hash_tree_root()); - result.append(&mut self.casper_slashings.hash_tree_root()); + result.append(&mut self.attester_slashings.hash_tree_root()); result.append(&mut self.attestations.hash_tree_root()); - result.append(&mut self.custody_reseeds.hash_tree_root()); - result.append(&mut self.custody_challenges.hash_tree_root()); - result.append(&mut self.custody_responses.hash_tree_root()); result.append(&mut self.deposits.hash_tree_root()); result.append(&mut self.exits.hash_tree_root()); hash(&result) @@ -81,11 +60,8 @@ impl TestRandom for BeaconBlockBody { fn random_for_test(rng: &mut T) -> Self { Self { proposer_slashings: <_>::random_for_test(rng), - casper_slashings: <_>::random_for_test(rng), + attester_slashings: <_>::random_for_test(rng), attestations: <_>::random_for_test(rng), - custody_reseeds: <_>::random_for_test(rng), - custody_challenges: <_>::random_for_test(rng), - custody_responses: <_>::random_for_test(rng), deposits: <_>::random_for_test(rng), exits: <_>::random_for_test(rng), } diff --git a/eth2/types/src/beacon_state.rs b/eth2/types/src/beacon_state.rs index b7f869c8d..d5e612397 100644 --- a/eth2/types/src/beacon_state.rs +++ b/eth2/types/src/beacon_state.rs @@ -1,20 +1,15 @@ use crate::test_utils::TestRandom; use crate::{ validator::StatusFlags, validator_registry::get_active_validator_indices, AggregatePublicKey, - Attestation, AttestationData, BeaconBlock, Bitfield, ChainSpec, Crosslink, Epoch, Eth1Data, - Eth1DataVote, Exit, Fork, Hash256, PendingAttestation, PublicKey, Signature, Slot, Validator, + Attestation, AttestationData, Bitfield, ChainSpec, Crosslink, Epoch, Eth1Data, Eth1DataVote, + Fork, Hash256, PendingAttestation, PublicKey, Signature, Slot, Validator, }; use bls::bls_verify_aggregate; use honey_badger_split::SplitExt; -use integer_sqrt::IntegerSquareRoot; -use log::debug; use rand::RngCore; -use rayon::prelude::*; use serde_derive::Serialize; -use ssz::ssz_encode; use ssz::{hash, Decodable, DecodeError, Encodable, SszStream, TreeHash}; -use std::collections::{HashMap, HashSet}; -use std::iter::FromIterator; +use std::collections::HashMap; use std::ops::Range; use vec_shuffle::shuffle; @@ -32,6 +27,7 @@ pub enum Error { CommitteesError(CommitteesError), } +/* #[derive(Debug, PartialEq)] pub enum BlockProcessingError { DBError(String), @@ -61,7 +57,9 @@ pub enum BlockProcessingError { CommitteesError(CommitteesError), SlotProcessingError(SlotProcessingError), } +*/ +/* #[derive(Debug, PartialEq)] pub enum EpochError { UnableToDetermineProducer, @@ -72,6 +70,7 @@ pub enum EpochError { InclusionError(InclusionError), WinningRootError(WinningRootError), } +*/ #[derive(Debug, PartialEq)] pub enum WinningRootError { @@ -99,11 +98,13 @@ pub enum AttestationParticipantsError { CommitteesError(CommitteesError), } +/* #[derive(Debug, PartialEq)] pub enum SlotProcessingError { CommitteesError(CommitteesError), EpochProcessingError(EpochError), } +*/ #[derive(Debug, PartialEq)] pub enum AttestationValidationError { @@ -153,33 +154,27 @@ pub struct BeaconState { // Misc pub slot: Slot, pub genesis_time: u64, - pub fork_data: Fork, + pub fork: Fork, // Validator registry pub validator_registry: Vec, pub validator_balances: Vec, - pub validator_registry_update_slot: Slot, - pub validator_registry_exit_count: u64, - pub validator_registry_delta_chain_tip: Hash256, + pub validator_registry_update_epoch: Epoch, // Randomness and committees pub latest_randao_mixes: Vec, - pub latest_vdf_outputs: Vec, pub previous_epoch_start_shard: u64, pub current_epoch_start_shard: u64, - pub previous_epoch_calculation_slot: Slot, - pub current_epoch_calculation_slot: Slot, + pub previous_calculation_epoch: Epoch, + pub current_calculation_epoch: Epoch, pub previous_epoch_seed: Hash256, pub current_epoch_seed: Hash256, - // Custody challenges - pub custody_challenges: Vec, - // Finality - pub previous_justified_slot: Slot, - pub justified_slot: Slot, + pub previous_justified_epoch: Epoch, + pub justified_epoch: Epoch, pub justification_bitfield: u64, - pub finalized_slot: Slot, + pub finalized_epoch: Epoch, // Recent state pub latest_crosslinks: Vec, @@ -206,6 +201,10 @@ impl BeaconState { self.current_epoch(spec).saturating_sub(1_u64) } + pub fn next_epoch(&self, spec: &ChainSpec) -> Epoch { + self.current_epoch(spec).saturating_add(1_u64) + } + pub fn current_epoch_start_slot(&self, spec: &ChainSpec) -> Slot { self.current_epoch(spec).start_slot(spec.epoch_length) } @@ -214,10 +213,12 @@ impl BeaconState { self.previous_epoch(spec).start_slot(spec.epoch_length) } - /// Returns the number of committees per slot. + /// Return the number of committees in one epoch. /// - /// Note: this is _not_ the committee size. - pub fn get_committee_count_per_slot( + /// TODO: this should probably be a method on `ChainSpec`. + /// + /// Spec v0.1 + pub fn get_epoch_committee_count( &self, active_validator_count: usize, spec: &ChainSpec, @@ -228,33 +229,75 @@ impl BeaconState { spec.shard_count / spec.epoch_length, active_validator_count as u64 / spec.epoch_length / spec.target_committee_size, ), - ) + ) * spec.epoch_length } - fn get_previous_epoch_committee_count_per_slot(&self, spec: &ChainSpec) -> u64 { - let previous_active_validators = get_active_validator_indices( - &self.validator_registry, - self.previous_epoch_calculation_slot, - ); - self.get_committee_count_per_slot(previous_active_validators.len(), spec) as u64 + /// Shuffle ``validators`` into crosslink committees seeded by ``seed`` and ``epoch``. + /// Return a list of ``committees_per_epoch`` committees where each + /// committee is itself a list of validator indices. + /// + /// Spec v0.1 + pub fn get_shuffling(&self, seed: Hash256, epoch: Epoch, spec: &ChainSpec) -> Vec> { + let active_validator_indices = + get_active_validator_indices(&self.validator_registry, epoch); + + let committees_per_epoch = + self.get_epoch_committee_count(active_validator_indices.len(), spec); + + // TODO: check that Hash256::from(u64) matches 'int_to_bytes32'. + let seed = seed ^ Hash256::from(epoch.as_u64()); + // TODO: fix `expect` assert. + let shuffled_active_validator_indices = + shuffle(&seed, active_validator_indices).expect("Max validator count exceed!"); + + shuffled_active_validator_indices + .honey_badger_split(committees_per_epoch as usize) + .filter_map(|slice: &[usize]| Some(slice.to_vec())) + .collect() } - pub fn get_current_epoch_committee_count_per_slot(&self, spec: &ChainSpec) -> u64 { - let current_active_validators = get_active_validator_indices( - &self.validator_registry, - self.current_epoch_calculation_slot, - ); - self.get_committee_count_per_slot(current_active_validators.len(), spec) + /// Return the number of committees in the previous epoch. + /// + /// Spec v0.1 + fn get_previous_epoch_committee_count(&self, spec: &ChainSpec) -> u64 { + let previous_active_validators = + get_active_validator_indices(&self.validator_registry, self.previous_calculation_epoch); + self.get_epoch_committee_count(previous_active_validators.len(), spec) } + /// Return the number of committees in the current epoch. + /// + /// Spec v0.1 + pub fn get_current_epoch_committee_count(&self, spec: &ChainSpec) -> u64 { + let current_active_validators = + get_active_validator_indices(&self.validator_registry, self.current_calculation_epoch); + self.get_epoch_committee_count(current_active_validators.len(), spec) + } + + /// Return the number of committees in the next epoch. + /// + /// Spec v0.1 + pub fn get_next_epoch_committee_count(&self, spec: &ChainSpec) -> u64 { + let current_active_validators = + get_active_validator_indices(&self.validator_registry, self.current_epoch(spec) + 1); + self.get_epoch_committee_count(current_active_validators.len(), spec) + } + + /// Return the list of ``(committee, shard)`` tuples for the ``slot``. + /// + /// Note: There are two possible shufflings for crosslink committees for a + /// `slot` in the next epoch: with and without a `registry_change` + /// + /// Spec v0.1 pub fn get_crosslink_committees_at_slot( &self, slot: Slot, + registry_change: bool, spec: &ChainSpec, ) -> Result, u64)>, CommitteesError> { let epoch = slot.epoch(spec.epoch_length); let current_epoch = self.current_epoch(spec); - let previous_epoch = if current_epoch == spec.genesis_slot.epoch(spec.epoch_length) { + let previous_epoch = if current_epoch == spec.genesis_epoch { current_epoch } else { current_epoch.saturating_sub(1_u64) @@ -269,20 +312,20 @@ impl BeaconState { let offset = slot.as_u64() % spec.epoch_length; let (committees_per_slot, shuffling, slot_start_shard) = if epoch < current_epoch { - let committees_per_slot = self.get_previous_epoch_committee_count_per_slot(spec); + let committees_per_slot = self.get_previous_epoch_committee_count(spec); let shuffling = self.get_shuffling( self.previous_epoch_seed, - self.previous_epoch_calculation_slot, + self.previous_calculation_epoch, spec, ); let slot_start_shard = (self.previous_epoch_start_shard + committees_per_slot * offset) % spec.shard_count; (committees_per_slot, shuffling, slot_start_shard) } else { - let committees_per_slot = self.get_current_epoch_committee_count_per_slot(spec); + let committees_per_slot = self.get_current_epoch_committee_count(spec); let shuffling = self.get_shuffling( self.current_epoch_seed, - self.current_epoch_calculation_slot, + self.current_calculation_epoch, spec, ); let slot_start_shard = @@ -301,34 +344,6 @@ impl BeaconState { Ok(crosslinks_at_slot) } - pub fn per_slot_processing( - &mut self, - previous_block_root: Hash256, - spec: &ChainSpec, - ) -> Result<(), SlotProcessingError> { - if (self.slot + 1) % spec.epoch_length == 0 { - self.per_epoch_processing(spec)?; - } - - self.slot += 1; - - let block_proposer = self.get_beacon_proposer_index(self.slot, spec)?; - - self.validator_registry[block_proposer].proposer_slots += 1; - self.latest_randao_mixes[(self.slot % spec.latest_randao_mixes_length).as_usize()] = self - .latest_randao_mixes[((self.slot - 1) % spec.latest_randao_mixes_length).as_usize()]; - - // Block roots. - self.latest_block_roots[((self.slot - 1) % spec.latest_block_roots_length).as_usize()] = - previous_block_root; - - if self.slot % spec.latest_block_roots_length == 0 { - let root = merkle_root(&self.latest_block_roots[..]); - self.batched_block_roots.push(root); - } - Ok(()) - } - pub fn attestation_slot_and_shard_for_validator( &self, validator_index: usize, @@ -336,7 +351,7 @@ impl BeaconState { ) -> Result, CommitteesError> { let mut result = None; for slot in self.current_epoch(spec).slot_iter(spec.epoch_length) { - for (committee, shard) in self.get_crosslink_committees_at_slot(slot, spec)? { + for (committee, shard) in self.get_crosslink_committees_at_slot(slot, false, spec)? { if let Some(committee_index) = committee.iter().position(|&i| i == validator_index) { result = Some((slot, shard, committee_index as u64)); @@ -346,254 +361,8 @@ impl BeaconState { Ok(result) } - pub fn per_block_processing( - &mut self, - block: &BeaconBlock, - spec: &ChainSpec, - ) -> Result<(), BlockProcessingError> { - self.per_block_processing_signature_optional(block, true, spec) - } - - pub fn per_block_processing_without_verifying_block_signature( - &mut self, - block: &BeaconBlock, - spec: &ChainSpec, - ) -> Result<(), BlockProcessingError> { - self.per_block_processing_signature_optional(block, false, spec) - } - - fn per_block_processing_signature_optional( - &mut self, - block: &BeaconBlock, - verify_block_signature: bool, - spec: &ChainSpec, - ) -> Result<(), BlockProcessingError> { - ensure!( - block.slot == self.slot, - BlockProcessingError::StateSlotMismatch - ); - - /* - * Proposer Signature - */ - let block_proposer_index = self - .get_beacon_proposer_index(block.slot, spec) - .map_err(|_| BlockProcessingError::NoBlockProducer)?; - let block_proposer = &self.validator_registry[block_proposer_index]; - - if verify_block_signature { - ensure!( - bls_verify( - &block_proposer.pubkey, - &block.proposal_root(spec)[..], - &block.signature, - get_domain(&self.fork_data, self.slot, DOMAIN_PROPOSAL) - ), - BlockProcessingError::BadBlockSignature - ); - } - - /* - * RANDAO - */ - ensure!( - bls_verify( - &block_proposer.pubkey, - &ssz_encode(&block_proposer.proposer_slots), - &block.randao_reveal, - get_domain(&self.fork_data, self.slot, DOMAIN_RANDAO) - ), - BlockProcessingError::BadRandaoSignature - ); - - // TODO: check this is correct. - let new_mix = { - let mut mix = self.latest_randao_mixes - [(self.slot % spec.latest_randao_mixes_length).as_usize()] - .to_vec(); - mix.append(&mut ssz_encode(&block.randao_reveal)); - Hash256::from(&hash(&mix)[..]) - }; - - self.latest_randao_mixes[(self.slot % spec.latest_randao_mixes_length).as_usize()] = - new_mix; - - /* - * Eth1 data - */ - - // TODO: Eth1 data processing. - - /* - * Proposer slashings - */ - ensure!( - block.body.proposer_slashings.len() as u64 <= spec.max_proposer_slashings, - BlockProcessingError::MaxProposerSlashingsExceeded - ); - for proposer_slashing in &block.body.proposer_slashings { - let proposer = self - .validator_registry - .get(proposer_slashing.proposer_index as usize) - .ok_or(BlockProcessingError::BadProposerSlashing)?; - ensure!( - proposer_slashing.proposal_data_1.slot == proposer_slashing.proposal_data_2.slot, - BlockProcessingError::BadProposerSlashing - ); - ensure!( - proposer_slashing.proposal_data_1.shard == proposer_slashing.proposal_data_2.shard, - BlockProcessingError::BadProposerSlashing - ); - ensure!( - proposer_slashing.proposal_data_1.block_root - != proposer_slashing.proposal_data_2.block_root, - BlockProcessingError::BadProposerSlashing - ); - ensure!( - proposer.penalized_slot > self.slot, - BlockProcessingError::BadProposerSlashing - ); - ensure!( - bls_verify( - &proposer.pubkey, - &proposer_slashing.proposal_data_1.hash_tree_root(), - &proposer_slashing.proposal_signature_1, - get_domain( - &self.fork_data, - proposer_slashing.proposal_data_1.slot, - DOMAIN_PROPOSAL - ) - ), - BlockProcessingError::BadProposerSlashing - ); - ensure!( - bls_verify( - &proposer.pubkey, - &proposer_slashing.proposal_data_2.hash_tree_root(), - &proposer_slashing.proposal_signature_2, - get_domain( - &self.fork_data, - proposer_slashing.proposal_data_2.slot, - DOMAIN_PROPOSAL - ) - ), - BlockProcessingError::BadProposerSlashing - ); - penalize_validator(&self, proposer_slashing.proposer_index as usize); - } - - /* - * Attestations - */ - ensure!( - block.body.attestations.len() as u64 <= spec.max_attestations, - BlockProcessingError::MaxAttestationsExceeded - ); - - for attestation in &block.body.attestations { - self.validate_attestation(attestation, spec)?; - - let pending_attestation = PendingAttestation { - data: attestation.data.clone(), - aggregation_bitfield: attestation.aggregation_bitfield.clone(), - custody_bitfield: attestation.custody_bitfield.clone(), - slot_included: self.slot, - }; - self.latest_attestations.push(pending_attestation); - } - - debug!( - "{} attestations verified & processed.", - block.body.attestations.len() - ); - - /* - * Deposits - */ - ensure!( - block.body.deposits.len() as u64 <= spec.max_deposits, - BlockProcessingError::MaxDepositsExceeded - ); - - // TODO: process deposits. - - /* - * Exits - */ - - ensure!( - block.body.exits.len() as u64 <= spec.max_exits, - BlockProcessingError::MaxExitsExceeded - ); - - for exit in &block.body.exits { - let validator = self - .validator_registry - .get(exit.validator_index as usize) - .ok_or(BlockProcessingError::BadExit)?; - ensure!( - validator.exit_slot > self.slot + spec.entry_exit_delay, - BlockProcessingError::BadExit - ); - ensure!(self.slot >= exit.slot, BlockProcessingError::BadExit); - let exit_message = { - let exit_struct = Exit { - slot: exit.slot, - validator_index: exit.validator_index, - signature: spec.empty_signature.clone(), - }; - exit_struct.hash_tree_root() - }; - ensure!( - bls_verify( - &validator.pubkey, - &exit_message, - &exit.signature, - get_domain(&self.fork_data, exit.slot, DOMAIN_EXIT) - ), - BlockProcessingError::BadProposerSlashing - ); - initiate_validator_exit(&self, exit.validator_index); - } - - /* - * Custody - */ - ensure!( - block.body.custody_reseeds.is_empty(), - BlockProcessingError::BadCustodyReseeds - ); - ensure!( - block.body.custody_challenges.is_empty(), - BlockProcessingError::BadCustodyChallenges - ); - ensure!( - block.body.custody_responses.is_empty(), - BlockProcessingError::BadCustodyResponses - ); - - debug!("State transition complete."); - - Ok(()) - } - - pub fn get_shuffling(&self, seed: Hash256, slot: Slot, spec: &ChainSpec) -> Vec> { - let slot = slot - (slot % spec.epoch_length); - - let active_validator_indices = get_active_validator_indices(&self.validator_registry, slot); - - let committees_per_slot = - self.get_committee_count_per_slot(active_validator_indices.len(), spec); - - // TODO: check that Hash256 matches 'int_to_bytes32'. - let seed = seed ^ Hash256::from(slot.as_u64()); - let shuffled_active_validator_indices = - shuffle(&seed, active_validator_indices).expect("Max validator count exceed!"); - - shuffled_active_validator_indices - .honey_badger_split((committees_per_slot * spec.epoch_length) as usize) - .filter_map(|slice: &[usize]| Some(slice.to_vec())) - .collect() + pub fn get_entry_exit_effect_epoch(&self, epoch: Epoch, spec: &ChainSpec) -> Epoch { + epoch + 1 + spec.entry_exit_delay } /// Returns the beacon proposer index for the `slot`. @@ -603,7 +372,7 @@ impl BeaconState { slot: Slot, spec: &ChainSpec, ) -> Result { - let committees = self.get_crosslink_committees_at_slot(slot, spec)?; + let committees = self.get_crosslink_committees_at_slot(slot, false, spec)?; committees .first() .ok_or(CommitteesError::InsufficientNumberOfValidators) @@ -616,527 +385,27 @@ impl BeaconState { }) } - pub fn per_epoch_processing(&mut self, spec: &ChainSpec) -> Result<(), EpochError> { - debug!( - "Starting per-epoch processing on epoch {}...", - self.current_epoch(spec) - ); - /* - * All Validators - */ - let active_validator_indices = - get_active_validator_indices(&self.validator_registry, self.slot); - let total_balance = self.get_effective_balances(&active_validator_indices[..], spec); - - debug!( - "{} validators with a total balance of {} wei.", - active_validator_indices.len(), - total_balance - ); - - let current_epoch_attestations: Vec<&PendingAttestation> = self - .latest_attestations - .par_iter() - .filter(|a| { - (a.data.slot / spec.epoch_length).epoch(spec.epoch_length) - == self.current_epoch(spec) - }) - .collect(); - - debug!( - "Current epoch attestations: {}", - current_epoch_attestations.len() - ); - - /* - * Validators attesting during the current epoch. - */ - if self.latest_block_roots.is_empty() { - return Err(EpochError::NoBlockRoots); - } - - let current_epoch_boundary_attestations: Vec<&PendingAttestation> = - current_epoch_attestations - .par_iter() - .filter(|a| { - match self.get_block_root(self.current_epoch_start_slot(spec), spec) { - Some(block_root) => { - (a.data.epoch_boundary_root == *block_root) - && (a.data.justified_slot == self.justified_slot) - } - // Protected by a check that latest_block_roots isn't empty. - // - // TODO: provide detailed reasoning. - None => unreachable!(), - } - }) - .cloned() - .collect(); - - let current_epoch_boundary_attester_indices = self - .get_attestation_participants_union(¤t_epoch_boundary_attestations[..], spec)?; - let current_epoch_boundary_attesting_balance = - self.get_effective_balances(¤t_epoch_boundary_attester_indices[..], spec); - - debug!( - "Current epoch boundary attesters: {}", - current_epoch_boundary_attester_indices.len() - ); - - /* - * Validators attesting during the previous epoch - */ - - /* - * Validators that made an attestation during the previous epoch - */ - let previous_epoch_attestations: Vec<&PendingAttestation> = self - .latest_attestations - .par_iter() - .filter(|a| { - //TODO: ensure these saturating subs are correct. - (a.data.slot / spec.epoch_length).epoch(spec.epoch_length) - == self.previous_epoch(spec) - }) - .collect(); - - debug!( - "previous epoch attestations: {}", - previous_epoch_attestations.len() - ); - - let previous_epoch_attester_indices = - self.get_attestation_participants_union(&previous_epoch_attestations[..], spec)?; - - /* - * Validators targetting the previous justified slot - */ - let previous_epoch_justified_attestations: Vec<&PendingAttestation> = { - let mut a: Vec<&PendingAttestation> = current_epoch_attestations - .iter() - .filter(|a| a.data.justified_slot == self.previous_justified_slot) - .cloned() - .collect(); - let mut b: Vec<&PendingAttestation> = previous_epoch_attestations - .iter() - .filter(|a| a.data.justified_slot == self.previous_justified_slot) - .cloned() - .collect(); - a.append(&mut b); - a - }; - - let previous_epoch_justified_attester_indices = self - .get_attestation_participants_union(&previous_epoch_justified_attestations[..], spec)?; - let previous_epoch_justified_attesting_balance = - self.get_effective_balances(&previous_epoch_justified_attester_indices[..], spec); - - /* - * Validators justifying the epoch boundary block at the start of the previous epoch - */ - let previous_epoch_boundary_attestations: Vec<&PendingAttestation> = - previous_epoch_justified_attestations - .iter() - .filter(|a| { - match self.get_block_root(self.previous_epoch_start_slot(spec), spec) { - Some(block_root) => a.data.epoch_boundary_root == *block_root, - // Protected by a check that latest_block_roots isn't empty. - // - // TODO: provide detailed reasoning. - None => unreachable!(), - } - }) - .cloned() - .collect(); - - let previous_epoch_boundary_attester_indices = self - .get_attestation_participants_union(&previous_epoch_boundary_attestations[..], spec)?; - let previous_epoch_boundary_attesting_balance = - self.get_effective_balances(&previous_epoch_boundary_attester_indices[..], spec); - - /* - * Validators attesting to the expected beacon chain head during the previous epoch. - */ - let previous_epoch_head_attestations: Vec<&PendingAttestation> = - previous_epoch_attestations - .iter() - .filter(|a| { - match self.get_block_root(a.data.slot, spec) { - Some(block_root) => a.data.beacon_block_root == *block_root, - // Protected by a check that latest_block_roots isn't empty. - // - // TODO: provide detailed reasoning. - None => unreachable!(), - } - }) - .cloned() - .collect(); - - let previous_epoch_head_attester_indices = - self.get_attestation_participants_union(&previous_epoch_head_attestations[..], spec)?; - let previous_epoch_head_attesting_balance = - self.get_effective_balances(&previous_epoch_head_attester_indices[..], spec); - - debug!( - "previous_epoch_head_attester_balance of {} wei.", - previous_epoch_head_attesting_balance - ); - - /* - * Eth1 Data - */ - if self.slot % spec.eth1_data_voting_period == 0 { - for eth1_data_vote in &self.eth1_data_votes { - if eth1_data_vote.vote_count * 2 > spec.eth1_data_voting_period { - self.latest_eth1_data = eth1_data_vote.eth1_data.clone(); - } - } - self.eth1_data_votes = vec![]; - } - - /* - * Justification - */ - self.previous_justified_slot = self.justified_slot; - let (new_bitfield, _) = self.justification_bitfield.overflowing_mul(2); - self.justification_bitfield = new_bitfield; - - // If >= 2/3 of validators voted for the previous epoch boundary - if (3 * previous_epoch_boundary_attesting_balance) >= (2 * total_balance) { - // TODO: check saturating_sub is correct. - self.justification_bitfield |= 2; - self.justified_slot = self.slot.saturating_sub(2 * spec.epoch_length); - debug!(">= 2/3 voted for previous epoch boundary"); - } - - // If >= 2/3 of validators voted for the current epoch boundary - if (3 * current_epoch_boundary_attesting_balance) >= (2 * total_balance) { - // TODO: check saturating_sub is correct. - self.justification_bitfield |= 1; - self.justified_slot = self.slot.saturating_sub(1 * spec.epoch_length); - debug!(">= 2/3 voted for current epoch boundary"); - } - - if (self.previous_justified_slot == self.slot.saturating_sub(2 * spec.epoch_length)) - && (self.justification_bitfield % 4 == 3) - { - self.finalized_slot = self.previous_justified_slot; - } - if (self.previous_justified_slot == self.slot.saturating_sub(3 * spec.epoch_length)) - && (self.justification_bitfield % 8 == 7) - { - self.finalized_slot = self.previous_justified_slot; - } - if (self.previous_justified_slot == self.slot.saturating_sub(4 * spec.epoch_length)) - && (self.justification_bitfield % 16 == 14) - { - self.finalized_slot = self.previous_justified_slot; - } - if (self.previous_justified_slot == self.slot.saturating_sub(4 * spec.epoch_length)) - && (self.justification_bitfield % 16 == 15) - { - self.finalized_slot = self.previous_justified_slot; - } - - debug!( - "Finalized slot {}, justified slot {}.", - self.finalized_slot, self.justified_slot - ); - - /* - * Crosslinks - */ - - // Cached for later lookups. - let mut winning_root_for_shards: HashMap> = - HashMap::new(); - - // for slot in self.slot.saturating_sub(2 * spec.epoch_length)..self.slot { - for slot in self.previous_epoch(spec).slot_iter(spec.epoch_length) { - let crosslink_committees_at_slot = self.get_crosslink_committees_at_slot(slot, spec)?; - - for (crosslink_committee, shard) in crosslink_committees_at_slot { - let shard = shard as u64; - - let winning_root = self.winning_root( - shard, - ¤t_epoch_attestations, - &previous_epoch_attestations, - spec, - ); - - if let Ok(winning_root) = &winning_root { - let total_committee_balance = - self.get_effective_balances(&crosslink_committee[..], spec); - - if (3 * winning_root.total_attesting_balance) >= (2 * total_committee_balance) { - self.latest_crosslinks[shard as usize] = Crosslink { - slot: self.slot, - shard_block_root: winning_root.shard_block_root, - } - } - } - winning_root_for_shards.insert(shard, winning_root); - } - } - - debug!( - "Found {} winning shard roots.", - winning_root_for_shards.len() - ); - - /* - * Rewards and Penalities - */ - let base_reward_quotient = total_balance.integer_sqrt(); - if base_reward_quotient == 0 { - return Err(EpochError::BaseRewardQuotientIsZero); - } - - /* - * Justification and finalization - */ - let epochs_since_finality = - (self.slot.saturating_sub(self.finalized_slot) / spec.epoch_length).as_u64(); - - // TODO: fix this extra map - let previous_epoch_justified_attester_indices_hashset: HashSet = - HashSet::from_iter(previous_epoch_justified_attester_indices.iter().map(|i| *i)); - let previous_epoch_boundary_attester_indices_hashset: HashSet = - HashSet::from_iter(previous_epoch_boundary_attester_indices.iter().map(|i| *i)); - let previous_epoch_head_attester_indices_hashset: HashSet = - HashSet::from_iter(previous_epoch_head_attester_indices.iter().map(|i| *i)); - let previous_epoch_attester_indices_hashset: HashSet = - HashSet::from_iter(previous_epoch_attester_indices.iter().map(|i| *i)); - - debug!("previous epoch justified attesters: {}, previous epoch boundary attesters: {}, previous epoch head attesters: {}, previous epoch attesters: {}", previous_epoch_justified_attester_indices.len(), previous_epoch_boundary_attester_indices.len(), previous_epoch_head_attester_indices.len(), previous_epoch_attester_indices.len()); - - debug!("{} epochs since finality.", epochs_since_finality); - - if epochs_since_finality <= 4 { - for index in 0..self.validator_balances.len() { - let base_reward = self.base_reward(index, base_reward_quotient, spec); - - if previous_epoch_justified_attester_indices_hashset.contains(&index) { - safe_add_assign!( - self.validator_balances[index], - base_reward * previous_epoch_justified_attesting_balance / total_balance - ); - } else { - safe_sub_assign!(self.validator_balances[index], base_reward); - } - - if previous_epoch_boundary_attester_indices_hashset.contains(&index) { - safe_add_assign!( - self.validator_balances[index], - base_reward * previous_epoch_boundary_attesting_balance / total_balance - ); - } else { - safe_sub_assign!(self.validator_balances[index], base_reward); - } - - if previous_epoch_head_attester_indices_hashset.contains(&index) { - safe_add_assign!( - self.validator_balances[index], - base_reward * previous_epoch_head_attesting_balance / total_balance - ); - } else { - safe_sub_assign!(self.validator_balances[index], base_reward); - } - } - - for index in previous_epoch_attester_indices { - let base_reward = self.base_reward(index, base_reward_quotient, spec); - let inclusion_distance = - self.inclusion_distance(&previous_epoch_attestations, index, spec)?; - - safe_add_assign!( - self.validator_balances[index], - base_reward * spec.min_attestation_inclusion_delay / inclusion_distance - ) - } - } else { - for index in 0..self.validator_balances.len() { - let inactivity_penalty = self.inactivity_penalty( - index, - epochs_since_finality, - base_reward_quotient, - spec, - ); - - if !previous_epoch_justified_attester_indices_hashset.contains(&index) { - safe_sub_assign!(self.validator_balances[index], inactivity_penalty); - } - - if !previous_epoch_boundary_attester_indices_hashset.contains(&index) { - safe_sub_assign!(self.validator_balances[index], inactivity_penalty); - } - - if !previous_epoch_head_attester_indices_hashset.contains(&index) { - safe_sub_assign!(self.validator_balances[index], inactivity_penalty); - } - } - - for index in previous_epoch_attester_indices { - let base_reward = self.base_reward(index, base_reward_quotient, spec); - let inclusion_distance = - self.inclusion_distance(&previous_epoch_attestations, index, spec)?; - - safe_sub_assign!( - self.validator_balances[index], - base_reward - - base_reward * spec.min_attestation_inclusion_delay / inclusion_distance - ); - } - } - - debug!("Processed validator justification and finalization rewards/penalities."); - - /* - * Attestation inclusion - */ - for &index in &previous_epoch_attester_indices_hashset { - let inclusion_slot = - self.inclusion_slot(&previous_epoch_attestations[..], index, spec)?; - let proposer_index = self - .get_beacon_proposer_index(inclusion_slot, spec) - .map_err(|_| EpochError::UnableToDetermineProducer)?; - let base_reward = self.base_reward(proposer_index, base_reward_quotient, spec); - safe_add_assign!( - self.validator_balances[proposer_index], - base_reward / spec.includer_reward_quotient - ); - } - - debug!( - "Previous epoch attesters: {}.", - previous_epoch_attester_indices_hashset.len() - ); - - /* - * Crosslinks - */ - for slot in self.previous_epoch(spec).slot_iter(spec.epoch_length) { - let crosslink_committees_at_slot = self.get_crosslink_committees_at_slot(slot, spec)?; - - for (_crosslink_committee, shard) in crosslink_committees_at_slot { - let shard = shard as u64; - - if let Some(Ok(winning_root)) = winning_root_for_shards.get(&shard) { - // TODO: remove the map. - let attesting_validator_indices: HashSet = HashSet::from_iter( - winning_root.attesting_validator_indices.iter().map(|i| *i), - ); - - for index in 0..self.validator_balances.len() { - let base_reward = self.base_reward(index, base_reward_quotient, spec); - - if attesting_validator_indices.contains(&index) { - safe_add_assign!( - self.validator_balances[index], - base_reward * winning_root.total_attesting_balance - / winning_root.total_balance - ); - } else { - safe_sub_assign!(self.validator_balances[index], base_reward); - } - } - - for index in &winning_root.attesting_validator_indices { - let base_reward = self.base_reward(*index, base_reward_quotient, spec); - safe_add_assign!( - self.validator_balances[*index], - base_reward * winning_root.total_attesting_balance - / winning_root.total_balance - ); - } - } - } - } - - /* - * Ejections - */ - self.process_ejections(); - - /* - * Validator Registry - */ - self.previous_epoch_calculation_slot = self.current_epoch_calculation_slot; - self.previous_epoch_start_shard = self.current_epoch_start_shard; - self.previous_epoch_seed = self.current_epoch_seed; - - let should_update_validator_registy = if self.finalized_slot - > self.validator_registry_update_slot - { - (0..self.get_current_epoch_committee_count_per_slot(spec)).all(|i| { - let shard = (self.current_epoch_start_shard + i as u64) % spec.shard_count; - self.latest_crosslinks[shard as usize].slot > self.validator_registry_update_slot - }) - } else { - false - }; - - if should_update_validator_registy { - self.update_validator_registry(spec); - - self.current_epoch_calculation_slot = self.slot; - self.current_epoch_start_shard = (self.current_epoch_start_shard - + self.get_current_epoch_committee_count_per_slot(spec) as u64 * spec.epoch_length) - % spec.shard_count; - self.current_epoch_seed = self.get_randao_mix( - self.current_epoch_calculation_slot - spec.seed_lookahead, - spec, - ); - } else { - let epochs_since_last_registry_change = - (self.slot - self.validator_registry_update_slot) / spec.epoch_length; - if epochs_since_last_registry_change.is_power_of_two() { - self.current_epoch_calculation_slot = self.slot; - self.current_epoch_seed = self.get_randao_mix( - self.current_epoch_calculation_slot - spec.seed_lookahead, - spec, - ); - } - } - - self.process_penalties_and_exits(spec); - - let e = self.slot / spec.epoch_length; - self.latest_penalized_balances[((e + 1) % spec.latest_penalized_exit_length).as_usize()] = - self.latest_penalized_balances[(e % spec.latest_penalized_exit_length).as_usize()]; - - self.latest_attestations = self - .latest_attestations - .iter() - .filter(|a| { - (a.data.slot / spec.epoch_length).epoch(spec.epoch_length) - >= self.current_epoch(spec) - }) - .cloned() - .collect(); - - debug!("Epoch transition complete."); - - Ok(()) - } - + /// Process the penalties and prepare the validators who are eligible to withdrawal. + /// + /// Spec v0.2.0 fn process_penalties_and_exits(&mut self, spec: &ChainSpec) { + let current_epoch = self.current_epoch(spec); let active_validator_indices = - get_active_validator_indices(&self.validator_registry, self.slot); - let total_balance = self.get_effective_balances(&active_validator_indices[..], spec); + get_active_validator_indices(&self.validator_registry, current_epoch); + let total_balance = self.get_total_balance(&active_validator_indices[..], spec); for index in 0..self.validator_balances.len() { let validator = &self.validator_registry[index]; - if (self.slot / spec.epoch_length) - == (validator.penalized_slot / spec.epoch_length) - + spec.latest_penalized_exit_length / 2 + if current_epoch + == validator.penalized_epoch + Epoch::from(spec.latest_penalized_exit_length / 2) { - let e = (self.slot / spec.epoch_length) % spec.latest_penalized_exit_length; + let epoch_index: usize = + current_epoch.as_usize() % spec.latest_penalized_exit_length; + let total_at_start = self.latest_penalized_balances - [((e + 1) % spec.latest_penalized_exit_length).as_usize()]; - let total_at_end = self.latest_penalized_balances[e.as_usize()]; + [(epoch_index + 1) % spec.latest_penalized_exit_length]; + let total_at_end = self.latest_penalized_balances[epoch_index]; let total_penalities = total_at_end.saturating_sub(total_at_start); let penalty = self.get_effective_balance(index, spec) * std::cmp::min(total_penalities * 3, total_balance) @@ -1148,19 +417,18 @@ impl BeaconState { let eligible = |index: usize| { let validator = &self.validator_registry[index]; - if validator.penalized_slot <= self.slot { - let penalized_withdrawal_time = - spec.latest_penalized_exit_length * spec.epoch_length / 2; - self.slot >= validator.penalized_slot + penalized_withdrawal_time + if validator.penalized_epoch <= current_epoch { + let penalized_withdrawal_epochs = spec.latest_penalized_exit_length / 2; + current_epoch >= validator.penalized_epoch + penalized_withdrawal_epochs as u64 } else { - self.slot >= validator.exit_slot + spec.min_validator_withdrawal_time + current_epoch >= validator.exit_epoch + spec.min_validator_withdrawal_epochs } }; let mut eligable_indices: Vec = (0..self.validator_registry.len()) .filter(|i| eligible(*i)) .collect(); - eligable_indices.sort_by_key(|i| self.validator_registry[*i].exit_count); + eligable_indices.sort_by_key(|i| self.validator_registry[*i].exit_epoch); let mut withdrawn_so_far = 0; for index in eligable_indices { self.prepare_validator_for_withdrawal(index); @@ -1171,24 +439,27 @@ impl BeaconState { } } - fn prepare_validator_for_withdrawal(&mut self, index: usize) { - //TODO: we're not ANDing here, we're setting. Potentially wrong. - self.validator_registry[index].status_flags = Some(StatusFlags::Withdrawable); - } - - fn get_randao_mix(&mut self, slot: Slot, spec: &ChainSpec) -> Hash256 { - assert!(self.slot < slot + spec.latest_randao_mixes_length); - assert!(slot <= self.slot); - self.latest_randao_mixes[(slot % spec.latest_randao_mixes_length).as_usize()] + /// Return the randao mix at a recent ``epoch``. + /// + /// Returns `None` if the epoch is out-of-bounds of `self.latest_randao_mixes`. + /// + /// Spec v0.2.0 + fn get_randao_mix(&mut self, epoch: Epoch, spec: &ChainSpec) -> Option<&Hash256> { + self.latest_randao_mixes + .get(epoch.as_usize() % spec.latest_randao_mixes_length) } + /// Update validator registry, activating/exiting validators if possible. + /// + /// Spec v0.2.0 fn update_validator_registry(&mut self, spec: &ChainSpec) { + let current_epoch = self.current_epoch(spec); let active_validator_indices = - get_active_validator_indices(&self.validator_registry, self.slot); - let total_balance = self.get_effective_balances(&active_validator_indices[..], spec); + get_active_validator_indices(&self.validator_registry, current_epoch); + let total_balance = self.get_total_balance(&active_validator_indices[..], spec); let max_balance_churn = std::cmp::max( - spec.max_deposit, + spec.max_deposit_amount, total_balance / (2 * spec.max_balance_churn_quotient), ); @@ -1196,14 +467,13 @@ impl BeaconState { for index in 0..self.validator_registry.len() { let validator = &self.validator_registry[index]; - if (validator.activation_slot > self.slot + spec.entry_exit_delay) - && self.validator_balances[index] >= spec.max_deposit + if (validator.activation_epoch > self.get_entry_exit_effect_epoch(current_epoch, spec)) + && self.validator_balances[index] >= spec.max_deposit_amount { balance_churn += self.get_effective_balance(index, spec); if balance_churn > max_balance_churn { break; } - self.activate_validator(index, false, spec); } } @@ -1212,7 +482,7 @@ impl BeaconState { for index in 0..self.validator_registry.len() { let validator = &self.validator_registry[index]; - if (validator.exit_slot > self.slot + spec.entry_exit_delay) + if (validator.exit_epoch > self.get_entry_exit_effect_epoch(current_epoch, spec)) && validator.status_flags == Some(StatusFlags::InitiatedExit) { balance_churn += self.get_effective_balance(index, spec); @@ -1224,39 +494,105 @@ impl BeaconState { } } - self.validator_registry_update_slot = self.slot; + self.validator_registry_update_epoch = current_epoch; } + /// Activate the validator of the given ``index``. + /// + /// Spec v0.2.0 + fn activate_validator(&mut self, validator_index: usize, is_genesis: bool, spec: &ChainSpec) { + let current_epoch = self.current_epoch(spec); + + self.validator_registry[validator_index].activation_epoch = if is_genesis { + spec.genesis_epoch + } else { + self.get_entry_exit_effect_epoch(current_epoch, spec) + } + } + + /// Initiate an exit for the validator of the given `index`. + /// + /// Spec v0.2.0 + fn initiate_validator_exit(&mut self, validator_index: usize, spec: &ChainSpec) { + // TODO: the spec does an `|=` here, ensure this isn't buggy. + self.validator_registry[validator_index].status_flags = Some(StatusFlags::InitiatedExit); + } + + /// Exit the validator of the given `index`. + /// + /// Spec v0.2.0 fn exit_validator(&mut self, validator_index: usize, spec: &ChainSpec) { - if self.validator_registry[validator_index].exit_slot - <= self.entry_exit_effect_slot(self.slot, spec) + let current_epoch = self.current_epoch(spec); + + if self.validator_registry[validator_index].exit_epoch + <= self.get_entry_exit_effect_epoch(current_epoch, spec) { return; } - self.validator_registry[validator_index].exit_slot = - self.entry_exit_effect_slot(self.slot, spec); - - self.validator_registry_exit_count += 1; - self.validator_registry[validator_index].exit_count = self.validator_registry_exit_count; + self.validator_registry[validator_index].exit_epoch = + self.get_entry_exit_effect_epoch(current_epoch, spec); } - fn activate_validator(&mut self, validator_index: usize, is_genesis: bool, spec: &ChainSpec) { - self.validator_registry[validator_index].activation_slot = if is_genesis { - spec.genesis_slot - } else { - self.entry_exit_effect_slot(self.slot, spec) + /// Penalize the validator of the given ``index``. + /// + /// Exits the validator and assigns its effective balance to the block producer for this + /// state. + /// + /// Spec v0.2.0 + pub fn penalize_validator( + &mut self, + validator_index: usize, + spec: &ChainSpec, + ) -> Result<(), CommitteesError> { + self.exit_validator(validator_index, spec); + let current_epoch = self.current_epoch(spec); + + self.latest_penalized_balances + [current_epoch.as_usize() % spec.latest_penalized_exit_length] += + self.get_effective_balance(validator_index, spec); + + let whistleblower_index = self.get_beacon_proposer_index(self.slot, spec)?; + let whistleblower_reward = self.get_effective_balance(validator_index, spec); + safe_add_assign!( + self.validator_balances[whistleblower_index as usize], + whistleblower_reward + ); + safe_sub_assign!( + self.validator_balances[validator_index], + whistleblower_reward + ); + self.validator_registry[validator_index].penalized_epoch = current_epoch; + Ok(()) + } + + /// Initiate an exit for the validator of the given `index`. + /// + /// Spec v0.2.0 + fn prepare_validator_for_withdrawal(&mut self, validator_index: usize) { + //TODO: we're not ANDing here, we're setting. Potentially wrong. + self.validator_registry[validator_index].status_flags = Some(StatusFlags::Withdrawable); + } + + /// Iterate through the validator registry and eject active validators with balance below + /// ``EJECTION_BALANCE``. + /// + /// Spec v0.2.0 + fn process_ejections(&mut self, spec: &ChainSpec) { + for validator_index in + get_active_validator_indices(&self.validator_registry, self.current_epoch(spec)) + { + if self.validator_balances[validator_index] < spec.ejection_balance { + self.exit_validator(validator_index, spec) + } } } - fn entry_exit_effect_slot(&self, slot: Slot, spec: &ChainSpec) -> Slot { - (slot - slot % spec.epoch_length) + spec.epoch_length + spec.entry_exit_delay - } - - fn process_ejections(&self) { - //TODO: stubbed out. - } - + /// Returns the penality that should be applied to some validator for inactivity. + /// + /// Note: this is defined "inline" in the spec, not as a helper function. + /// + /// Spec v0.2.0 fn inactivity_penalty( &self, validator_index: usize, @@ -1269,6 +605,12 @@ impl BeaconState { + effective_balance * epochs_since_finality / spec.inactivity_penalty_quotient / 2 } + /// Returns the distance between the first included attestation for some validator and this + /// slot. + /// + /// Note: In the spec this is defined "inline", not as a helper function. + /// + /// Spec v0.2.0 fn inclusion_distance( &self, attestations: &[&PendingAttestation], @@ -1277,9 +619,14 @@ impl BeaconState { ) -> Result { let attestation = self.earliest_included_attestation(attestations, validator_index, spec)?; - Ok((attestation.slot_included - attestation.data.slot).as_u64()) + Ok((attestation.inclusion_slot - attestation.data.slot).as_u64()) } + /// Returns the slot of the earliest included attestation for some validator. + /// + /// Note: In the spec this is defined "inline", not as a helper function. + /// + /// Spec v0.2.0 fn inclusion_slot( &self, attestations: &[&PendingAttestation], @@ -1288,9 +635,14 @@ impl BeaconState { ) -> Result { let attestation = self.earliest_included_attestation(attestations, validator_index, spec)?; - Ok(attestation.slot_included) + Ok(attestation.inclusion_slot) } + /// Finds the earliest included attestation for some validator. + /// + /// Note: In the spec this is defined "inline", not as a helper function. + /// + /// Spec v0.2.0 fn earliest_included_attestation( &self, attestations: &[&PendingAttestation], @@ -1313,11 +665,16 @@ impl BeaconState { let earliest_attestation_index = included_attestations .iter() - .min_by_key(|i| attestations[**i].slot_included) + .min_by_key(|i| attestations[**i].inclusion_slot) .ok_or_else(|| InclusionError::NoIncludedAttestations)?; Ok(attestations[*earliest_attestation_index].clone()) } + /// Returns the base reward for some validator. + /// + /// Note: In the spec this is defined "inline", not as a helper function. + /// + /// Spec v0.2.0 fn base_reward( &self, validator_index: usize, @@ -1327,23 +684,31 @@ impl BeaconState { self.get_effective_balance(validator_index, spec) / base_reward_quotient / 5 } - pub fn get_effective_balances(&self, validator_indices: &[usize], spec: &ChainSpec) -> u64 { + /// Return the combined effective balance of an array of validators. + /// + /// Spec v0.2.0 + pub fn get_total_balance(&self, validator_indices: &[usize], spec: &ChainSpec) -> u64 { validator_indices .iter() .fold(0, |acc, i| acc + self.get_effective_balance(*i, spec)) } + /// Return the effective balance (also known as "balance at stake") for a validator with the given ``index``. + /// + /// Spec v0.2.0 pub fn get_effective_balance(&self, validator_index: usize, spec: &ChainSpec) -> u64 { - std::cmp::min(self.validator_balances[validator_index], spec.max_deposit) + std::cmp::min( + self.validator_balances[validator_index], + spec.max_deposit_amount, + ) } + /// Return the block root at a recent `slot`. + /// + /// Spec v0.2.0 pub fn get_block_root(&self, slot: Slot, spec: &ChainSpec) -> Option<&Hash256> { - if self.slot <= slot + spec.latest_block_roots_length && slot <= self.slot { - self.latest_block_roots - .get((slot % spec.latest_block_roots_length).as_usize()) - } else { - None - } + self.latest_block_roots + .get(slot.as_usize() % spec.latest_block_roots_length) } pub(crate) fn winning_root( @@ -1455,7 +820,7 @@ impl BeaconState { spec: &ChainSpec, ) -> Result, AttestationParticipantsError> { let crosslink_committees = - self.get_crosslink_committees_at_slot(attestation_data.slot, spec)?; + self.get_crosslink_committees_at_slot(attestation_data.slot, false, spec)?; let committee_index: usize = crosslink_committees .iter() @@ -1509,27 +874,33 @@ impl BeaconState { ); if attestation.data.slot >= self.current_epoch_start_slot(spec) { ensure!( - attestation.data.justified_slot == self.justified_slot, + attestation.data.justified_epoch == self.justified_epoch, AttestationValidationError::WrongJustifiedSlot ); } else { ensure!( - attestation.data.justified_slot == self.previous_justified_slot, + attestation.data.justified_epoch == self.previous_justified_epoch, AttestationValidationError::WrongJustifiedSlot ); } ensure!( attestation.data.justified_block_root == *self - .get_block_root(attestation.data.justified_slot, &spec) + .get_block_root( + attestation + .data + .justified_epoch + .start_slot(spec.epoch_length), + &spec + ) .ok_or(AttestationValidationError::NoBlockRoot)?, AttestationValidationError::WrongJustifiedRoot ); ensure!( - (attestation.data.latest_crosslink_root - == self.latest_crosslinks[attestation.data.shard as usize].shard_block_root) - || (attestation.data.shard_block_root - == self.latest_crosslinks[attestation.data.shard as usize].shard_block_root), + (attestation.data.latest_crosslink + == self.latest_crosslinks[attestation.data.shard as usize]) + || (attestation.data.latest_crosslink + == self.latest_crosslinks[attestation.data.shard as usize]), AttestationValidationError::BadLatestCrosslinkRoot ); if verify_signature { @@ -1551,7 +922,11 @@ impl BeaconState { &group_public_key, &attestation.signable_message(PHASE_0_CUSTODY_BIT), &attestation.aggregate_signature, - get_domain(&self.fork_data, attestation.data.slot, DOMAIN_ATTESTATION) + get_domain( + &self.fork, + attestation.data.slot.epoch(spec.epoch_length), + DOMAIN_ATTESTATION + ) ), AttestationValidationError::BadSignature ); @@ -1568,15 +943,7 @@ fn merkle_root(_input: &[Hash256]) -> Hash256 { Hash256::zero() } -fn initiate_validator_exit(_state: &BeaconState, _index: u32) { - // TODO: stubbed out. -} - -fn penalize_validator(_state: &BeaconState, _proposer_index: usize) { - // TODO: stubbed out. -} - -fn get_domain(_fork: &Fork, _slot: Slot, _domain_type: u64) -> u64 { +fn get_domain(_fork: &Fork, _epoch: Epoch, _domain_type: u64) -> u64 { // TODO: stubbed out. 0 } @@ -1604,6 +971,7 @@ impl From for AttestationParticipantsError { } } +/* impl From for BlockProcessingError { fn from(e: AttestationValidationError) -> BlockProcessingError { BlockProcessingError::InvalidAttestation(e) @@ -1633,6 +1001,7 @@ impl From for SlotProcessingError { SlotProcessingError::EpochProcessingError(e) } } +*/ impl From for InclusionError { fn from(e: AttestationParticipantsError) -> InclusionError { @@ -1640,6 +1009,7 @@ impl From for InclusionError { } } +/* impl From for EpochError { fn from(e: InclusionError) -> EpochError { EpochError::InclusionError(e) @@ -1657,6 +1027,7 @@ impl From for EpochError { EpochError::AttestationParticipantsError(e) } } +*/ impl From for Error { fn from(e: CommitteesError) -> Error { @@ -1668,25 +1039,21 @@ impl Encodable for BeaconState { fn ssz_append(&self, s: &mut SszStream) { s.append(&self.slot); s.append(&self.genesis_time); - s.append(&self.fork_data); + s.append(&self.fork); s.append(&self.validator_registry); s.append(&self.validator_balances); - s.append(&self.validator_registry_update_slot); - s.append(&self.validator_registry_exit_count); - s.append(&self.validator_registry_delta_chain_tip); + s.append(&self.validator_registry_update_epoch); s.append(&self.latest_randao_mixes); - s.append(&self.latest_vdf_outputs); s.append(&self.previous_epoch_start_shard); s.append(&self.current_epoch_start_shard); - s.append(&self.previous_epoch_calculation_slot); - s.append(&self.current_epoch_calculation_slot); + s.append(&self.previous_calculation_epoch); + s.append(&self.current_calculation_epoch); s.append(&self.previous_epoch_seed); s.append(&self.current_epoch_seed); - s.append(&self.custody_challenges); - s.append(&self.previous_justified_slot); - s.append(&self.justified_slot); + s.append(&self.previous_justified_epoch); + s.append(&self.justified_epoch); s.append(&self.justification_bitfield); - s.append(&self.finalized_slot); + s.append(&self.finalized_epoch); s.append(&self.latest_crosslinks); s.append(&self.latest_block_roots); s.append(&self.latest_penalized_balances); @@ -1701,25 +1068,21 @@ impl Decodable for BeaconState { fn ssz_decode(bytes: &[u8], i: usize) -> Result<(Self, usize), DecodeError> { let (slot, i) = <_>::ssz_decode(bytes, i)?; let (genesis_time, i) = <_>::ssz_decode(bytes, i)?; - let (fork_data, i) = <_>::ssz_decode(bytes, i)?; + let (fork, i) = <_>::ssz_decode(bytes, i)?; let (validator_registry, i) = <_>::ssz_decode(bytes, i)?; let (validator_balances, i) = <_>::ssz_decode(bytes, i)?; - let (validator_registry_update_slot, i) = <_>::ssz_decode(bytes, i)?; - let (validator_registry_exit_count, i) = <_>::ssz_decode(bytes, i)?; - let (validator_registry_delta_chain_tip, i) = <_>::ssz_decode(bytes, i)?; + let (validator_registry_update_epoch, i) = <_>::ssz_decode(bytes, i)?; let (latest_randao_mixes, i) = <_>::ssz_decode(bytes, i)?; - let (latest_vdf_outputs, i) = <_>::ssz_decode(bytes, i)?; let (previous_epoch_start_shard, i) = <_>::ssz_decode(bytes, i)?; let (current_epoch_start_shard, i) = <_>::ssz_decode(bytes, i)?; - let (previous_epoch_calculation_slot, i) = <_>::ssz_decode(bytes, i)?; - let (current_epoch_calculation_slot, i) = <_>::ssz_decode(bytes, i)?; + let (previous_calculation_epoch, i) = <_>::ssz_decode(bytes, i)?; + let (current_calculation_epoch, i) = <_>::ssz_decode(bytes, i)?; let (previous_epoch_seed, i) = <_>::ssz_decode(bytes, i)?; let (current_epoch_seed, i) = <_>::ssz_decode(bytes, i)?; - let (custody_challenges, i) = <_>::ssz_decode(bytes, i)?; - let (previous_justified_slot, i) = <_>::ssz_decode(bytes, i)?; - let (justified_slot, i) = <_>::ssz_decode(bytes, i)?; + let (previous_justified_epoch, i) = <_>::ssz_decode(bytes, i)?; + let (justified_epoch, i) = <_>::ssz_decode(bytes, i)?; let (justification_bitfield, i) = <_>::ssz_decode(bytes, i)?; - let (finalized_slot, i) = <_>::ssz_decode(bytes, i)?; + let (finalized_epoch, i) = <_>::ssz_decode(bytes, i)?; let (latest_crosslinks, i) = <_>::ssz_decode(bytes, i)?; let (latest_block_roots, i) = <_>::ssz_decode(bytes, i)?; let (latest_penalized_balances, i) = <_>::ssz_decode(bytes, i)?; @@ -1732,25 +1095,21 @@ impl Decodable for BeaconState { Self { slot, genesis_time, - fork_data, + fork, validator_registry, validator_balances, - validator_registry_update_slot, - validator_registry_exit_count, - validator_registry_delta_chain_tip, + validator_registry_update_epoch, latest_randao_mixes, - latest_vdf_outputs, previous_epoch_start_shard, current_epoch_start_shard, - previous_epoch_calculation_slot, - current_epoch_calculation_slot, + previous_calculation_epoch, + current_calculation_epoch, previous_epoch_seed, current_epoch_seed, - custody_challenges, - previous_justified_slot, - justified_slot, + previous_justified_epoch, + justified_epoch, justification_bitfield, - finalized_slot, + finalized_epoch, latest_crosslinks, latest_block_roots, latest_penalized_balances, @@ -1769,25 +1128,21 @@ impl TreeHash for BeaconState { let mut result: Vec = vec![]; result.append(&mut self.slot.hash_tree_root()); result.append(&mut self.genesis_time.hash_tree_root()); - result.append(&mut self.fork_data.hash_tree_root()); + result.append(&mut self.fork.hash_tree_root()); result.append(&mut self.validator_registry.hash_tree_root()); result.append(&mut self.validator_balances.hash_tree_root()); - result.append(&mut self.validator_registry_update_slot.hash_tree_root()); - result.append(&mut self.validator_registry_exit_count.hash_tree_root()); - result.append(&mut self.validator_registry_delta_chain_tip.hash_tree_root()); + result.append(&mut self.validator_registry_update_epoch.hash_tree_root()); result.append(&mut self.latest_randao_mixes.hash_tree_root()); - result.append(&mut self.latest_vdf_outputs.hash_tree_root()); result.append(&mut self.previous_epoch_start_shard.hash_tree_root()); result.append(&mut self.current_epoch_start_shard.hash_tree_root()); - result.append(&mut self.previous_epoch_calculation_slot.hash_tree_root()); - result.append(&mut self.current_epoch_calculation_slot.hash_tree_root()); + result.append(&mut self.previous_calculation_epoch.hash_tree_root()); + result.append(&mut self.current_calculation_epoch.hash_tree_root()); result.append(&mut self.previous_epoch_seed.hash_tree_root()); result.append(&mut self.current_epoch_seed.hash_tree_root()); - result.append(&mut self.custody_challenges.hash_tree_root()); - result.append(&mut self.previous_justified_slot.hash_tree_root()); - result.append(&mut self.justified_slot.hash_tree_root()); + result.append(&mut self.previous_justified_epoch.hash_tree_root()); + result.append(&mut self.justified_epoch.hash_tree_root()); result.append(&mut self.justification_bitfield.hash_tree_root()); - result.append(&mut self.finalized_slot.hash_tree_root()); + result.append(&mut self.finalized_epoch.hash_tree_root()); result.append(&mut self.latest_crosslinks.hash_tree_root()); result.append(&mut self.latest_block_roots.hash_tree_root()); result.append(&mut self.latest_penalized_balances.hash_tree_root()); @@ -1804,25 +1159,21 @@ impl TestRandom for BeaconState { Self { slot: <_>::random_for_test(rng), genesis_time: <_>::random_for_test(rng), - fork_data: <_>::random_for_test(rng), + fork: <_>::random_for_test(rng), validator_registry: <_>::random_for_test(rng), validator_balances: <_>::random_for_test(rng), - validator_registry_update_slot: <_>::random_for_test(rng), - validator_registry_exit_count: <_>::random_for_test(rng), - validator_registry_delta_chain_tip: <_>::random_for_test(rng), + validator_registry_update_epoch: <_>::random_for_test(rng), latest_randao_mixes: <_>::random_for_test(rng), - latest_vdf_outputs: <_>::random_for_test(rng), previous_epoch_start_shard: <_>::random_for_test(rng), current_epoch_start_shard: <_>::random_for_test(rng), - previous_epoch_calculation_slot: <_>::random_for_test(rng), - current_epoch_calculation_slot: <_>::random_for_test(rng), + previous_calculation_epoch: <_>::random_for_test(rng), + current_calculation_epoch: <_>::random_for_test(rng), previous_epoch_seed: <_>::random_for_test(rng), current_epoch_seed: <_>::random_for_test(rng), - custody_challenges: <_>::random_for_test(rng), - previous_justified_slot: <_>::random_for_test(rng), - justified_slot: <_>::random_for_test(rng), + previous_justified_epoch: <_>::random_for_test(rng), + justified_epoch: <_>::random_for_test(rng), justification_bitfield: <_>::random_for_test(rng), - finalized_slot: <_>::random_for_test(rng), + finalized_epoch: <_>::random_for_test(rng), latest_crosslinks: <_>::random_for_test(rng), latest_block_roots: <_>::random_for_test(rng), latest_penalized_balances: <_>::random_for_test(rng), diff --git a/eth2/types/src/crosslink.rs b/eth2/types/src/crosslink.rs index ca53baad7..3cb857ef4 100644 --- a/eth2/types/src/crosslink.rs +++ b/eth2/types/src/crosslink.rs @@ -1,12 +1,12 @@ use crate::test_utils::TestRandom; -use crate::{Hash256, Slot}; +use crate::{Epoch, Hash256}; use rand::RngCore; use serde_derive::Serialize; use ssz::{hash, Decodable, DecodeError, Encodable, SszStream, TreeHash}; -#[derive(Clone, Debug, PartialEq, Serialize)] +#[derive(Debug, Clone, PartialEq, Default, Serialize, Hash)] pub struct Crosslink { - pub slot: Slot, + pub epoch: Epoch, pub shard_block_root: Hash256, } @@ -14,7 +14,7 @@ impl Crosslink { /// Generates a new instance where `dynasty` and `hash` are both zero. pub fn zero() -> Self { Self { - slot: Slot::from(0_u64), + epoch: Epoch::new(0), shard_block_root: Hash256::zero(), } } @@ -22,19 +22,19 @@ impl Crosslink { impl Encodable for Crosslink { fn ssz_append(&self, s: &mut SszStream) { - s.append(&self.slot); + s.append(&self.epoch); s.append(&self.shard_block_root); } } impl Decodable for Crosslink { fn ssz_decode(bytes: &[u8], i: usize) -> Result<(Self, usize), DecodeError> { - let (slot, i) = <_>::ssz_decode(bytes, i)?; + let (epoch, i) = <_>::ssz_decode(bytes, i)?; let (shard_block_root, i) = <_>::ssz_decode(bytes, i)?; Ok(( Self { - slot, + epoch, shard_block_root, }, i, @@ -45,7 +45,7 @@ impl Decodable for Crosslink { impl TreeHash for Crosslink { fn hash_tree_root(&self) -> Vec { let mut result: Vec = vec![]; - result.append(&mut self.slot.hash_tree_root()); + result.append(&mut self.epoch.hash_tree_root()); result.append(&mut self.shard_block_root.hash_tree_root()); hash(&result) } @@ -54,7 +54,7 @@ impl TreeHash for Crosslink { impl TestRandom for Crosslink { fn random_for_test(rng: &mut T) -> Self { Self { - slot: <_>::random_for_test(rng), + epoch: <_>::random_for_test(rng), shard_block_root: <_>::random_for_test(rng), } } diff --git a/eth2/types/src/deposit.rs b/eth2/types/src/deposit.rs index 85b002101..62349cbc1 100644 --- a/eth2/types/src/deposit.rs +++ b/eth2/types/src/deposit.rs @@ -6,29 +6,29 @@ use ssz::{hash, Decodable, DecodeError, Encodable, SszStream, TreeHash}; #[derive(Debug, PartialEq, Clone, Serialize)] pub struct Deposit { - pub merkle_branch: Vec, - pub merkle_tree_index: u64, + pub branch: Vec, + pub index: u64, pub deposit_data: DepositData, } impl Encodable for Deposit { fn ssz_append(&self, s: &mut SszStream) { - s.append_vec(&self.merkle_branch); - s.append(&self.merkle_tree_index); + s.append_vec(&self.branch); + s.append(&self.index); s.append(&self.deposit_data); } } impl Decodable for Deposit { fn ssz_decode(bytes: &[u8], i: usize) -> Result<(Self, usize), DecodeError> { - let (merkle_branch, i) = <_>::ssz_decode(bytes, i)?; - let (merkle_tree_index, i) = <_>::ssz_decode(bytes, i)?; + let (branch, i) = <_>::ssz_decode(bytes, i)?; + let (index, i) = <_>::ssz_decode(bytes, i)?; let (deposit_data, i) = <_>::ssz_decode(bytes, i)?; Ok(( Self { - merkle_branch, - merkle_tree_index, + branch, + index, deposit_data, }, i, @@ -39,8 +39,8 @@ impl Decodable for Deposit { impl TreeHash for Deposit { fn hash_tree_root(&self) -> Vec { let mut result: Vec = vec![]; - result.append(&mut self.merkle_branch.hash_tree_root()); - result.append(&mut self.merkle_tree_index.hash_tree_root()); + result.append(&mut self.branch.hash_tree_root()); + result.append(&mut self.index.hash_tree_root()); result.append(&mut self.deposit_data.hash_tree_root()); hash(&result) } @@ -49,8 +49,8 @@ impl TreeHash for Deposit { impl TestRandom for Deposit { fn random_for_test(rng: &mut T) -> Self { Self { - merkle_branch: <_>::random_for_test(rng), - merkle_tree_index: <_>::random_for_test(rng), + branch: <_>::random_for_test(rng), + index: <_>::random_for_test(rng), deposit_data: <_>::random_for_test(rng), } } diff --git a/eth2/types/src/exit.rs b/eth2/types/src/exit.rs index 846d20494..cd7746919 100644 --- a/eth2/types/src/exit.rs +++ b/eth2/types/src/exit.rs @@ -1,4 +1,4 @@ -use crate::{test_utils::TestRandom, Slot}; +use crate::{test_utils::TestRandom, Epoch}; use bls::Signature; use rand::RngCore; use serde_derive::Serialize; @@ -6,14 +6,14 @@ use ssz::{hash, Decodable, DecodeError, Encodable, SszStream, TreeHash}; #[derive(Debug, PartialEq, Clone, Serialize)] pub struct Exit { - pub slot: Slot, - pub validator_index: u32, + pub epoch: Epoch, + pub validator_index: u64, pub signature: Signature, } impl Encodable for Exit { fn ssz_append(&self, s: &mut SszStream) { - s.append(&self.slot); + s.append(&self.epoch); s.append(&self.validator_index); s.append(&self.signature); } @@ -21,13 +21,13 @@ impl Encodable for Exit { impl Decodable for Exit { fn ssz_decode(bytes: &[u8], i: usize) -> Result<(Self, usize), DecodeError> { - let (slot, i) = <_>::ssz_decode(bytes, i)?; + let (epoch, i) = <_>::ssz_decode(bytes, i)?; let (validator_index, i) = <_>::ssz_decode(bytes, i)?; let (signature, i) = <_>::ssz_decode(bytes, i)?; Ok(( Self { - slot, + epoch, validator_index, signature, }, @@ -39,7 +39,7 @@ impl Decodable for Exit { impl TreeHash for Exit { fn hash_tree_root(&self) -> Vec { let mut result: Vec = vec![]; - result.append(&mut self.slot.hash_tree_root()); + result.append(&mut self.epoch.hash_tree_root()); result.append(&mut self.validator_index.hash_tree_root()); result.append(&mut self.signature.hash_tree_root()); hash(&result) @@ -49,7 +49,7 @@ impl TreeHash for Exit { impl TestRandom for Exit { fn random_for_test(rng: &mut T) -> Self { Self { - slot: <_>::random_for_test(rng), + epoch: <_>::random_for_test(rng), validator_index: <_>::random_for_test(rng), signature: <_>::random_for_test(rng), } diff --git a/eth2/types/src/fork.rs b/eth2/types/src/fork.rs index c147c03bc..1c96a34ac 100644 --- a/eth2/types/src/fork.rs +++ b/eth2/types/src/fork.rs @@ -1,34 +1,34 @@ -use crate::{test_utils::TestRandom, Slot}; +use crate::{test_utils::TestRandom, Epoch}; use rand::RngCore; use serde_derive::Serialize; use ssz::{hash, Decodable, DecodeError, Encodable, SszStream, TreeHash}; #[derive(Debug, Clone, PartialEq, Default, Serialize)] pub struct Fork { - pub pre_fork_version: u64, - pub post_fork_version: u64, - pub fork_slot: Slot, + pub previous_version: u64, + pub current_version: u64, + pub epoch: Epoch, } impl Encodable for Fork { fn ssz_append(&self, s: &mut SszStream) { - s.append(&self.pre_fork_version); - s.append(&self.post_fork_version); - s.append(&self.fork_slot); + s.append(&self.previous_version); + s.append(&self.current_version); + s.append(&self.epoch); } } impl Decodable for Fork { fn ssz_decode(bytes: &[u8], i: usize) -> Result<(Self, usize), DecodeError> { - let (pre_fork_version, i) = <_>::ssz_decode(bytes, i)?; - let (post_fork_version, i) = <_>::ssz_decode(bytes, i)?; - let (fork_slot, i) = <_>::ssz_decode(bytes, i)?; + let (previous_version, i) = <_>::ssz_decode(bytes, i)?; + let (current_version, i) = <_>::ssz_decode(bytes, i)?; + let (epoch, i) = <_>::ssz_decode(bytes, i)?; Ok(( Self { - pre_fork_version, - post_fork_version, - fork_slot, + previous_version, + current_version, + epoch, }, i, )) @@ -38,9 +38,9 @@ impl Decodable for Fork { impl TreeHash for Fork { fn hash_tree_root(&self) -> Vec { let mut result: Vec = vec![]; - result.append(&mut self.pre_fork_version.hash_tree_root()); - result.append(&mut self.post_fork_version.hash_tree_root()); - result.append(&mut self.fork_slot.hash_tree_root()); + result.append(&mut self.previous_version.hash_tree_root()); + result.append(&mut self.current_version.hash_tree_root()); + result.append(&mut self.epoch.hash_tree_root()); hash(&result) } } @@ -48,9 +48,9 @@ impl TreeHash for Fork { impl TestRandom for Fork { fn random_for_test(rng: &mut T) -> Self { Self { - pre_fork_version: <_>::random_for_test(rng), - post_fork_version: <_>::random_for_test(rng), - fork_slot: <_>::random_for_test(rng), + previous_version: <_>::random_for_test(rng), + current_version: <_>::random_for_test(rng), + epoch: <_>::random_for_test(rng), } } } diff --git a/eth2/types/src/lib.rs b/eth2/types/src/lib.rs index 519440beb..32e7f1fe6 100644 --- a/eth2/types/src/lib.rs +++ b/eth2/types/src/lib.rs @@ -3,6 +3,7 @@ pub mod test_utils; pub mod attestation; pub mod attestation_data; pub mod attestation_data_and_custody_bit; +pub mod attester_slashing; pub mod beacon_block; pub mod beacon_block_body; pub mod beacon_state; @@ -20,12 +21,11 @@ pub mod pending_attestation; pub mod proposal_signed_data; pub mod proposer_slashing; pub mod readers; -pub mod shard_committee; pub mod shard_reassignment_record; +pub mod slashable_attestation; pub mod slashable_vote_data; pub mod slot_epoch; pub mod spec; -pub mod special_record; pub mod validator; pub mod validator_registry; pub mod validator_registry_delta_block; @@ -36,6 +36,7 @@ use std::collections::HashMap; pub use crate::attestation::Attestation; pub use crate::attestation_data::AttestationData; pub use crate::attestation_data_and_custody_bit::AttestationDataAndCustodyBit; +pub use crate::attester_slashing::AttesterSlashing; pub use crate::beacon_block::BeaconBlock; pub use crate::beacon_block_body::BeaconBlockBody; pub use crate::beacon_state::BeaconState; @@ -52,11 +53,10 @@ pub use crate::free_attestation::FreeAttestation; pub use crate::pending_attestation::PendingAttestation; pub use crate::proposal_signed_data::ProposalSignedData; pub use crate::proposer_slashing::ProposerSlashing; -pub use crate::shard_committee::ShardCommittee; +pub use crate::slashable_attestation::SlashableAttestation; pub use crate::slashable_vote_data::SlashableVoteData; pub use crate::slot_epoch::{Epoch, Slot}; pub use crate::spec::ChainSpec; -pub use crate::special_record::{SpecialRecord, SpecialRecordKind}; pub use crate::validator::{StatusFlags as ValidatorStatusFlags, Validator}; pub use crate::validator_registry_delta_block::ValidatorRegistryDeltaBlock; diff --git a/eth2/types/src/pending_attestation.rs b/eth2/types/src/pending_attestation.rs index 7957ca93c..25ec109d7 100644 --- a/eth2/types/src/pending_attestation.rs +++ b/eth2/types/src/pending_attestation.rs @@ -6,34 +6,34 @@ use ssz::{hash, Decodable, DecodeError, Encodable, SszStream, TreeHash}; #[derive(Debug, Clone, PartialEq, Serialize)] pub struct PendingAttestation { - pub data: AttestationData, pub aggregation_bitfield: Bitfield, + pub data: AttestationData, pub custody_bitfield: Bitfield, - pub slot_included: Slot, + pub inclusion_slot: Slot, } impl Encodable for PendingAttestation { fn ssz_append(&self, s: &mut SszStream) { - s.append(&self.data); s.append(&self.aggregation_bitfield); + s.append(&self.data); s.append(&self.custody_bitfield); - s.append(&self.slot_included); + s.append(&self.inclusion_slot); } } impl Decodable for PendingAttestation { fn ssz_decode(bytes: &[u8], i: usize) -> Result<(Self, usize), DecodeError> { - let (data, i) = <_>::ssz_decode(bytes, i)?; let (aggregation_bitfield, i) = <_>::ssz_decode(bytes, i)?; + let (data, i) = <_>::ssz_decode(bytes, i)?; let (custody_bitfield, i) = <_>::ssz_decode(bytes, i)?; - let (slot_included, i) = <_>::ssz_decode(bytes, i)?; + let (inclusion_slot, i) = <_>::ssz_decode(bytes, i)?; Ok(( Self { data, aggregation_bitfield, custody_bitfield, - slot_included, + inclusion_slot, }, i, )) @@ -43,10 +43,10 @@ impl Decodable for PendingAttestation { impl TreeHash for PendingAttestation { fn hash_tree_root(&self) -> Vec { let mut result: Vec = vec![]; - result.append(&mut self.data.hash_tree_root()); result.append(&mut self.aggregation_bitfield.hash_tree_root()); + result.append(&mut self.data.hash_tree_root()); result.append(&mut self.custody_bitfield.hash_tree_root()); - result.append(&mut self.custody_bitfield.hash_tree_root()); + result.append(&mut self.inclusion_slot.hash_tree_root()); hash(&result) } } @@ -57,7 +57,7 @@ impl TestRandom for PendingAttestation { data: <_>::random_for_test(rng), aggregation_bitfield: <_>::random_for_test(rng), custody_bitfield: <_>::random_for_test(rng), - slot_included: <_>::random_for_test(rng), + inclusion_slot: <_>::random_for_test(rng), } } } diff --git a/eth2/types/src/proposer_slashing.rs b/eth2/types/src/proposer_slashing.rs index a82a37074..417d23dbc 100644 --- a/eth2/types/src/proposer_slashing.rs +++ b/eth2/types/src/proposer_slashing.rs @@ -7,7 +7,7 @@ use ssz::{hash, Decodable, DecodeError, Encodable, SszStream, TreeHash}; #[derive(Debug, PartialEq, Clone, Serialize)] pub struct ProposerSlashing { - pub proposer_index: u32, + pub proposer_index: u64, pub proposal_data_1: ProposalSignedData, pub proposal_signature_1: Signature, pub proposal_data_2: ProposalSignedData, diff --git a/eth2/types/src/shard_committee.rs b/eth2/types/src/shard_committee.rs deleted file mode 100644 index 3632cb0c1..000000000 --- a/eth2/types/src/shard_committee.rs +++ /dev/null @@ -1,74 +0,0 @@ -use crate::test_utils::TestRandom; -use rand::RngCore; -use serde_derive::Serialize; -use ssz::{hash, Decodable, DecodeError, Encodable, SszStream, TreeHash}; - -#[derive(Clone, Debug, PartialEq, Serialize)] -pub struct ShardCommittee { - pub shard: u64, - pub committee: Vec, -} - -impl Encodable for ShardCommittee { - fn ssz_append(&self, s: &mut SszStream) { - s.append(&self.shard); - s.append(&self.committee); - } -} - -impl Decodable for ShardCommittee { - fn ssz_decode(bytes: &[u8], i: usize) -> Result<(Self, usize), DecodeError> { - let (shard, i) = <_>::ssz_decode(bytes, i)?; - let (committee, i) = <_>::ssz_decode(bytes, i)?; - - Ok((Self { shard, committee }, i)) - } -} - -impl TreeHash for ShardCommittee { - fn hash_tree_root(&self) -> Vec { - let mut result: Vec = vec![]; - result.append(&mut self.shard.hash_tree_root()); - result.append(&mut self.committee.hash_tree_root()); - hash(&result) - } -} - -impl TestRandom for ShardCommittee { - fn random_for_test(rng: &mut T) -> Self { - Self { - shard: <_>::random_for_test(rng), - committee: <_>::random_for_test(rng), - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::test_utils::{SeedableRng, TestRandom, XorShiftRng}; - use ssz::ssz_encode; - - #[test] - pub fn test_ssz_round_trip() { - let mut rng = XorShiftRng::from_seed([42; 16]); - let original = ShardCommittee::random_for_test(&mut rng); - - let bytes = ssz_encode(&original); - let (decoded, _) = <_>::ssz_decode(&bytes, 0).unwrap(); - - assert_eq!(original, decoded); - } - - #[test] - pub fn test_hash_tree_root() { - let mut rng = XorShiftRng::from_seed([42; 16]); - let original = ShardCommittee::random_for_test(&mut rng); - - let result = original.hash_tree_root(); - - assert_eq!(result.len(), 32); - // TODO: Add further tests - // https://github.com/sigp/lighthouse/issues/170 - } -} diff --git a/eth2/types/src/slashable_attestation.rs b/eth2/types/src/slashable_attestation.rs new file mode 100644 index 000000000..6d83ad147 --- /dev/null +++ b/eth2/types/src/slashable_attestation.rs @@ -0,0 +1,92 @@ +use crate::{test_utils::TestRandom, AggregateSignature, AttestationData, Bitfield}; +use rand::RngCore; +use serde_derive::Serialize; +use ssz::{hash, Decodable, DecodeError, Encodable, SszStream, TreeHash}; + +#[derive(Debug, PartialEq, Clone, Serialize)] +pub struct SlashableAttestation { + pub validator_indices: Vec, + pub data: AttestationData, + pub custody_bitfield: Bitfield, + pub aggregate_signature: AggregateSignature, +} + +impl Encodable for SlashableAttestation { + fn ssz_append(&self, s: &mut SszStream) { + s.append_vec(&self.validator_indices); + s.append(&self.data); + s.append(&self.custody_bitfield); + s.append(&self.aggregate_signature); + } +} + +impl Decodable for SlashableAttestation { + fn ssz_decode(bytes: &[u8], i: usize) -> Result<(Self, usize), DecodeError> { + let (validator_indices, i) = <_>::ssz_decode(bytes, i)?; + let (data, i) = <_>::ssz_decode(bytes, i)?; + let (custody_bitfield, i) = <_>::ssz_decode(bytes, i)?; + let (aggregate_signature, i) = <_>::ssz_decode(bytes, i)?; + + Ok(( + SlashableAttestation { + validator_indices, + data, + custody_bitfield, + aggregate_signature, + }, + i, + )) + } +} + +impl TreeHash for SlashableAttestation { + fn hash_tree_root(&self) -> Vec { + let mut result: Vec = vec![]; + result.append(&mut self.validator_indices.hash_tree_root()); + result.append(&mut self.data.hash_tree_root()); + result.append(&mut self.custody_bitfield.hash_tree_root()); + result.append(&mut self.aggregate_signature.hash_tree_root()); + hash(&result) + } +} + +impl TestRandom for SlashableAttestation { + fn random_for_test(rng: &mut T) -> Self { + Self { + validator_indices: <_>::random_for_test(rng), + data: <_>::random_for_test(rng), + custody_bitfield: <_>::random_for_test(rng), + aggregate_signature: <_>::random_for_test(rng), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::test_utils::{SeedableRng, TestRandom, XorShiftRng}; + use ssz::ssz_encode; + + #[test] + pub fn test_ssz_round_trip() { + let mut rng = XorShiftRng::from_seed([42; 16]); + let original = SlashableAttestation::random_for_test(&mut rng); + + let bytes = ssz_encode(&original); + let (decoded, _) = <_>::ssz_decode(&bytes, 0).unwrap(); + + assert_eq!(original, decoded); + } + + #[test] + pub fn test_hash_tree_root() { + let mut rng = XorShiftRng::from_seed([42; 16]); + let original = SlashableAttestation::random_for_test(&mut rng); + + let result = original.hash_tree_root(); + + assert_eq!(result.len(), 32); + // TODO: Add further tests + // https://github.com/sigp/lighthouse/issues/170 + } +} diff --git a/eth2/types/src/slot_epoch.rs b/eth2/types/src/slot_epoch.rs index 3ca3377f3..36e0dc9cb 100644 --- a/eth2/types/src/slot_epoch.rs +++ b/eth2/types/src/slot_epoch.rs @@ -163,6 +163,10 @@ macro_rules! impl_math { *self - other.into() } + pub fn saturating_add>(&self, other: T) -> $type { + *self + other.into() + } + pub fn checked_div>(&self, rhs: T) -> Option<$type> { let rhs: $type = rhs.into(); if rhs == 0 { @@ -279,6 +283,10 @@ impl Epoch { Epoch(slot) } + pub fn max_value() -> Epoch { + Epoch(u64::max_value()) + } + pub fn start_slot(&self, epoch_length: u64) -> Slot { Slot::from(self.0.saturating_mul(epoch_length)) } @@ -527,6 +535,22 @@ mod tests { assert_saturating_sub(1, 2, 0); } + fn saturating_add() { + let assert_saturating_add = |a: u64, b: u64, result: u64| { + assert_eq!($type(a).saturating_add($type(b)), $type(result)); + }; + + assert_saturating_add(0, 1, 1); + assert_saturating_add(1, 0, 1); + assert_saturating_add(1, 2, 3); + assert_saturating_add(2, 1, 3); + assert_saturating_add(7, 7, 14); + + // Addition should be saturating. + assert_saturating_add(u64::max_value(), 1, u64::max_value()); + assert_saturating_add(u64::max_value(), u64::max_value(), u64::max_value()); + } + #[test] fn checked_div() { let assert_checked_div = |a: u64, b: u64, result: Option| { diff --git a/eth2/types/src/spec/foundation.rs b/eth2/types/src/spec/foundation.rs index ea45559a7..e9300fb95 100644 --- a/eth2/types/src/spec/foundation.rs +++ b/eth2/types/src/spec/foundation.rs @@ -1,10 +1,6 @@ -use super::ChainSpec; -use bls::{Keypair, PublicKey, SecretKey, Signature}; +use crate::{Address, ChainSpec, Epoch, Hash256, Signature, Slot}; -use crate::{Address, Eth1Data, Hash256, Slot, Validator}; - -/// The size of a validators deposit in GWei. -pub const DEPOSIT_GWEI: u64 = 32_000_000_000; +const GWEI: u64 = 1_000_000_000; impl ChainSpec { /// Returns a `ChainSpec` compatible with the specification from Ethereum Foundation. @@ -12,123 +8,96 @@ impl ChainSpec { /// Of course, the actual foundation specs are unknown at this point so these are just a rough /// estimate. pub fn foundation() -> Self { + let genesis_slot = Slot::new(2_u64.pow(19)); + let epoch_length = 64; + let genesis_epoch = genesis_slot.epoch(epoch_length); + Self { /* * Misc */ shard_count: 1_024, target_committee_size: 128, - ejection_balance: 16 * u64::pow(10, 9), max_balance_churn_quotient: 32, beacon_chain_shard_number: u64::max_value(), - max_casper_votes: 1_024, - latest_block_roots_length: 8_192, - latest_randao_mixes_length: 8_192, - latest_penalized_exit_length: 8_192, + max_indices_per_slashable_vote: 4_096, max_withdrawals_per_epoch: 4, + shuffle_round_count: 90, + /* * Deposit contract */ - deposit_contract_address: Address::from("TBD".as_bytes()), + deposit_contract_address: Address::zero(), deposit_contract_tree_depth: 32, - min_deposit: 1 * u64::pow(10, 9), - max_deposit: 32 * u64::pow(10, 9), + + /* + * Gwei values + */ + min_deposit_amount: u64::pow(2, 0) * GWEI, + max_deposit_amount: u64::pow(2, 5) * GWEI, + fork_choice_balance_increment: u64::pow(2, 0) * GWEI, + ejection_balance: u64::pow(2, 4) * GWEI, + /* * Initial Values */ genesis_fork_version: 0, - genesis_slot: Slot::from(0_u64), + genesis_slot: Slot::new(2_u64.pow(19)), + genesis_epoch, genesis_start_shard: 0, - far_future_slot: Slot::from(u64::max_value()), + far_future_epoch: Epoch::new(u64::max_value()), zero_hash: Hash256::zero(), empty_signature: Signature::empty_signature(), - bls_withdrawal_prefix_byte: 0x00, + bls_withdrawal_prefix_byte: 0, + /* * Time parameters */ slot_duration: 6, - min_attestation_inclusion_delay: 4, - epoch_length: 64, - seed_lookahead: 64, - entry_exit_delay: 256, - eth1_data_voting_period: 1_024, - min_validator_withdrawal_time: u64::pow(2, 14), + min_attestation_inclusion_delay: Slot::new(4), + epoch_length, + seed_lookahead: Epoch::new(1), + entry_exit_delay: Epoch::new(4), + eth1_data_voting_period: 16, + min_validator_withdrawal_epochs: Epoch::new(256), + + /* + * State list lengths + */ + latest_block_roots_length: 8_192, + latest_randao_mixes_length: 8_192, + latest_index_roots_length: 8_192, + latest_penalized_exit_length: 8_192, + /* * Reward and penalty quotients */ base_reward_quotient: 32, whistleblower_reward_quotient: 512, includer_reward_quotient: 8, - inactivity_penalty_quotient: u64::pow(2, 24), + inactivity_penalty_quotient: 16_777_216, + /* * Max operations per block */ max_proposer_slashings: 16, - max_casper_slashings: 16, + max_attester_slashings: 1, max_attestations: 128, max_deposits: 16, max_exits: 16, + /* - * Intialization parameters + * Signature domains */ - initial_validators: initial_validators_for_testing(), - initial_balances: initial_balances_for_testing(), - genesis_time: 1_544_672_897, - intial_eth1_data: Eth1Data { - deposit_root: Hash256::from("deposit_root".as_bytes()), - block_hash: Hash256::from("block_hash".as_bytes()), - }, + domain_deposit: 0, + domain_attestation: 1, + domain_proposal: 2, + domain_exit: 3, + domain_randao: 4, } } } -/// Generate a set of validator records to use with testing until the real chain starts. -fn initial_validators_for_testing() -> Vec { - // Some dummy private keys to start with. - let key_strings = vec![ - "jzjxxgjajfjrmgodszzsgqccmhnyvetcuxobhtynojtpdtbj", - "gpeehcjudxdijzhjgirfuhahmnjutlchjmoffxmimbdejakd", - "ntrrdwwebodokuwaclhoqreqyodngoyhurvesghjfxeswoaj", - "cibmzkqrzdgdlrvqaxinwpvyhcgjkeysrsjkqtkcxvznsvth", - "erqrfuahdwprsstkawggounxmihzhrvbhchcyiwtaypqcedr", - ]; - - let mut initial_validators = Vec::with_capacity(key_strings.len()); - for key_string in key_strings { - let keypair = { - let secret_key = match SecretKey::from_bytes(&key_string.as_bytes()) { - Ok(key) => key, - Err(_) => unreachable!(), // Keys are static and should not fail. - }; - let public_key = PublicKey::from_secret_key(&secret_key); - Keypair { - sk: secret_key, - pk: public_key, - } - }; - let validator = Validator { - pubkey: keypair.pk.clone(), - withdrawal_credentials: Hash256::zero(), - proposer_slots: 0, - activation_slot: Slot::max_value(), - exit_slot: Slot::max_value(), - withdrawal_slot: Slot::max_value(), - penalized_slot: Slot::max_value(), - exit_count: 0, - status_flags: None, - latest_custody_reseed_slot: Slot::from(0_u64), - penultimate_custody_reseed_slot: Slot::from(0_u64), - }; - initial_validators.push(validator); - } - - initial_validators -} - -fn initial_balances_for_testing() -> Vec { - vec![DEPOSIT_GWEI; 4] -} - #[cfg(test)] mod tests { use super::*; diff --git a/eth2/types/src/spec/mod.rs b/eth2/types/src/spec/mod.rs index 5007a26b0..a291b14b2 100644 --- a/eth2/types/src/spec/mod.rs +++ b/eth2/types/src/spec/mod.rs @@ -1,6 +1,6 @@ mod foundation; -use crate::{Address, Eth1Data, Hash256, Slot, Validator}; +use crate::{Address, Epoch, Hash256, Slot}; use bls::Signature; #[derive(PartialEq, Debug, Clone)] @@ -10,41 +10,57 @@ pub struct ChainSpec { */ pub shard_count: u64, pub target_committee_size: u64, - pub ejection_balance: u64, pub max_balance_churn_quotient: u64, pub beacon_chain_shard_number: u64, - pub max_casper_votes: u64, - pub latest_block_roots_length: u64, - pub latest_randao_mixes_length: u64, - pub latest_penalized_exit_length: u64, + pub max_indices_per_slashable_vote: u64, pub max_withdrawals_per_epoch: u64, + pub shuffle_round_count: u64, + /* * Deposit contract */ pub deposit_contract_address: Address, pub deposit_contract_tree_depth: u64, - pub min_deposit: u64, - pub max_deposit: u64, + + /* + * Gwei values + */ + pub min_deposit_amount: u64, + pub max_deposit_amount: u64, + pub fork_choice_balance_increment: u64, + pub ejection_balance: u64, + /* * Initial Values */ pub genesis_fork_version: u64, pub genesis_slot: Slot, + pub genesis_epoch: Epoch, pub genesis_start_shard: u64, - pub far_future_slot: Slot, + pub far_future_epoch: Epoch, pub zero_hash: Hash256, pub empty_signature: Signature, pub bls_withdrawal_prefix_byte: u8, + /* * Time parameters */ pub slot_duration: u64, - pub min_attestation_inclusion_delay: u64, + pub min_attestation_inclusion_delay: Slot, pub epoch_length: u64, - pub seed_lookahead: u64, - pub entry_exit_delay: u64, + pub seed_lookahead: Epoch, + pub entry_exit_delay: Epoch, pub eth1_data_voting_period: u64, - pub min_validator_withdrawal_time: u64, + pub min_validator_withdrawal_epochs: Epoch, + + /* + * State list lengths + */ + pub latest_block_roots_length: usize, + pub latest_randao_mixes_length: usize, + pub latest_index_roots_length: usize, + pub latest_penalized_exit_length: usize, + /* * Reward and penalty quotients */ @@ -52,19 +68,22 @@ pub struct ChainSpec { pub whistleblower_reward_quotient: u64, pub includer_reward_quotient: u64, pub inactivity_penalty_quotient: u64, + /* * Max operations per block */ pub max_proposer_slashings: u64, - pub max_casper_slashings: u64, + pub max_attester_slashings: u64, pub max_attestations: u64, pub max_deposits: u64, pub max_exits: u64, + /* - * Intialization parameters + * Signature domains */ - pub initial_validators: Vec, - pub initial_balances: Vec, - pub genesis_time: u64, - pub intial_eth1_data: Eth1Data, + pub domain_deposit: u64, + pub domain_attestation: u64, + pub domain_proposal: u64, + pub domain_exit: u64, + pub domain_randao: u64, } diff --git a/eth2/types/src/special_record.rs b/eth2/types/src/special_record.rs deleted file mode 100644 index 2ab6f2b5b..000000000 --- a/eth2/types/src/special_record.rs +++ /dev/null @@ -1,142 +0,0 @@ -use serde_derive::Serialize; -use ssz::{hash, Decodable, DecodeError, Encodable, SszStream, TreeHash}; - -/// The value of the "type" field of SpecialRecord. -/// -/// Note: this value must serialize to a u8 and therefore must not be greater than 255. -#[derive(Debug, PartialEq, Clone, Copy, Serialize)] -pub enum SpecialRecordKind { - Logout = 0, - CasperSlashing = 1, - RandaoChange = 2, -} - -/// The structure used in the `BeaconBlock.specials` field. -#[derive(Debug, PartialEq, Clone)] -pub struct SpecialRecord { - pub kind: u8, - pub data: Vec, -} - -impl SpecialRecord { - pub fn logout(data: &[u8]) -> Self { - Self { - kind: SpecialRecordKind::Logout as u8, - data: data.to_vec(), - } - } - - pub fn casper_slashing(data: &[u8]) -> Self { - Self { - kind: SpecialRecordKind::CasperSlashing as u8, - data: data.to_vec(), - } - } - - pub fn randao_change(data: &[u8]) -> Self { - Self { - kind: SpecialRecordKind::RandaoChange as u8, - data: data.to_vec(), - } - } - - /// Match `self.kind` to a `SpecialRecordKind`. - /// - /// Returns `None` if `self.kind` is an unknown value. - pub fn resolve_kind(&self) -> Option { - match self.kind { - x if x == SpecialRecordKind::Logout as u8 => Some(SpecialRecordKind::Logout), - x if x == SpecialRecordKind::CasperSlashing as u8 => { - Some(SpecialRecordKind::CasperSlashing) - } - x if x == SpecialRecordKind::RandaoChange as u8 => { - Some(SpecialRecordKind::RandaoChange) - } - _ => None, - } - } -} - -impl Encodable for SpecialRecord { - fn ssz_append(&self, s: &mut SszStream) { - s.append(&self.kind); - s.append_vec(&self.data); - } -} - -impl Decodable for SpecialRecord { - fn ssz_decode(bytes: &[u8], i: usize) -> Result<(Self, usize), DecodeError> { - let (kind, i) = u8::ssz_decode(bytes, i)?; - let (data, i) = Decodable::ssz_decode(bytes, i)?; - Ok((SpecialRecord { kind, data }, i)) - } -} - -impl TreeHash for SpecialRecord { - fn hash_tree_root(&self) -> Vec { - let mut result: Vec = vec![]; - result.append(&mut self.kind.hash_tree_root()); - result.append(&mut self.data.as_slice().hash_tree_root()); - hash(&result) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - pub fn test_special_record_ssz_encode() { - let s = SpecialRecord::logout(&vec![]); - let mut ssz_stream = SszStream::new(); - ssz_stream.append(&s); - let ssz = ssz_stream.drain(); - assert_eq!(ssz, vec![0, 0, 0, 0, 0]); - - let s = SpecialRecord::casper_slashing(&vec![]); - let mut ssz_stream = SszStream::new(); - ssz_stream.append(&s); - let ssz = ssz_stream.drain(); - assert_eq!(ssz, vec![1, 0, 0, 0, 0]); - - let s = SpecialRecord::randao_change(&vec![]); - let mut ssz_stream = SszStream::new(); - ssz_stream.append(&s); - let ssz = ssz_stream.drain(); - assert_eq!(ssz, vec![2, 0, 0, 0, 0]); - - let s = SpecialRecord::randao_change(&vec![42, 43, 44]); - let mut ssz_stream = SszStream::new(); - ssz_stream.append(&s); - let ssz = ssz_stream.drain(); - assert_eq!(ssz, vec![2, 0, 0, 0, 3, 42, 43, 44]); - } - - #[test] - pub fn test_special_record_ssz_encode_decode() { - let s = SpecialRecord::randao_change(&vec![13, 16, 14]); - let mut ssz_stream = SszStream::new(); - ssz_stream.append(&s); - let ssz = ssz_stream.drain(); - let (s_decoded, _) = SpecialRecord::ssz_decode(&ssz, 0).unwrap(); - assert_eq!(s, s_decoded); - } - - #[test] - pub fn test_special_record_resolve_kind() { - let s = SpecialRecord::logout(&vec![]); - assert_eq!(s.resolve_kind(), Some(SpecialRecordKind::Logout)); - - let s = SpecialRecord::casper_slashing(&vec![]); - assert_eq!(s.resolve_kind(), Some(SpecialRecordKind::CasperSlashing)); - - let s = SpecialRecord::randao_change(&vec![]); - assert_eq!(s.resolve_kind(), Some(SpecialRecordKind::RandaoChange)); - - let s = SpecialRecord { - kind: 88, - data: vec![], - }; - assert_eq!(s.resolve_kind(), None); - } -} diff --git a/eth2/types/src/validator.rs b/eth2/types/src/validator.rs index bfbd962b4..50eb65486 100644 --- a/eth2/types/src/validator.rs +++ b/eth2/types/src/validator.rs @@ -1,4 +1,4 @@ -use crate::{test_utils::TestRandom, Hash256, PublicKey, Slot}; +use crate::{test_utils::TestRandom, Epoch, Hash256, PublicKey}; use rand::RngCore; use serde_derive::Serialize; use ssz::{hash, Decodable, DecodeError, Encodable, SszStream, TreeHash}; @@ -46,21 +46,17 @@ fn status_flag_from_byte(flag: u8) -> Result, StatusFlagsDec pub struct Validator { pub pubkey: PublicKey, pub withdrawal_credentials: Hash256, - pub proposer_slots: u64, - pub activation_slot: Slot, - pub exit_slot: Slot, - pub withdrawal_slot: Slot, - pub penalized_slot: Slot, - pub exit_count: u64, + pub activation_epoch: Epoch, + pub exit_epoch: Epoch, + pub withdrawal_epoch: Epoch, + pub penalized_epoch: Epoch, pub status_flags: Option, - pub latest_custody_reseed_slot: Slot, - pub penultimate_custody_reseed_slot: Slot, } impl Validator { /// This predicate indicates if the validator represented by this record is considered "active" at `slot`. - pub fn is_active_at(&self, slot: Slot) -> bool { - self.activation_slot <= slot && slot < self.exit_slot + pub fn is_active_at(&self, slot: Epoch) -> bool { + self.activation_epoch <= slot && slot < self.exit_epoch } } @@ -70,15 +66,11 @@ impl Default for Validator { Self { pubkey: PublicKey::default(), withdrawal_credentials: Hash256::default(), - proposer_slots: 0, - activation_slot: Slot::from(std::u64::MAX), - exit_slot: Slot::from(std::u64::MAX), - withdrawal_slot: Slot::from(std::u64::MAX), - penalized_slot: Slot::from(std::u64::MAX), - exit_count: 0, + activation_epoch: Epoch::from(std::u64::MAX), + exit_epoch: Epoch::from(std::u64::MAX), + withdrawal_epoch: Epoch::from(std::u64::MAX), + penalized_epoch: Epoch::from(std::u64::MAX), status_flags: None, - latest_custody_reseed_slot: Slot::from(0_u64), // NOTE: is `GENESIS_SLOT` - penultimate_custody_reseed_slot: Slot::from(0_u64), // NOTE: is `GENESIS_SLOT` } } } @@ -94,15 +86,11 @@ impl Encodable for Validator { fn ssz_append(&self, s: &mut SszStream) { s.append(&self.pubkey); s.append(&self.withdrawal_credentials); - s.append(&self.proposer_slots); - s.append(&self.activation_slot); - s.append(&self.exit_slot); - s.append(&self.withdrawal_slot); - s.append(&self.penalized_slot); - s.append(&self.exit_count); + s.append(&self.activation_epoch); + s.append(&self.exit_epoch); + s.append(&self.withdrawal_epoch); + s.append(&self.penalized_epoch); s.append(&status_flag_to_byte(self.status_flags)); - s.append(&self.latest_custody_reseed_slot); - s.append(&self.penultimate_custody_reseed_slot); } } @@ -110,15 +98,11 @@ impl Decodable for Validator { fn ssz_decode(bytes: &[u8], i: usize) -> Result<(Self, usize), DecodeError> { let (pubkey, i) = <_>::ssz_decode(bytes, i)?; let (withdrawal_credentials, i) = <_>::ssz_decode(bytes, i)?; - let (proposer_slots, i) = <_>::ssz_decode(bytes, i)?; - let (activation_slot, i) = <_>::ssz_decode(bytes, i)?; - let (exit_slot, i) = <_>::ssz_decode(bytes, i)?; - let (withdrawal_slot, i) = <_>::ssz_decode(bytes, i)?; - let (penalized_slot, i) = <_>::ssz_decode(bytes, i)?; - let (exit_count, i) = <_>::ssz_decode(bytes, i)?; + let (activation_epoch, i) = <_>::ssz_decode(bytes, i)?; + let (exit_epoch, i) = <_>::ssz_decode(bytes, i)?; + let (withdrawal_epoch, i) = <_>::ssz_decode(bytes, i)?; + let (penalized_epoch, i) = <_>::ssz_decode(bytes, i)?; let (status_flags_byte, i): (u8, usize) = <_>::ssz_decode(bytes, i)?; - let (latest_custody_reseed_slot, i) = <_>::ssz_decode(bytes, i)?; - let (penultimate_custody_reseed_slot, i) = <_>::ssz_decode(bytes, i)?; let status_flags = status_flag_from_byte(status_flags_byte)?; @@ -126,15 +110,11 @@ impl Decodable for Validator { Self { pubkey, withdrawal_credentials, - proposer_slots, - activation_slot, - exit_slot, - withdrawal_slot, - penalized_slot, - exit_count, + activation_epoch, + exit_epoch, + withdrawal_epoch, + penalized_epoch, status_flags, - latest_custody_reseed_slot, - penultimate_custody_reseed_slot, }, i, )) @@ -146,15 +126,11 @@ impl TreeHash for Validator { let mut result: Vec = vec![]; result.append(&mut self.pubkey.hash_tree_root()); result.append(&mut self.withdrawal_credentials.hash_tree_root()); - result.append(&mut self.proposer_slots.hash_tree_root()); - result.append(&mut self.activation_slot.hash_tree_root()); - result.append(&mut self.exit_slot.hash_tree_root()); - result.append(&mut self.withdrawal_slot.hash_tree_root()); - result.append(&mut self.penalized_slot.hash_tree_root()); - result.append(&mut self.exit_count.hash_tree_root()); + result.append(&mut self.activation_epoch.hash_tree_root()); + result.append(&mut self.exit_epoch.hash_tree_root()); + result.append(&mut self.withdrawal_epoch.hash_tree_root()); + result.append(&mut self.penalized_epoch.hash_tree_root()); result.append(&mut (status_flag_to_byte(self.status_flags) as u64).hash_tree_root()); - result.append(&mut self.latest_custody_reseed_slot.hash_tree_root()); - result.append(&mut self.penultimate_custody_reseed_slot.hash_tree_root()); hash(&result) } } @@ -164,15 +140,11 @@ impl TestRandom for Validator { Self { pubkey: <_>::random_for_test(rng), withdrawal_credentials: <_>::random_for_test(rng), - proposer_slots: <_>::random_for_test(rng), - activation_slot: <_>::random_for_test(rng), - exit_slot: <_>::random_for_test(rng), - withdrawal_slot: <_>::random_for_test(rng), - penalized_slot: <_>::random_for_test(rng), - exit_count: <_>::random_for_test(rng), + activation_epoch: <_>::random_for_test(rng), + exit_epoch: <_>::random_for_test(rng), + withdrawal_epoch: <_>::random_for_test(rng), + penalized_epoch: <_>::random_for_test(rng), status_flags: Some(<_>::random_for_test(rng)), - latest_custody_reseed_slot: <_>::random_for_test(rng), - penultimate_custody_reseed_slot: <_>::random_for_test(rng), } } } @@ -199,17 +171,17 @@ mod tests { let mut rng = XorShiftRng::from_seed([42; 16]); let mut validator = Validator::random_for_test(&mut rng); - let activation_slot = u64::random_for_test(&mut rng); - let exit_slot = activation_slot + 234; + let activation_epoch = u64::random_for_test(&mut rng); + let exit_epoch = activation_epoch + 234; - validator.activation_slot = Slot::from(activation_slot); - validator.exit_slot = Slot::from(exit_slot); + validator.activation_epoch = Epoch::from(activation_epoch); + validator.exit_epoch = Epoch::from(exit_epoch); - for slot in (activation_slot - 100)..(exit_slot + 100) { - let slot = Slot::from(slot); - if slot < activation_slot { + for slot in (activation_epoch - 100)..(exit_epoch + 100) { + let slot = Epoch::from(slot); + if slot < activation_epoch { assert!(!validator.is_active_at(slot)); - } else if slot >= exit_slot { + } else if slot >= exit_epoch { assert!(!validator.is_active_at(slot)); } else { assert!(validator.is_active_at(slot)); diff --git a/eth2/types/src/validator_registry.rs b/eth2/types/src/validator_registry.rs index 236d512b6..20863dd72 100644 --- a/eth2/types/src/validator_registry.rs +++ b/eth2/types/src/validator_registry.rs @@ -1,15 +1,15 @@ /// Contains logic to manipulate a `&[Validator]`. /// For now, we avoid defining a newtype and just have flat functions here. use super::validator::*; -use crate::Slot; +use crate::Epoch; -/// Given an indexed sequence of `validators`, return the indices corresponding to validators that are active at `slot`. -pub fn get_active_validator_indices(validators: &[Validator], slot: Slot) -> Vec { +/// Given an indexed sequence of `validators`, return the indices corresponding to validators that are active at `epoch`. +pub fn get_active_validator_indices(validators: &[Validator], epoch: Epoch) -> Vec { validators .iter() .enumerate() .filter_map(|(index, validator)| { - if validator.is_active_at(slot) { + if validator.is_active_at(epoch) { Some(index) } else { None @@ -28,8 +28,8 @@ mod tests { let mut rng = XorShiftRng::from_seed([42; 16]); let validators = vec![]; - let some_slot = Slot::random_for_test(&mut rng); - let indices = get_active_validator_indices(&validators, some_slot); + let some_epoch = Epoch::random_for_test(&mut rng); + let indices = get_active_validator_indices(&validators, some_epoch); assert_eq!(indices, vec![]); } @@ -42,8 +42,8 @@ mod tests { validators.push(Validator::default()) } - let some_slot = Slot::random_for_test(&mut rng); - let indices = get_active_validator_indices(&validators, some_slot); + let some_epoch = Epoch::random_for_test(&mut rng); + let indices = get_active_validator_indices(&validators, some_epoch); assert_eq!(indices, vec![]); } @@ -51,7 +51,7 @@ mod tests { fn can_get_all_active_validator_indices() { let mut rng = XorShiftRng::from_seed([42; 16]); let count_validators = 10; - let some_slot = Slot::random_for_test(&mut rng); + let some_epoch = Epoch::random_for_test(&mut rng); let mut validators = (0..count_validators) .into_iter() @@ -61,8 +61,8 @@ mod tests { let activation_offset = u64::random_for_test(&mut rng); let exit_offset = u64::random_for_test(&mut rng); - validator.activation_slot = some_slot - activation_offset; - validator.exit_slot = some_slot + exit_offset; + validator.activation_epoch = some_epoch - activation_offset; + validator.exit_epoch = some_epoch + exit_offset; validator }) @@ -70,10 +70,10 @@ mod tests { // test boundary condition by ensuring that at least one validator in the list just activated if let Some(validator) = validators.get_mut(0) { - validator.activation_slot = some_slot; + validator.activation_epoch = some_epoch; } - let indices = get_active_validator_indices(&validators, some_slot); + let indices = get_active_validator_indices(&validators, some_epoch); assert_eq!( indices, (0..count_validators).into_iter().collect::>() @@ -82,31 +82,35 @@ mod tests { fn set_validators_to_default_entry_exit(validators: &mut [Validator]) { for validator in validators.iter_mut() { - validator.activation_slot = Slot::max_value(); - validator.exit_slot = Slot::max_value(); + validator.activation_epoch = Epoch::max_value(); + validator.exit_epoch = Epoch::max_value(); } } - // sets all `validators` to be active as of some slot prior to `slot`. returns the activation slot. - fn set_validators_to_activated(validators: &mut [Validator], slot: Slot) -> Slot { - let activation_slot = slot - 10; + // sets all `validators` to be active as of some epoch prior to `epoch`. returns the activation epoch. + fn set_validators_to_activated(validators: &mut [Validator], epoch: Epoch) -> Epoch { + let activation_epoch = epoch - 10; for validator in validators.iter_mut() { - validator.activation_slot = activation_slot; + validator.activation_epoch = activation_epoch; } - activation_slot + activation_epoch } - // sets all `validators` to be exited as of some slot before `slot`. - fn set_validators_to_exited(validators: &mut [Validator], slot: Slot, activation_slot: Slot) { - assert!(activation_slot < slot); - let mut exit_slot = activation_slot + 10; - while exit_slot >= slot { - exit_slot -= 1; + // sets all `validators` to be exited as of some epoch before `epoch`. + fn set_validators_to_exited( + validators: &mut [Validator], + epoch: Epoch, + activation_epoch: Epoch, + ) { + assert!(activation_epoch < epoch); + let mut exit_epoch = activation_epoch + 10; + while exit_epoch >= epoch { + exit_epoch -= 1; } - assert!(activation_slot < exit_slot && exit_slot < slot); + assert!(activation_epoch < exit_epoch && exit_epoch < epoch); for validator in validators.iter_mut() { - validator.exit_slot = exit_slot; + validator.exit_epoch = exit_epoch; } } @@ -115,18 +119,18 @@ mod tests { let mut rng = XorShiftRng::from_seed([42; 16]); const COUNT_PARTITIONS: usize = 3; const COUNT_VALIDATORS: usize = 3 * COUNT_PARTITIONS; - let some_slot: Slot = Slot::random_for_test(&mut rng); + let some_epoch: Epoch = Epoch::random_for_test(&mut rng); let mut validators = (0..COUNT_VALIDATORS) .into_iter() .map(|_| { let mut validator = Validator::default(); - let activation_offset = Slot::random_for_test(&mut rng); - let exit_offset = Slot::random_for_test(&mut rng); + let activation_offset = Epoch::random_for_test(&mut rng); + let exit_offset = Epoch::random_for_test(&mut rng); - validator.activation_slot = some_slot - activation_offset; - validator.exit_slot = some_slot + exit_offset; + validator.activation_epoch = some_epoch - activation_offset; + validator.exit_epoch = some_epoch + exit_offset; validator }) @@ -141,19 +145,19 @@ mod tests { } 1 => { // 2. activated, but not exited - set_validators_to_activated(chunk, some_slot); + set_validators_to_activated(chunk, some_epoch); // test boundary condition by ensuring that at least one validator in the list just activated if let Some(validator) = chunk.get_mut(0) { - validator.activation_slot = some_slot; + validator.activation_epoch = some_epoch; } } 2 => { // 3. exited - let activation_slot = set_validators_to_activated(chunk, some_slot); - set_validators_to_exited(chunk, some_slot, activation_slot); + let activation_epoch = set_validators_to_activated(chunk, some_epoch); + set_validators_to_exited(chunk, some_epoch, activation_epoch); // test boundary condition by ensuring that at least one validator in the list just exited if let Some(validator) = chunk.get_mut(0) { - validator.exit_slot = some_slot; + validator.exit_epoch = some_epoch; } } _ => unreachable!( @@ -162,7 +166,7 @@ mod tests { } } - let indices = get_active_validator_indices(&validators, some_slot); + let indices = get_active_validator_indices(&validators, some_epoch); assert_eq!(indices, vec![3, 4, 5]); } }