From a13dd0d871272884f2533a79c07bf8ee1c5d108c Mon Sep 17 00:00:00 2001 From: Paul Hauner Date: Mon, 11 Feb 2019 14:02:59 +1100 Subject: [PATCH 01/27] 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]); } } From d7f8c60f933419b9fcfbba61a98d4613e90053aa Mon Sep 17 00:00:00 2001 From: Paul Hauner Date: Mon, 11 Feb 2019 16:31:33 +1100 Subject: [PATCH 02/27] Fix un-annotated test in types crate --- eth2/types/src/slot_epoch.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/eth2/types/src/slot_epoch.rs b/eth2/types/src/slot_epoch.rs index 36e0dc9cb..a810bb102 100644 --- a/eth2/types/src/slot_epoch.rs +++ b/eth2/types/src/slot_epoch.rs @@ -535,6 +535,7 @@ mod tests { assert_saturating_sub(1, 2, 0); } + #[test] fn saturating_add() { let assert_saturating_add = |a: u64, b: u64, result: u64| { assert_eq!($type(a).saturating_add($type(b)), $type(result)); From 25ae0f64c242cac25cb101e5dee4e39305d16361 Mon Sep 17 00:00:00 2001 From: Paul Hauner Date: Mon, 11 Feb 2019 16:32:55 +1100 Subject: [PATCH 03/27] Tidy, fix & extend `BeaconState` impls. --- eth2/types/src/beacon_state.rs | 374 +++++++++++---------------------- 1 file changed, 125 insertions(+), 249 deletions(-) diff --git a/eth2/types/src/beacon_state.rs b/eth2/types/src/beacon_state.rs index d5e612397..03e313383 100644 --- a/eth2/types/src/beacon_state.rs +++ b/eth2/types/src/beacon_state.rs @@ -2,21 +2,17 @@ use crate::test_utils::TestRandom; use crate::{ validator::StatusFlags, validator_registry::get_active_validator_indices, AggregatePublicKey, Attestation, AttestationData, Bitfield, ChainSpec, Crosslink, Epoch, Eth1Data, Eth1DataVote, - Fork, Hash256, PendingAttestation, PublicKey, Signature, Slot, Validator, + Fork, Hash256, PendingAttestation, Slot, Validator, }; use bls::bls_verify_aggregate; use honey_badger_split::SplitExt; use rand::RngCore; use serde_derive::Serialize; use ssz::{hash, Decodable, DecodeError, Encodable, SszStream, TreeHash}; -use std::collections::HashMap; use std::ops::Range; use vec_shuffle::shuffle; // TODO: define elsehwere. -const DOMAIN_PROPOSAL: u64 = 2; -const DOMAIN_EXIT: u64 = 3; -const DOMAIN_RANDAO: u64 = 4; const PHASE_0_CUSTODY_BIT: bool = false; const DOMAIN_ATTESTATION: u64 = 1; @@ -27,61 +23,11 @@ pub enum Error { CommitteesError(CommitteesError), } -/* -#[derive(Debug, PartialEq)] -pub enum BlockProcessingError { - DBError(String), - StateAlreadyTransitioned, - PresentSlotIsNone, - UnableToDecodeBlock, - MissingParentState(Hash256), - InvalidParentState(Hash256), - MissingBeaconBlock(Hash256), - InvalidBeaconBlock(Hash256), - MissingParentBlock(Hash256), - NoBlockProducer, - StateSlotMismatch, - BadBlockSignature, - BadRandaoSignature, - MaxProposerSlashingsExceeded, - BadProposerSlashing, - MaxAttestationsExceeded, - InvalidAttestation(AttestationValidationError), - NoBlockRoot, - MaxDepositsExceeded, - MaxExitsExceeded, - BadExit, - BadCustodyReseeds, - BadCustodyChallenges, - BadCustodyResponses, - CommitteesError(CommitteesError), - SlotProcessingError(SlotProcessingError), -} -*/ - -/* -#[derive(Debug, PartialEq)] -pub enum EpochError { - UnableToDetermineProducer, - NoBlockRoots, - BaseRewardQuotientIsZero, - CommitteesError(CommitteesError), - AttestationParticipantsError(AttestationParticipantsError), - InclusionError(InclusionError), - WinningRootError(WinningRootError), -} -*/ - -#[derive(Debug, PartialEq)] -pub enum WinningRootError { - NoWinningRoot, - AttestationParticipantsError(AttestationParticipantsError), -} - #[derive(Debug, PartialEq)] pub enum CommitteesError { InvalidEpoch, InsufficientNumberOfValidators, + BadRandao, } #[derive(Debug, PartialEq)] @@ -119,14 +65,6 @@ pub enum AttestationValidationError { AttestationParticipantsError(AttestationParticipantsError), } -#[derive(Clone)] -pub struct WinningRoot { - pub shard_block_root: Hash256, - pub attesting_validator_indices: Vec, - pub total_balance: u64, - pub total_attesting_balance: u64, -} - macro_rules! ensure { ($condition: expr, $result: expr) => { if !$condition { @@ -146,9 +84,6 @@ macro_rules! safe_sub_assign { }; } -// Custody will not be added to the specs until Phase 1 (Sharding Phase) so dummy class used. -type CustodyChallenge = usize; - #[derive(Debug, PartialEq, Clone, Default, Serialize)] pub struct BeaconState { // Misc @@ -179,6 +114,7 @@ pub struct BeaconState { // Recent state pub latest_crosslinks: Vec, pub latest_block_roots: Vec, + pub latest_index_roots: Vec, pub latest_penalized_balances: Vec, pub latest_attestations: Vec, pub batched_block_roots: Vec, @@ -217,7 +153,7 @@ impl BeaconState { /// /// TODO: this should probably be a method on `ChainSpec`. /// - /// Spec v0.1 + /// Spec v0.2.0 pub fn get_epoch_committee_count( &self, active_validator_count: usize, @@ -258,7 +194,7 @@ impl BeaconState { /// Return the number of committees in the previous epoch. /// - /// Spec v0.1 + /// Spec v0.2.0 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); @@ -267,7 +203,7 @@ impl BeaconState { /// Return the number of committees in the current epoch. /// - /// Spec v0.1 + /// Spec v0.2.0 pub fn get_current_epoch_committee_count(&self, spec: &ChainSpec) -> u64 { let current_active_validators = get_active_validator_indices(&self.validator_registry, self.current_calculation_epoch); @@ -276,19 +212,45 @@ impl BeaconState { /// Return the number of committees in the next epoch. /// - /// Spec v0.1 + /// Spec v0.2.0 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); + get_active_validator_indices(&self.validator_registry, self.next_epoch(spec)); self.get_epoch_committee_count(current_active_validators.len(), spec) } + pub fn get_active_index_root(&self, epoch: Epoch, spec: &ChainSpec) -> Option { + let current_epoch = self.current_epoch(spec); + + let earliest_index_root = current_epoch - Epoch::from(spec.latest_index_roots_length) + + Epoch::from(spec.entry_exit_delay) + + 1; + let latest_index_root = current_epoch + spec.entry_exit_delay; + + if (epoch <= earliest_index_root) & (epoch >= latest_index_root) { + Some(self.latest_index_roots[epoch.as_usize() % spec.latest_index_roots_length]) + } else { + None + } + } + + /// Generate a seed for the given ``epoch``. + /// + /// Spec v0.2.0 + pub fn generate_seed(&self, epoch: Epoch, spec: &ChainSpec) -> Option { + let mut input = self.get_randao_mix(epoch, spec)?.to_vec(); + input.append(&mut self.get_active_index_root(epoch, spec)?.to_vec()); + // TODO: ensure `Hash256::from(u64)` == `int_to_bytes32`. + input.append(&mut Hash256::from(epoch.as_u64()).to_vec()); + Some(Hash256::from(&hash(&input[..])[..])) + } + /// 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 + /// Spec v0.2.0 pub fn get_crosslink_committees_at_slot( &self, slot: Slot, @@ -302,36 +264,61 @@ impl BeaconState { } else { current_epoch.saturating_sub(1_u64) }; - let next_epoch = current_epoch + 1; + let next_epoch = self.next_epoch(spec); - ensure!( - (previous_epoch <= epoch) & (epoch < next_epoch), - CommitteesError::InvalidEpoch - ); + let (committees_per_epoch, seed, shuffling_epoch, shuffling_start_shard) = + if epoch == previous_epoch { + ( + self.get_previous_epoch_committee_count(spec), + self.previous_epoch_seed, + self.previous_calculation_epoch, + self.previous_epoch_start_shard, + ) + } else if epoch == current_epoch { + ( + self.get_current_epoch_committee_count(spec), + self.current_epoch_seed, + self.current_calculation_epoch, + self.current_epoch_start_shard, + ) + } else if epoch == next_epoch { + let current_committees_per_epoch = self.get_current_epoch_committee_count(spec); + let epochs_since_last_registry_update = + current_epoch - self.validator_registry_update_epoch; + let (seed, shuffling_start_shard) = if registry_change { + let next_seed = self + .generate_seed(next_epoch, spec) + .ok_or_else(|| CommitteesError::BadRandao)?; + ( + next_seed, + (self.current_epoch_start_shard + current_committees_per_epoch) + % spec.shard_count, + ) + } else if (epochs_since_last_registry_update > 1) + & epochs_since_last_registry_update.is_power_of_two() + { + let next_seed = self + .generate_seed(next_epoch, spec) + .ok_or_else(|| CommitteesError::BadRandao)?; + (next_seed, self.current_epoch_start_shard) + } else { + (self.current_epoch_seed, self.current_epoch_start_shard) + }; + ( + self.get_next_epoch_committee_count(spec), + seed, + next_epoch, + shuffling_start_shard, + ) + } else { + panic!("Epoch out-of-bounds.") + }; + let shuffling = self.get_shuffling(seed, shuffling_epoch, spec); 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(spec); - let shuffling = self.get_shuffling( - self.previous_epoch_seed, - 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(spec); - let shuffling = self.get_shuffling( - self.current_epoch_seed, - self.current_calculation_epoch, - spec, - ); - let slot_start_shard = - (self.current_epoch_start_shard + committees_per_slot * offset) % spec.shard_count; - (committees_per_slot, shuffling, slot_start_shard) - }; + let committees_per_slot = committees_per_epoch / spec.epoch_length; + let slot_start_shard = + (shuffling_start_shard + committees_per_slot * offset) % spec.shard_count; let mut crosslinks_at_slot = vec![]; for i in 0..committees_per_slot { @@ -361,12 +348,19 @@ impl BeaconState { Ok(result) } + /// An entry or exit triggered in the ``epoch`` given by the input takes effect at + /// the epoch given by the output. + /// + /// Spec v0.2.0 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`. + /// /// If the state does not contain an index for a beacon proposer at the requested `slot`, then `None` is returned. + /// + /// Spec v0.2.0 pub fn get_beacon_proposer_index( &self, slot: Slot, @@ -380,7 +374,7 @@ impl BeaconState { let index = (slot.as_usize()) .checked_rem(first_committee.len()) .ok_or(CommitteesError::InsufficientNumberOfValidators)?; - // NOTE: next index will not panic as we have already returned if this is the case + // NOTE: next index will not panic as we have already returned if this is the case. Ok(first_committee[index]) }) } @@ -388,7 +382,7 @@ impl BeaconState { /// 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) { + pub 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, current_epoch); @@ -444,7 +438,7 @@ impl BeaconState { /// 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> { + pub fn get_randao_mix(&self, epoch: Epoch, spec: &ChainSpec) -> Option<&Hash256> { self.latest_randao_mixes .get(epoch.as_usize() % spec.latest_randao_mixes_length) } @@ -452,7 +446,7 @@ impl BeaconState { /// Update validator registry, activating/exiting validators if possible. /// /// Spec v0.2.0 - fn update_validator_registry(&mut self, spec: &ChainSpec) { + pub 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, current_epoch); @@ -500,7 +494,12 @@ impl BeaconState { /// Activate the validator of the given ``index``. /// /// Spec v0.2.0 - fn activate_validator(&mut self, validator_index: usize, is_genesis: bool, spec: &ChainSpec) { + pub 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 { @@ -513,7 +512,7 @@ impl BeaconState { /// 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) { + pub fn initiate_validator_exit(&mut self, validator_index: usize) { // TODO: the spec does an `|=` here, ensure this isn't buggy. self.validator_registry[validator_index].status_flags = Some(StatusFlags::InitiatedExit); } @@ -569,7 +568,7 @@ impl BeaconState { /// Initiate an exit for the validator of the given `index`. /// /// Spec v0.2.0 - fn prepare_validator_for_withdrawal(&mut self, validator_index: usize) { + pub 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); } @@ -578,7 +577,7 @@ impl BeaconState { /// ``EJECTION_BALANCE``. /// /// Spec v0.2.0 - fn process_ejections(&mut self, spec: &ChainSpec) { + pub fn process_ejections(&mut self, spec: &ChainSpec) { for validator_index in get_active_validator_indices(&self.validator_registry, self.current_epoch(spec)) { @@ -593,7 +592,7 @@ impl BeaconState { /// Note: this is defined "inline" in the spec, not as a helper function. /// /// Spec v0.2.0 - fn inactivity_penalty( + pub fn inactivity_penalty( &self, validator_index: usize, epochs_since_finality: u64, @@ -611,7 +610,7 @@ impl BeaconState { /// Note: In the spec this is defined "inline", not as a helper function. /// /// Spec v0.2.0 - fn inclusion_distance( + pub fn inclusion_distance( &self, attestations: &[&PendingAttestation], validator_index: usize, @@ -627,7 +626,7 @@ impl BeaconState { /// Note: In the spec this is defined "inline", not as a helper function. /// /// Spec v0.2.0 - fn inclusion_slot( + pub fn inclusion_slot( &self, attestations: &[&PendingAttestation], validator_index: usize, @@ -675,7 +674,7 @@ impl BeaconState { /// Note: In the spec this is defined "inline", not as a helper function. /// /// Spec v0.2.0 - fn base_reward( + pub fn base_reward( &self, validator_index: usize, base_reward_quotient: u64, @@ -711,86 +710,6 @@ impl BeaconState { .get(slot.as_usize() % spec.latest_block_roots_length) } - pub(crate) fn winning_root( - &self, - shard: u64, - current_epoch_attestations: &[&PendingAttestation], - previous_epoch_attestations: &[&PendingAttestation], - spec: &ChainSpec, - ) -> Result { - let mut attestations = current_epoch_attestations.to_vec(); - attestations.append(&mut previous_epoch_attestations.to_vec()); - - let mut candidates: HashMap = HashMap::new(); - - let mut highest_seen_balance = 0; - - for a in &attestations { - if a.data.shard != shard { - continue; - } - - let shard_block_root = &a.data.shard_block_root; - - if candidates.contains_key(shard_block_root) { - continue; - } - - // TODO: `cargo fmt` makes this rather ugly; tidy up. - let attesting_validator_indices = attestations.iter().try_fold::<_, _, Result< - _, - AttestationParticipantsError, - >>( - vec![], - |mut acc, a| { - if (a.data.shard == shard) && (a.data.shard_block_root == *shard_block_root) { - acc.append(&mut self.get_attestation_participants( - &a.data, - &a.aggregation_bitfield, - spec, - )?); - } - Ok(acc) - }, - )?; - - let total_balance: u64 = attesting_validator_indices - .iter() - .fold(0, |acc, i| acc + self.get_effective_balance(*i, spec)); - - let total_attesting_balance: u64 = attesting_validator_indices - .iter() - .fold(0, |acc, i| acc + self.get_effective_balance(*i, spec)); - - if total_attesting_balance > highest_seen_balance { - highest_seen_balance = total_attesting_balance; - } - - let candidate_root = WinningRoot { - shard_block_root: shard_block_root.clone(), - attesting_validator_indices, - total_attesting_balance, - total_balance, - }; - - candidates.insert(*shard_block_root, candidate_root); - } - - Ok(candidates - .iter() - .filter_map(|(_hash, candidate)| { - if candidate.total_attesting_balance == highest_seen_balance { - Some(candidate) - } else { - None - } - }) - .min_by_key(|candidate| candidate.shard_block_root) - .ok_or_else(|| WinningRootError::NoWinningRoot)? - // TODO: avoid clone. - .clone()) - } - pub fn get_attestation_participants_union( &self, attestations: &[&PendingAttestation], @@ -812,11 +731,16 @@ impl BeaconState { Ok(all_participants) } - // TODO: analyse for efficiency improvments. This implementation is naive. + /// Return the participant indices at for the ``attestation_data`` and ``bitfield``. + /// + /// In effect, this converts the "committee indices" on the bitfield into "validator indices" + /// for self.validator_registy. + /// + /// Spec v0.2.0 pub fn get_attestation_participants( &self, attestation_data: &AttestationData, - aggregation_bitfield: &Bitfield, + bitfield: &Bitfield, spec: &ChainSpec, ) -> Result, AttestationParticipantsError> { let crosslink_committees = @@ -829,13 +753,12 @@ impl BeaconState { let (crosslink_committee, _shard) = &crosslink_committees[committee_index]; /* - * TODO: that bitfield length is valid. - * + * TODO: verify bitfield length is valid. */ let mut participants = vec![]; for (i, validator_index) in crosslink_committee.iter().enumerate() { - if aggregation_bitfield.get(i).unwrap() { + if bitfield.get(i).unwrap() { participants.push(*validator_index); } } @@ -939,32 +862,17 @@ impl BeaconState { } } -fn merkle_root(_input: &[Hash256]) -> Hash256 { - Hash256::zero() -} - fn get_domain(_fork: &Fork, _epoch: Epoch, _domain_type: u64) -> u64 { // TODO: stubbed out. 0 } -fn bls_verify(pubkey: &PublicKey, message: &[u8], signature: &Signature, _domain: u64) -> bool { - // TODO: add domain - signature.verify(message, pubkey) -} - impl From for AttestationValidationError { fn from(e: AttestationParticipantsError) -> AttestationValidationError { AttestationValidationError::AttestationParticipantsError(e) } } -impl From for WinningRootError { - fn from(e: AttestationParticipantsError) -> WinningRootError { - WinningRootError::AttestationParticipantsError(e) - } -} - impl From for AttestationParticipantsError { fn from(e: CommitteesError) -> AttestationParticipantsError { AttestationParticipantsError::CommitteesError(e) @@ -972,23 +880,6 @@ impl From for AttestationParticipantsError { } /* -impl From for BlockProcessingError { - fn from(e: AttestationValidationError) -> BlockProcessingError { - BlockProcessingError::InvalidAttestation(e) - } -} - -impl From for BlockProcessingError { - fn from(e: CommitteesError) -> BlockProcessingError { - BlockProcessingError::CommitteesError(e) - } -} - -impl From for BlockProcessingError { - fn from(e: SlotProcessingError) -> BlockProcessingError { - BlockProcessingError::SlotProcessingError(e) - } -} impl From for SlotProcessingError { fn from(e: CommitteesError) -> SlotProcessingError { @@ -1009,26 +900,6 @@ impl From for InclusionError { } } -/* -impl From for EpochError { - fn from(e: InclusionError) -> EpochError { - EpochError::InclusionError(e) - } -} - -impl From for EpochError { - fn from(e: CommitteesError) -> EpochError { - EpochError::CommitteesError(e) - } -} - -impl From for EpochError { - fn from(e: AttestationParticipantsError) -> EpochError { - EpochError::AttestationParticipantsError(e) - } -} -*/ - impl From for Error { fn from(e: CommitteesError) -> Error { Error::CommitteesError(e) @@ -1056,6 +927,7 @@ impl Encodable for BeaconState { s.append(&self.finalized_epoch); s.append(&self.latest_crosslinks); s.append(&self.latest_block_roots); + s.append(&self.latest_index_roots); s.append(&self.latest_penalized_balances); s.append(&self.latest_attestations); s.append(&self.batched_block_roots); @@ -1085,6 +957,7 @@ impl Decodable for BeaconState { 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_index_roots, i) = <_>::ssz_decode(bytes, i)?; let (latest_penalized_balances, i) = <_>::ssz_decode(bytes, i)?; let (latest_attestations, i) = <_>::ssz_decode(bytes, i)?; let (batched_block_roots, i) = <_>::ssz_decode(bytes, i)?; @@ -1112,6 +985,7 @@ impl Decodable for BeaconState { finalized_epoch, latest_crosslinks, latest_block_roots, + latest_index_roots, latest_penalized_balances, latest_attestations, batched_block_roots, @@ -1145,6 +1019,7 @@ impl TreeHash for BeaconState { 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_index_roots.hash_tree_root()); result.append(&mut self.latest_penalized_balances.hash_tree_root()); result.append(&mut self.latest_attestations.hash_tree_root()); result.append(&mut self.batched_block_roots.hash_tree_root()); @@ -1176,6 +1051,7 @@ impl TestRandom for BeaconState { finalized_epoch: <_>::random_for_test(rng), latest_crosslinks: <_>::random_for_test(rng), latest_block_roots: <_>::random_for_test(rng), + latest_index_roots: <_>::random_for_test(rng), latest_penalized_balances: <_>::random_for_test(rng), latest_attestations: <_>::random_for_test(rng), batched_block_roots: <_>::random_for_test(rng), From 75a9e0f3de1b6e409762e299b7f316270f7d3ad0 Mon Sep 17 00:00:00 2001 From: Paul Hauner Date: Mon, 11 Feb 2019 17:24:32 +1100 Subject: [PATCH 04/27] Add `verify_signature()` to `Attestation` --- eth2/types/src/attestation.rs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/eth2/types/src/attestation.rs b/eth2/types/src/attestation.rs index 7938ae657..afffc2e2f 100644 --- a/eth2/types/src/attestation.rs +++ b/eth2/types/src/attestation.rs @@ -1,6 +1,6 @@ -use super::{AttestationData, Bitfield, Hash256}; +use super::{AggregatePublicKey, AggregateSignature, AttestationData, Bitfield, Hash256}; use crate::test_utils::TestRandom; -use bls::AggregateSignature; +use bls::bls_verify_aggregate; use rand::RngCore; use serde_derive::Serialize; use ssz::{hash, Decodable, DecodeError, Encodable, SszStream, TreeHash}; @@ -21,6 +21,17 @@ impl Attestation { pub fn signable_message(&self, custody_bit: bool) -> Vec { self.data.signable_message(custody_bit) } + + pub fn verify_signature( + &self, + group_public_key: &AggregatePublicKey, + custody_bit: bool, + // TODO: use domain. + _domain: u64, + ) -> bool { + self.aggregate_signature + .verify(&self.signable_message(custody_bit), group_public_key) + } } impl Encodable for Attestation { From 03e84a63ecb366ba48284050629ef4382a743176 Mon Sep 17 00:00:00 2001 From: Paul Hauner Date: Mon, 11 Feb 2019 17:24:56 +1100 Subject: [PATCH 05/27] Tidy `BeaconState` --- eth2/types/src/beacon_state.rs | 123 ++++++------------------------ eth2/types/src/spec/foundation.rs | 4 +- eth2/types/src/spec/mod.rs | 5 +- 3 files changed, 29 insertions(+), 103 deletions(-) diff --git a/eth2/types/src/beacon_state.rs b/eth2/types/src/beacon_state.rs index 03e313383..d442eb367 100644 --- a/eth2/types/src/beacon_state.rs +++ b/eth2/types/src/beacon_state.rs @@ -125,26 +125,44 @@ pub struct BeaconState { } impl BeaconState { + /// Return the tree hash root for this `BeaconState`. + /// + /// Spec v0.2.0 pub fn canonical_root(&self) -> Hash256 { Hash256::from(&self.hash_tree_root()[..]) } + /// The epoch corresponding to `self.slot`. + /// + /// Spec v0.2.0 pub fn current_epoch(&self, spec: &ChainSpec) -> Epoch { self.slot.epoch(spec.epoch_length) } + /// The epoch prior to `self.current_epoch()`. + /// + /// Spec v0.2.0 pub fn previous_epoch(&self, spec: &ChainSpec) -> Epoch { self.current_epoch(spec).saturating_sub(1_u64) } + /// The epoch following `self.current_epoch()`. + /// + /// Spec v0.2.0 pub fn next_epoch(&self, spec: &ChainSpec) -> Epoch { self.current_epoch(spec).saturating_add(1_u64) } + /// The first slot of the epoch corresponding to `self.slot`. + /// + /// Spec v0.2.0 pub fn current_epoch_start_slot(&self, spec: &ChainSpec) -> Slot { self.current_epoch(spec).start_slot(spec.epoch_length) } + /// The first slot of the epoch preceeding the one corresponding to `self.slot`. + /// + /// Spec v0.2.0 pub fn previous_epoch_start_slot(&self, spec: &ChainSpec) -> Slot { self.previous_epoch(spec).start_slot(spec.epoch_length) } @@ -331,6 +349,10 @@ impl BeaconState { Ok(crosslinks_at_slot) } + /// Returns the `slot`, `shard` and `committee_index` for which a validator must produce an + /// attestation. + /// + /// Spec v0.2.0 pub fn attestation_slot_and_shard_for_validator( &self, validator_index: usize, @@ -764,107 +786,6 @@ impl BeaconState { } Ok(participants) } - - pub fn validate_attestation( - &self, - attestation: &Attestation, - spec: &ChainSpec, - ) -> Result<(), AttestationValidationError> { - self.validate_attestation_signature_optional(attestation, spec, true) - } - - pub fn validate_attestation_without_signature( - &self, - attestation: &Attestation, - spec: &ChainSpec, - ) -> Result<(), AttestationValidationError> { - self.validate_attestation_signature_optional(attestation, spec, false) - } - - fn validate_attestation_signature_optional( - &self, - attestation: &Attestation, - spec: &ChainSpec, - verify_signature: bool, - ) -> Result<(), AttestationValidationError> { - ensure!( - attestation.data.slot + spec.min_attestation_inclusion_delay <= self.slot, - AttestationValidationError::IncludedTooEarly - ); - ensure!( - attestation.data.slot + spec.epoch_length >= self.slot, - AttestationValidationError::IncludedTooLate - ); - if attestation.data.slot >= self.current_epoch_start_slot(spec) { - ensure!( - attestation.data.justified_epoch == self.justified_epoch, - AttestationValidationError::WrongJustifiedSlot - ); - } else { - ensure!( - attestation.data.justified_epoch == self.previous_justified_epoch, - AttestationValidationError::WrongJustifiedSlot - ); - } - ensure!( - attestation.data.justified_block_root - == *self - .get_block_root( - attestation - .data - .justified_epoch - .start_slot(spec.epoch_length), - &spec - ) - .ok_or(AttestationValidationError::NoBlockRoot)?, - AttestationValidationError::WrongJustifiedRoot - ); - ensure!( - (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 { - let participants = self.get_attestation_participants( - &attestation.data, - &attestation.aggregation_bitfield, - spec, - )?; - let mut group_public_key = AggregatePublicKey::new(); - for participant in participants { - group_public_key.add( - self.validator_registry[participant as usize] - .pubkey - .as_raw(), - ) - } - ensure!( - bls_verify_aggregate( - &group_public_key, - &attestation.signable_message(PHASE_0_CUSTODY_BIT), - &attestation.aggregate_signature, - get_domain( - &self.fork, - attestation.data.slot.epoch(spec.epoch_length), - DOMAIN_ATTESTATION - ) - ), - AttestationValidationError::BadSignature - ); - } - ensure!( - attestation.data.shard_block_root == spec.zero_hash, - AttestationValidationError::ShardBlockRootNotZero - ); - Ok(()) - } -} - -fn get_domain(_fork: &Fork, _epoch: Epoch, _domain_type: u64) -> u64 { - // TODO: stubbed out. - 0 } impl From for AttestationValidationError { diff --git a/eth2/types/src/spec/foundation.rs b/eth2/types/src/spec/foundation.rs index e9300fb95..0d8f34f0d 100644 --- a/eth2/types/src/spec/foundation.rs +++ b/eth2/types/src/spec/foundation.rs @@ -7,6 +7,8 @@ impl ChainSpec { /// /// Of course, the actual foundation specs are unknown at this point so these are just a rough /// estimate. + /// + /// Spec v0.2.0 pub fn foundation() -> Self { let genesis_slot = Slot::new(2_u64.pow(19)); let epoch_length = 64; @@ -57,7 +59,7 @@ impl ChainSpec { min_attestation_inclusion_delay: Slot::new(4), epoch_length, seed_lookahead: Epoch::new(1), - entry_exit_delay: Epoch::new(4), + entry_exit_delay: 4, eth1_data_voting_period: 16, min_validator_withdrawal_epochs: Epoch::new(256), diff --git a/eth2/types/src/spec/mod.rs b/eth2/types/src/spec/mod.rs index a291b14b2..ad0d78012 100644 --- a/eth2/types/src/spec/mod.rs +++ b/eth2/types/src/spec/mod.rs @@ -3,6 +3,9 @@ mod foundation; use crate::{Address, Epoch, Hash256, Slot}; use bls::Signature; +/// Holds all the "constants" for a BeaconChain. +/// +/// Spec v0.2.0 #[derive(PartialEq, Debug, Clone)] pub struct ChainSpec { /* @@ -49,7 +52,7 @@ pub struct ChainSpec { pub min_attestation_inclusion_delay: Slot, pub epoch_length: u64, pub seed_lookahead: Epoch, - pub entry_exit_delay: Epoch, + pub entry_exit_delay: u64, pub eth1_data_voting_period: u64, pub min_validator_withdrawal_epochs: Epoch, From c1f8c85ce9e29d71de92de567181e284166a25e1 Mon Sep 17 00:00:00 2001 From: Paul Hauner Date: Mon, 11 Feb 2019 17:25:23 +1100 Subject: [PATCH 06/27] Add un-finished, uncompiling `state_processing` --- Cargo.toml | 1 + eth2/state_processing/Cargo.toml | 12 + .../state_processing/src/block_processable.rs | 400 +++++++++++ .../state_processing/src/epoch_processable.rs | 670 ++++++++++++++++++ eth2/state_processing/src/lib.rs | 7 + eth2/state_processing/src/slot_processable.rs | 72 ++ 6 files changed, 1162 insertions(+) create mode 100644 eth2/state_processing/Cargo.toml create mode 100644 eth2/state_processing/src/block_processable.rs create mode 100644 eth2/state_processing/src/epoch_processable.rs create mode 100644 eth2/state_processing/src/lib.rs create mode 100644 eth2/state_processing/src/slot_processable.rs diff --git a/Cargo.toml b/Cargo.toml index b2efe55ad..323d10dbe 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,6 +4,7 @@ members = [ "eth2/block_producer", "eth2/genesis", "eth2/naive_fork_choice", + "eth2/state_processing", "eth2/types", "eth2/utils/bls", "eth2/utils/boolean-bitfield", diff --git a/eth2/state_processing/Cargo.toml b/eth2/state_processing/Cargo.toml new file mode 100644 index 000000000..57d1c99ed --- /dev/null +++ b/eth2/state_processing/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "state_processing" +version = "0.1.0" +authors = ["Paul Hauner "] +edition = "2018" + +[dependencies] +hashing = { path = "../utils/hashing" } +log = "0.4" +ssz = { path = "../utils/ssz" } +types = { path = "../types" } +rayon = "1.0" diff --git a/eth2/state_processing/src/block_processable.rs b/eth2/state_processing/src/block_processable.rs new file mode 100644 index 000000000..3750529cd --- /dev/null +++ b/eth2/state_processing/src/block_processable.rs @@ -0,0 +1,400 @@ +use crate::SlotProcessingError; +use hashing::hash; +use log::debug; +use ssz::{ssz_encode, TreeHash}; +use types::{ + beacon_state::{AttestationValidationError, CommitteesError}, + AggregatePublicKey, Attestation, BeaconBlock, BeaconState, ChainSpec, Epoch, Exit, Fork, + Hash256, PendingAttestation, PublicKey, Signature, +}; + +// TODO: define elsehwere. +const DOMAIN_PROPOSAL: u64 = 2; +const DOMAIN_EXIT: u64 = 3; +const DOMAIN_RANDAO: u64 = 4; +const PHASE_0_CUSTODY_BIT: bool = false; +const DOMAIN_ATTESTATION: u64 = 1; + +#[derive(Debug, PartialEq)] +pub enum Error { + DBError(String), + StateAlreadyTransitioned, + PresentSlotIsNone, + UnableToDecodeBlock, + MissingParentState(Hash256), + InvalidParentState(Hash256), + MissingBeaconBlock(Hash256), + InvalidBeaconBlock(Hash256), + MissingParentBlock(Hash256), + NoBlockProducer, + StateSlotMismatch, + BadBlockSignature, + BadRandaoSignature, + MaxProposerSlashingsExceeded, + BadProposerSlashing, + MaxAttestationsExceeded, + InvalidAttestation(AttestationValidationError), + NoBlockRoot, + MaxDepositsExceeded, + MaxExitsExceeded, + BadExit, + BadCustodyReseeds, + BadCustodyChallenges, + BadCustodyResponses, + CommitteesError(CommitteesError), + SlotProcessingError(SlotProcessingError), +} + +macro_rules! ensure { + ($condition: expr, $result: expr) => { + if !$condition { + return Err($result); + } + }; +} + +pub trait BlockProcessable { + fn per_block_processing(&mut self, block: &BeaconBlock, spec: &ChainSpec) -> Result<(), Error>; + fn per_block_processing_without_verifying_block_signature( + &mut self, + block: &BeaconBlock, + spec: &ChainSpec, + ) -> Result<(), Error>; +} + +impl BlockProcessable for BeaconState { + fn per_block_processing(&mut self, block: &BeaconBlock, spec: &ChainSpec) -> Result<(), Error> { + per_block_processing_signature_optional(self, block, true, spec) + } + + fn per_block_processing_without_verifying_block_signature( + &mut self, + block: &BeaconBlock, + spec: &ChainSpec, + ) -> Result<(), Error> { + per_block_processing_signature_optional(self, block, false, spec) + } +} + +fn per_block_processing_signature_optional( + state: &mut BeaconState, + block: &BeaconBlock, + verify_block_signature: bool, + spec: &ChainSpec, +) -> Result<(), Error> { + ensure!(block.slot == state.slot, Error::StateSlotMismatch); + + /* + * Proposer Signature + */ + let block_proposer_index = state + .get_beacon_proposer_index(block.slot, spec) + .map_err(|_| Error::NoBlockProducer)?; + let block_proposer = &state.validator_registry[block_proposer_index]; + + if verify_block_signature { + ensure!( + bls_verify( + &block_proposer.pubkey, + &block.proposal_root(spec)[..], + &block.signature, + get_domain(&state.fork, state.current_epoch(spec), DOMAIN_PROPOSAL) + ), + Error::BadBlockSignature + ); + } + + /* + * RANDAO + */ + ensure!( + bls_verify( + &block_proposer.pubkey, + &ssz_encode(&state.current_epoch(spec)), + &block.randao_reveal, + get_domain(&state.fork, state.current_epoch(spec), DOMAIN_RANDAO) + ), + Error::BadRandaoSignature + ); + + // TODO: check this is correct. + let new_mix = { + let mut mix = state.latest_randao_mixes + [state.slot.as_usize() % spec.latest_randao_mixes_length] + .to_vec(); + mix.append(&mut ssz_encode(&block.randao_reveal)); + Hash256::from(&hash(&mix)[..]) + }; + + state.latest_randao_mixes[state.slot.as_usize() % spec.latest_randao_mixes_length] = new_mix; + + /* + * Eth1 data + */ + // TODO: Eth1 data processing. + + /* + * Proposer slashings + */ + ensure!( + block.body.proposer_slashings.len() as u64 <= spec.max_proposer_slashings, + Error::MaxProposerSlashingsExceeded + ); + for proposer_slashing in &block.body.proposer_slashings { + let proposer = state + .validator_registry + .get(proposer_slashing.proposer_index as usize) + .ok_or(Error::BadProposerSlashing)?; + ensure!( + proposer_slashing.proposal_data_1.slot == proposer_slashing.proposal_data_2.slot, + Error::BadProposerSlashing + ); + ensure!( + proposer_slashing.proposal_data_1.shard == proposer_slashing.proposal_data_2.shard, + Error::BadProposerSlashing + ); + ensure!( + proposer_slashing.proposal_data_1.block_root + != proposer_slashing.proposal_data_2.block_root, + Error::BadProposerSlashing + ); + ensure!( + proposer.penalized_epoch > state.current_epoch(spec), + Error::BadProposerSlashing + ); + ensure!( + bls_verify( + &proposer.pubkey, + &proposer_slashing.proposal_data_1.hash_tree_root(), + &proposer_slashing.proposal_signature_1, + get_domain( + &state.fork, + proposer_slashing + .proposal_data_1 + .slot + .epoch(spec.epoch_length), + DOMAIN_PROPOSAL + ) + ), + Error::BadProposerSlashing + ); + ensure!( + bls_verify( + &proposer.pubkey, + &proposer_slashing.proposal_data_2.hash_tree_root(), + &proposer_slashing.proposal_signature_2, + get_domain( + &state.fork, + proposer_slashing + .proposal_data_2 + .slot + .epoch(spec.epoch_length), + DOMAIN_PROPOSAL + ) + ), + Error::BadProposerSlashing + ); + state.penalize_validator(proposer_slashing.proposer_index as usize, spec); + } + + /* + * Attestations + */ + ensure!( + block.body.attestations.len() as u64 <= spec.max_attestations, + Error::MaxAttestationsExceeded + ); + + for attestation in &block.body.attestations { + validate_attestation(&state, attestation, spec)?; + + let pending_attestation = PendingAttestation { + data: attestation.data.clone(), + aggregation_bitfield: attestation.aggregation_bitfield.clone(), + custody_bitfield: attestation.custody_bitfield.clone(), + inclusion_slot: state.slot, + }; + state.latest_attestations.push(pending_attestation); + } + + debug!( + "{} attestations verified & processed.", + block.body.attestations.len() + ); + + /* + * Deposits + */ + ensure!( + block.body.deposits.len() as u64 <= spec.max_deposits, + Error::MaxDepositsExceeded + ); + + // TODO: process deposits. + + /* + * Exits + */ + ensure!( + block.body.exits.len() as u64 <= spec.max_exits, + Error::MaxExitsExceeded + ); + + for exit in &block.body.exits { + let validator = state + .validator_registry + .get(exit.validator_index as usize) + .ok_or(Error::BadExit)?; + ensure!( + validator.exit_epoch + > state.get_entry_exit_effect_epoch(state.current_epoch(spec), spec), + Error::BadExit + ); + ensure!(state.current_epoch(spec) >= exit.epoch, Error::BadExit); + let exit_message = { + let exit_struct = Exit { + epoch: exit.epoch, + 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(&state.fork, exit.epoch, DOMAIN_EXIT) + ), + Error::BadProposerSlashing + ); + state.initiate_validator_exit(exit.validator_index as usize); + } + + debug!("State transition complete."); + + Ok(()) +} + +pub fn validate_attestation( + state: &BeaconState, + attestation: &Attestation, + spec: &ChainSpec, +) -> Result<(), AttestationValidationError> { + validate_attestation_signature_optional(state, attestation, spec, true) +} + +pub fn validate_attestation_without_signature( + state: &BeaconState, + attestation: &Attestation, + spec: &ChainSpec, +) -> Result<(), AttestationValidationError> { + validate_attestation_signature_optional(state, attestation, spec, false) +} + +fn validate_attestation_signature_optional( + state: &BeaconState, + attestation: &Attestation, + spec: &ChainSpec, + verify_signature: bool, +) -> Result<(), AttestationValidationError> { + ensure!( + attestation.data.slot + spec.min_attestation_inclusion_delay <= state.slot, + AttestationValidationError::IncludedTooEarly + ); + ensure!( + attestation.data.slot + spec.epoch_length >= state.slot, + AttestationValidationError::IncludedTooLate + ); + if attestation.data.slot >= state.current_epoch_start_slot(spec) { + ensure!( + attestation.data.justified_epoch == state.justified_epoch, + AttestationValidationError::WrongJustifiedSlot + ); + } else { + ensure!( + attestation.data.justified_epoch == state.previous_justified_epoch, + AttestationValidationError::WrongJustifiedSlot + ); + } + ensure!( + attestation.data.justified_block_root + == *state + .get_block_root( + attestation + .data + .justified_epoch + .start_slot(spec.epoch_length), + &spec + ) + .ok_or(AttestationValidationError::NoBlockRoot)?, + AttestationValidationError::WrongJustifiedRoot + ); + ensure!( + (attestation.data.latest_crosslink + == state.latest_crosslinks[attestation.data.shard as usize]) + || (attestation.data.latest_crosslink + == state.latest_crosslinks[attestation.data.shard as usize]), + AttestationValidationError::BadLatestCrosslinkRoot + ); + if verify_signature { + let participants = state.get_attestation_participants( + &attestation.data, + &attestation.aggregation_bitfield, + spec, + )?; + let mut group_public_key = AggregatePublicKey::new(); + for participant in participants { + group_public_key.add( + state.validator_registry[participant as usize] + .pubkey + .as_raw(), + ) + } + ensure!( + attestation.verify_signature( + &group_public_key, + PHASE_0_CUSTODY_BIT, + get_domain( + &state.fork, + attestation.data.slot.epoch(spec.epoch_length), + DOMAIN_ATTESTATION, + ) + ), + AttestationValidationError::BadSignature + ); + } + ensure!( + attestation.data.shard_block_root == spec.zero_hash, + AttestationValidationError::ShardBlockRootNotZero + ); + Ok(()) +} + +fn get_domain(_fork: &Fork, _epoch: Epoch, _domain_type: u64) -> u64 { + // TODO: stubbed out. + 0 +} + +fn bls_verify(pubkey: &PublicKey, message: &[u8], signature: &Signature, _domain: u64) -> bool { + // TODO: add domain + signature.verify(message, pubkey) +} + +impl From for Error { + fn from(e: AttestationValidationError) -> Error { + Error::InvalidAttestation(e) + } +} + +impl From for Error { + fn from(e: CommitteesError) -> Error { + Error::CommitteesError(e) + } +} + +impl From for Error { + fn from(e: SlotProcessingError) -> Error { + Error::SlotProcessingError(e) + } +} diff --git a/eth2/state_processing/src/epoch_processable.rs b/eth2/state_processing/src/epoch_processable.rs new file mode 100644 index 000000000..45a61ce83 --- /dev/null +++ b/eth2/state_processing/src/epoch_processable.rs @@ -0,0 +1,670 @@ +use log::debug; +use rayon::prelude::*; +use std::collections::{HashMap, HashSet}; +use std::iter::FromIterator; +use types::{ + beacon_state::{AttestationParticipantsError, CommitteesError, InclusionError}, + validator_registry::get_active_validator_indices, + BeaconState, ChainSpec, Crosslink, Hash256, PendingAttestation, +}; + +macro_rules! safe_add_assign { + ($a: expr, $b: expr) => { + $a = $a.saturating_add($b); + }; +} +macro_rules! safe_sub_assign { + ($a: expr, $b: expr) => { + $a = $a.saturating_sub($b); + }; +} + +#[derive(Debug, PartialEq)] +pub enum Error { + UnableToDetermineProducer, + NoBlockRoots, + BaseRewardQuotientIsZero, + CommitteesError(CommitteesError), + AttestationParticipantsError(AttestationParticipantsError), + InclusionError(InclusionError), + WinningRootError(WinningRootError), +} + +#[derive(Debug, PartialEq)] +pub enum WinningRootError { + NoWinningRoot, + AttestationParticipantsError(AttestationParticipantsError), +} + +#[derive(Clone)] +pub struct WinningRoot { + pub shard_block_root: Hash256, + pub attesting_validator_indices: Vec, + pub total_balance: u64, + pub total_attesting_balance: u64, +} + +pub trait EpochProcessable { + fn per_epoch_processing(&mut self, spec: &ChainSpec) -> Result<(), Error>; +} + +impl EpochProcessable for BeaconState { + fn per_epoch_processing(&mut self, spec: &ChainSpec) -> Result<(), Error> { + let current_epoch = self.current_epoch(spec); + let previous_epoch = self.previous_epoch(spec); + let next_epoch = self.next_epoch(spec); + + 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.epoch(spec.epoch_length), + ); + let total_balance = self.get_total_balance(&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(Error::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_epoch == self.justified_epoch) + } + // 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_total_balance(¤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_epoch == self.previous_justified_epoch) + .cloned() + .collect(); + let mut b: Vec<&PendingAttestation> = previous_epoch_attestations + .iter() + .filter(|a| a.data.justified_epoch == self.previous_justified_epoch) + .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_total_balance(&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_total_balance(&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_total_balance(&previous_epoch_head_attester_indices[..], spec); + + debug!( + "previous_epoch_head_attester_balance of {} wei.", + previous_epoch_head_attesting_balance + ); + + /* + * Eth1 Data + */ + if self.next_epoch(spec) % 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 + */ + let new_justified_epoch = self.justified_epoch; + self.previous_justified_epoch = self.justified_epoch; + 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_epoch = 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_epoch = self.slot.saturating_sub(1 * spec.epoch_length); + debug!(">= 2/3 voted for current epoch boundary"); + } + + if (self.previous_justified_epoch == self.slot.saturating_sub(2 * spec.epoch_length)) + && (self.justification_bitfield % 4 == 3) + { + self.finalized_slot = self.previous_justified_epoch; + } + if (self.previous_justified_epoch == self.slot.saturating_sub(3 * spec.epoch_length)) + && (self.justification_bitfield % 8 == 7) + { + self.finalized_slot = self.previous_justified_epoch; + } + if (self.previous_justified_epoch == self.slot.saturating_sub(4 * spec.epoch_length)) + && (self.justification_bitfield % 16 == 14) + { + self.finalized_slot = self.previous_justified_epoch; + } + if (self.previous_justified_epoch == self.slot.saturating_sub(4 * spec.epoch_length)) + && (self.justification_bitfield % 16 == 15) + { + self.finalized_slot = self.previous_justified_epoch; + } + + debug!( + "Finalized slot {}, justified slot {}.", + self.finalized_slot, self.justified_epoch + ); + + /* + * 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, false, 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_total_balance(&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(Error::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(|_| Error::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_calculation_epoch = self.current_calculation_epoch; + 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_epoch + { + (0..self.get_current_epoch_committee_count(spec)).all(|i| { + let shard = (self.current_epoch_start_shard + i as u64) % spec.shard_count; + self.latest_crosslinks[shard as usize].epoch > self.validator_registry_update_epoch + }) + } else { + false + }; + + if should_update_validator_registy { + self.update_validator_registry(spec); + + self.current_calculation_epoch = self.slot; + self.current_epoch_start_shard = (self.current_epoch_start_shard + + self.get_current_epoch_committee_count(spec) as u64 * spec.epoch_length) + % spec.shard_count; + self.current_epoch_seed = + self.get_randao_mix(self.current_calculation_epoch - spec.seed_lookahead, spec); + } else { + let epochs_since_last_registry_change = + (self.slot - self.validator_registry_update_epoch) / spec.epoch_length; + if epochs_since_last_registry_change.is_power_of_two() { + self.current_calculation_epoch = self.slot; + self.current_epoch_seed = + self.get_randao_mix(self.current_calculation_epoch - 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(()) + } +} + +fn winning_root( + state: &BeaconState, + shard: u64, + current_epoch_attestations: &[&PendingAttestation], + previous_epoch_attestations: &[&PendingAttestation], + spec: &ChainSpec, +) -> Result { + let mut attestations = current_epoch_attestations.to_vec(); + attestations.append(&mut previous_epoch_attestations.to_vec()); + + let mut candidates: HashMap = HashMap::new(); + + let mut highest_seen_balance = 0; + + for a in &attestations { + if a.data.shard != shard { + continue; + } + + let shard_block_root = &a.data.shard_block_root; + + if candidates.contains_key(shard_block_root) { + continue; + } + + // TODO: `cargo fmt` makes this rather ugly; tidy up. + let attesting_validator_indices = attestations.iter().try_fold::<_, _, Result< + _, + AttestationParticipantsError, + >>(vec![], |mut acc, a| { + if (a.data.shard == shard) && (a.data.shard_block_root == *shard_block_root) { + acc.append(&mut state.get_attestation_participants( + &a.data, + &a.aggregation_bitfield, + spec, + )?); + } + Ok(acc) + })?; + + let total_balance: u64 = attesting_validator_indices + .iter() + .fold(0, |acc, i| acc + state.get_effective_balance(*i, spec)); + + let total_attesting_balance: u64 = attesting_validator_indices + .iter() + .fold(0, |acc, i| acc + state.get_effective_balance(*i, spec)); + + if total_attesting_balance > highest_seen_balance { + highest_seen_balance = total_attesting_balance; + } + + let candidate_root = WinningRoot { + shard_block_root: shard_block_root.clone(), + attesting_validator_indices, + total_attesting_balance, + total_balance, + }; + + candidates.insert(*shard_block_root, candidate_root); + } + + Ok(candidates + .iter() + .filter_map(|(_hash, candidate)| { + if candidate.total_attesting_balance == highest_seen_balance { + Some(candidate) + } else { + None + } + }) + .min_by_key(|candidate| candidate.shard_block_root) + .ok_or_else(|| WinningRootError::NoWinningRoot)? + // TODO: avoid clone. + .clone()) +} + +impl From for Error { + fn from(e: InclusionError) -> Error { + Error::InclusionError(e) + } +} + +impl From for Error { + fn from(e: CommitteesError) -> Error { + Error::CommitteesError(e) + } +} + +impl From for Error { + fn from(e: AttestationParticipantsError) -> Error { + Error::AttestationParticipantsError(e) + } +} + +impl From for WinningRootError { + fn from(e: AttestationParticipantsError) -> WinningRootError { + WinningRootError::AttestationParticipantsError(e) + } +} + +#[cfg(test)] +mod tests { + #[test] + fn it_works() { + assert_eq!(2 + 2, 4); + } +} diff --git a/eth2/state_processing/src/lib.rs b/eth2/state_processing/src/lib.rs new file mode 100644 index 000000000..ff5ae8660 --- /dev/null +++ b/eth2/state_processing/src/lib.rs @@ -0,0 +1,7 @@ +mod block_processable; +mod epoch_processable; +mod slot_processable; + +pub use block_processable::{BlockProcessable, Error as BlockProcessingError}; +pub use epoch_processable::{EpochProcessable, Error as EpochProcessingError}; +pub use slot_processable::{Error as SlotProcessingError, SlotProcessable}; diff --git a/eth2/state_processing/src/slot_processable.rs b/eth2/state_processing/src/slot_processable.rs new file mode 100644 index 000000000..f0e776f57 --- /dev/null +++ b/eth2/state_processing/src/slot_processable.rs @@ -0,0 +1,72 @@ +use crate::{EpochProcessable, EpochProcessingError}; +use types::{beacon_state::CommitteesError, BeaconState, ChainSpec, Hash256}; + +#[derive(Debug, PartialEq)] +pub enum Error { + CommitteesError(CommitteesError), + EpochProcessingError(EpochProcessingError), +} + +pub trait SlotProcessable { + fn per_slot_processing( + &mut self, + previous_block_root: Hash256, + spec: &ChainSpec, + ) -> Result<(), Error>; +} + +impl SlotProcessable for BeaconState +where + BeaconState: EpochProcessable, +{ + fn per_slot_processing( + &mut self, + previous_block_root: Hash256, + spec: &ChainSpec, + ) -> Result<(), Error> { + 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.latest_randao_mixes[self.slot.as_usize() % spec.latest_randao_mixes_length] = + self.latest_randao_mixes[(self.slot.as_usize() - 1) % spec.latest_randao_mixes_length]; + + // Block roots. + self.latest_block_roots[(self.slot.as_usize() - 1) % spec.latest_block_roots_length] = + previous_block_root; + + if self.slot.as_usize() % spec.latest_block_roots_length == 0 { + let root = merkle_root(&self.latest_block_roots[..]); + self.batched_block_roots.push(root); + } + Ok(()) + } +} + +fn merkle_root(_input: &[Hash256]) -> Hash256 { + Hash256::zero() +} + +impl From for Error { + fn from(e: CommitteesError) -> Error { + Error::CommitteesError(e) + } +} + +impl From for Error { + fn from(e: EpochProcessingError) -> Error { + Error::EpochProcessingError(e) + } +} + +#[cfg(test)] +mod tests { + #[test] + fn it_works() { + assert_eq!(2 + 2, 4); + } +} From ff2025ec5ccf7b11b4dec52992f15046557e68f2 Mon Sep 17 00:00:00 2001 From: Paul Hauner Date: Tue, 12 Feb 2019 08:58:20 +1100 Subject: [PATCH 07/27] Update epoch processing to v0.2.0 --- eth2/state_processing/Cargo.toml | 1 + .../state_processing/src/epoch_processable.rs | 281 +++++++++++------- eth2/types/Cargo.toml | 1 - eth2/types/src/beacon_state.rs | 17 +- eth2/types/src/spec/foundation.rs | 2 +- eth2/types/src/spec/mod.rs | 2 +- 6 files changed, 176 insertions(+), 128 deletions(-) diff --git a/eth2/state_processing/Cargo.toml b/eth2/state_processing/Cargo.toml index 57d1c99ed..683475f47 100644 --- a/eth2/state_processing/Cargo.toml +++ b/eth2/state_processing/Cargo.toml @@ -6,6 +6,7 @@ edition = "2018" [dependencies] hashing = { path = "../utils/hashing" } +integer-sqrt = "0.1" log = "0.4" ssz = { path = "../utils/ssz" } types = { path = "../types" } diff --git a/eth2/state_processing/src/epoch_processable.rs b/eth2/state_processing/src/epoch_processable.rs index 45a61ce83..cf634e52f 100644 --- a/eth2/state_processing/src/epoch_processable.rs +++ b/eth2/state_processing/src/epoch_processable.rs @@ -1,11 +1,13 @@ -use log::debug; +use integer_sqrt::IntegerSquareRoot; +use log::{debug, trace}; use rayon::prelude::*; +use ssz::TreeHash; use std::collections::{HashMap, HashSet}; use std::iter::FromIterator; use types::{ beacon_state::{AttestationParticipantsError, CommitteesError, InclusionError}, validator_registry::get_active_validator_indices, - BeaconState, ChainSpec, Crosslink, Hash256, PendingAttestation, + BeaconState, ChainSpec, Crosslink, Epoch, Hash256, PendingAttestation, }; macro_rules! safe_add_assign { @@ -24,6 +26,7 @@ pub enum Error { UnableToDetermineProducer, NoBlockRoots, BaseRewardQuotientIsZero, + NoRandaoSeed, CommitteesError(CommitteesError), AttestationParticipantsError(AttestationParticipantsError), InclusionError(InclusionError), @@ -60,18 +63,18 @@ impl EpochProcessable for BeaconState { ); /* - * All Validators + * Validators attesting during the current epoch. */ let active_validator_indices = get_active_validator_indices( &self.validator_registry, self.slot.epoch(spec.epoch_length), ); - let total_balance = self.get_total_balance(&active_validator_indices[..], spec); + let current_total_balance = self.get_total_balance(&active_validator_indices[..], spec); - debug!( + trace!( "{} validators with a total balance of {} wei.", active_validator_indices.len(), - total_balance + current_total_balance ); let current_epoch_attestations: Vec<&PendingAttestation> = self @@ -83,33 +86,23 @@ impl EpochProcessable for BeaconState { }) .collect(); - debug!( + trace!( "Current epoch attestations: {}", current_epoch_attestations.len() ); - /* - * Validators attesting during the current epoch. - */ - if self.latest_block_roots.is_empty() { - return Err(Error::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) { + .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_epoch == self.justified_epoch) } - // Protected by a check that latest_block_roots isn't empty. - // - // TODO: provide detailed reasoning. None => unreachable!(), - } - }) + }, + ) .cloned() .collect(); @@ -118,7 +111,7 @@ impl EpochProcessable for BeaconState { let current_epoch_boundary_attesting_balance = self.get_total_balance(¤t_epoch_boundary_attester_indices[..], spec); - debug!( + trace!( "Current epoch boundary attesters: {}", current_epoch_boundary_attester_indices.len() ); @@ -147,6 +140,8 @@ impl EpochProcessable for BeaconState { let previous_epoch_attester_indices = self.get_attestation_participants_union(&previous_epoch_attestations[..], spec)?; + let previous_total_balance = + self.get_total_balance(&previous_epoch_attester_indices[..], spec); /* * Validators targetting the previous justified slot @@ -177,15 +172,12 @@ impl EpochProcessable for BeaconState { 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) { + .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(); @@ -200,14 +192,9 @@ impl EpochProcessable for BeaconState { 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!(), - } + .filter(|a| match self.get_block_root(a.data.slot, spec) { + Some(block_root) => a.data.beacon_block_root == *block_root, + None => unreachable!(), }) .cloned() .collect(); @@ -237,51 +224,84 @@ impl EpochProcessable for BeaconState { /* * Justification */ - let new_justified_epoch = self.justified_epoch; - self.previous_justified_epoch = self.justified_epoch; - 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. + let mut new_justified_epoch = self.justified_epoch; + self.justification_bitfield = self.justification_bitfield << 1; + + // If > 2/3 of the total balance attested to the previous epoch boundary + // + // - Set the 2nd bit of the bitfield. + // - Set the previous epoch to be justified. + if (3 * previous_epoch_boundary_attesting_balance) >= (2 * current_total_balance) { self.justification_bitfield |= 2; - self.justified_epoch = self.slot.saturating_sub(2 * spec.epoch_length); - debug!(">= 2/3 voted for previous epoch boundary"); + new_justified_epoch = previous_epoch; + trace!(">= 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. + // If > 2/3 of the total balance attested to the previous epoch boundary + // + // - Set the 1st bit of the bitfield. + // - Set the current epoch to be justified. + if (3 * current_epoch_boundary_attesting_balance) >= (2 * current_total_balance) { self.justification_bitfield |= 1; - self.justified_epoch = self.slot.saturating_sub(1 * spec.epoch_length); - debug!(">= 2/3 voted for current epoch boundary"); + new_justified_epoch = current_epoch; + trace!(">= 2/3 voted for current epoch boundary"); } - if (self.previous_justified_epoch == self.slot.saturating_sub(2 * spec.epoch_length)) - && (self.justification_bitfield % 4 == 3) + // If: + // + // - All three epochs prior to this epoch have been justified. + // - The previous justified justified epoch was three epochs ago. + // + // Then, set the finalized epoch to be three epochs ago. + if ((self.justification_bitfield >> 1) % 8 == 0b111) + & (self.previous_justified_epoch == previous_epoch - 2) { - self.finalized_slot = self.previous_justified_epoch; + self.finalized_epoch = self.previous_justified_epoch; + trace!("epoch - 3 was finalized (1st condition)."); } - if (self.previous_justified_epoch == self.slot.saturating_sub(3 * spec.epoch_length)) - && (self.justification_bitfield % 8 == 7) + // If: + // + // - Both two epochs prior to this epoch have been justified. + // - The previous justified epoch was two epochs ago. + // + // Then, set the finalized epoch to two epochs ago. + if ((self.justification_bitfield >> 1) % 4 == 0b11) + & (self.previous_justified_epoch == previous_epoch - 1) { - self.finalized_slot = self.previous_justified_epoch; + self.finalized_epoch = self.previous_justified_epoch; + trace!("epoch - 2 was finalized (2nd condition)."); } - if (self.previous_justified_epoch == self.slot.saturating_sub(4 * spec.epoch_length)) - && (self.justification_bitfield % 16 == 14) + // If: + // + // - This epoch and the two prior have been justified. + // - The presently justified epoch was two epochs ago. + // + // Then, set the finalized epoch to two epochs ago. + if ((self.justification_bitfield >> 0) % 8 == 0b111) + & (self.justified_epoch == previous_epoch - 1) { - self.finalized_slot = self.previous_justified_epoch; + self.finalized_epoch = self.justified_epoch; + trace!("epoch - 2 was finalized (3rd condition)."); } - if (self.previous_justified_epoch == self.slot.saturating_sub(4 * spec.epoch_length)) - && (self.justification_bitfield % 16 == 15) + // If: + // + // - This epoch and the epoch prior to it have been justified. + // - Set the previous epoch to be justified. + // + // Then, set the finalized epoch to be the previous epoch. + if ((self.justification_bitfield >> 0) % 4 == 0b11) + & (self.justified_epoch == previous_epoch) { - self.finalized_slot = self.previous_justified_epoch; + self.finalized_epoch = self.justified_epoch; + trace!("epoch - 1 was finalized (4th condition)."); } + self.previous_justified_epoch = self.justified_epoch; + self.justified_epoch = new_justified_epoch; + debug!( - "Finalized slot {}, justified slot {}.", - self.finalized_slot, self.justified_epoch + "Finalized epoch {}, justified epoch {}.", + self.finalized_epoch, self.justified_epoch ); /* @@ -300,7 +320,8 @@ impl EpochProcessable for BeaconState { for (crosslink_committee, shard) in crosslink_committees_at_slot { let shard = shard as u64; - let winning_root = self.winning_root( + let winning_root = winning_root( + self, shard, ¤t_epoch_attestations, &previous_epoch_attestations, @@ -313,7 +334,7 @@ impl EpochProcessable for BeaconState { if (3 * winning_root.total_attesting_balance) >= (2 * total_committee_balance) { self.latest_crosslinks[shard as usize] = Crosslink { - slot: self.slot, + epoch: current_epoch, shard_block_root: winning_root.shard_block_root, } } @@ -322,7 +343,7 @@ impl EpochProcessable for BeaconState { } } - debug!( + trace!( "Found {} winning shard roots.", winning_root_for_shards.len() ); @@ -330,7 +351,7 @@ impl EpochProcessable for BeaconState { /* * Rewards and Penalities */ - let base_reward_quotient = total_balance.integer_sqrt(); + let base_reward_quotient = previous_total_balance.integer_sqrt(); if base_reward_quotient == 0 { return Err(Error::BaseRewardQuotientIsZero); } @@ -338,18 +359,18 @@ impl EpochProcessable for BeaconState { /* * Justification and finalization */ - let epochs_since_finality = - (self.slot.saturating_sub(self.finalized_slot) / spec.epoch_length).as_u64(); + let epochs_since_finality = next_epoch - self.finalized_epoch; - // 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)); + HashSet::from_iter(previous_epoch_justified_attester_indices.iter().cloned()); let previous_epoch_boundary_attester_indices_hashset: HashSet = - HashSet::from_iter(previous_epoch_boundary_attester_indices.iter().map(|i| *i)); + HashSet::from_iter(previous_epoch_boundary_attester_indices.iter().cloned()); let previous_epoch_head_attester_indices_hashset: HashSet = - HashSet::from_iter(previous_epoch_head_attester_indices.iter().map(|i| *i)); + HashSet::from_iter(previous_epoch_head_attester_indices.iter().cloned()); let previous_epoch_attester_indices_hashset: HashSet = - HashSet::from_iter(previous_epoch_attester_indices.iter().map(|i| *i)); + HashSet::from_iter(previous_epoch_attester_indices.iter().cloned()); + let active_validator_indices_hashset: HashSet = + HashSet::from_iter(active_validator_indices.iter().cloned()); 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()); @@ -362,28 +383,37 @@ impl EpochProcessable for BeaconState { 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 + base_reward * previous_epoch_justified_attesting_balance + / previous_total_balance ); } else { - safe_sub_assign!(self.validator_balances[index], base_reward); + if active_validator_indices_hashset.contains(&index) { + 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 + base_reward * previous_epoch_boundary_attesting_balance + / previous_total_balance ); } else { - safe_sub_assign!(self.validator_balances[index], base_reward); + if active_validator_indices_hashset.contains(&index) { + 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 + base_reward * previous_epoch_head_attesting_balance + / previous_total_balance ); } else { - safe_sub_assign!(self.validator_balances[index], base_reward); + if active_validator_indices_hashset.contains(&index) { + safe_sub_assign!(self.validator_balances[index], base_reward); + } } } @@ -405,17 +435,24 @@ impl EpochProcessable for BeaconState { base_reward_quotient, spec, ); + if active_validator_indices_hashset.contains(&index) { + 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); + } - 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); + if self.validator_registry[index].penalized_epoch <= current_epoch { + let base_reward = self.base_reward(index, base_reward_quotient, spec); + safe_sub_assign!( + self.validator_balances[index], + 2 * inactivity_penalty + base_reward + ); + } } } @@ -432,7 +469,7 @@ impl EpochProcessable for BeaconState { } } - debug!("Processed validator justification and finalization rewards/penalities."); + trace!("Processed validator justification and finalization rewards/penalities."); /* * Attestation inclusion @@ -450,7 +487,7 @@ impl EpochProcessable for BeaconState { ); } - debug!( + trace!( "Previous epoch attesters: {}.", previous_epoch_attester_indices_hashset.len() ); @@ -459,7 +496,8 @@ impl EpochProcessable for BeaconState { * 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)?; + let crosslink_committees_at_slot = + self.get_crosslink_committees_at_slot(slot, false, spec)?; for (_crosslink_committee, shard) in crosslink_committees_at_slot { let shard = shard as u64; @@ -499,7 +537,7 @@ impl EpochProcessable for BeaconState { /* * Ejections */ - self.process_ejections(); + self.process_ejections(spec); /* * Validator Registry @@ -508,7 +546,7 @@ impl EpochProcessable for BeaconState { 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 + let should_update_validator_registy = if self.finalized_epoch > self.validator_registry_update_epoch { (0..self.get_current_epoch_committee_count(spec)).all(|i| { @@ -522,35 +560,50 @@ impl EpochProcessable for BeaconState { if should_update_validator_registy { self.update_validator_registry(spec); - self.current_calculation_epoch = self.slot; + self.current_calculation_epoch = next_epoch; self.current_epoch_start_shard = (self.current_epoch_start_shard - + self.get_current_epoch_committee_count(spec) as u64 * spec.epoch_length) + + self.get_current_epoch_committee_count(spec) as u64) % spec.shard_count; - self.current_epoch_seed = - self.get_randao_mix(self.current_calculation_epoch - spec.seed_lookahead, spec); + self.current_epoch_seed = self + .generate_seed(self.current_calculation_epoch, spec) + .ok_or_else(|| Error::NoRandaoSeed)?; } else { - let epochs_since_last_registry_change = - (self.slot - self.validator_registry_update_epoch) / spec.epoch_length; - if epochs_since_last_registry_change.is_power_of_two() { - self.current_calculation_epoch = self.slot; - self.current_epoch_seed = - self.get_randao_mix(self.current_calculation_epoch - spec.seed_lookahead, spec); + let epochs_since_last_registry_update = + current_epoch - self.validator_registry_update_epoch; + if (epochs_since_last_registry_update > 1) + & epochs_since_last_registry_update.is_power_of_two() + { + self.current_calculation_epoch = next_epoch; + self.current_epoch_seed = self + .generate_seed(self.current_calculation_epoch, spec) + .ok_or_else(|| Error::NoRandaoSeed)?; } } 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_index_roots[(next_epoch.as_usize() + spec.entry_exit_delay as usize) + % spec.latest_index_roots_length] = hash_tree_root(get_active_validator_indices( + &self.validator_registry, + next_epoch + Epoch::from(spec.entry_exit_delay), + )); + self.latest_penalized_balances[next_epoch.as_usize() % spec.latest_penalized_exit_length] = + self.latest_penalized_balances + [current_epoch.as_usize() % spec.latest_penalized_exit_length]; + self.latest_randao_mixes[next_epoch.as_usize() % spec.latest_randao_mixes_length] = self + .get_randao_mix(current_epoch, spec) + .and_then(|x| Some(*x)) + .ok_or_else(|| Error::NoRandaoSeed)?; self.latest_attestations = self .latest_attestations .iter() - .filter(|a| { - (a.data.slot / spec.epoch_length).epoch(spec.epoch_length) - >= self.current_epoch(spec) - }) + .filter(|a| a.data.slot.epoch(spec.epoch_length) >= current_epoch) .cloned() .collect(); @@ -560,6 +613,10 @@ impl EpochProcessable for BeaconState { } } +fn hash_tree_root(input: Vec) -> Hash256 { + Hash256::from(&input.hash_tree_root()[..]) +} + fn winning_root( state: &BeaconState, shard: u64, diff --git a/eth2/types/Cargo.toml b/eth2/types/Cargo.toml index c6550891e..24aabf148 100644 --- a/eth2/types/Cargo.toml +++ b/eth2/types/Cargo.toml @@ -10,7 +10,6 @@ boolean-bitfield = { path = "../utils/boolean-bitfield" } ethereum-types = "0.4.0" hashing = { path = "../utils/hashing" } honey-badger-split = { path = "../utils/honey-badger-split" } -integer-sqrt = "0.1" log = "0.4" rayon = "1.0" rand = "0.5.5" diff --git a/eth2/types/src/beacon_state.rs b/eth2/types/src/beacon_state.rs index d442eb367..0d5b19eb6 100644 --- a/eth2/types/src/beacon_state.rs +++ b/eth2/types/src/beacon_state.rs @@ -617,13 +617,15 @@ impl BeaconState { pub fn inactivity_penalty( &self, validator_index: usize, - epochs_since_finality: u64, + epochs_since_finality: Epoch, base_reward_quotient: u64, spec: &ChainSpec, ) -> u64 { let effective_balance = self.get_effective_balance(validator_index, spec); self.base_reward(validator_index, base_reward_quotient, spec) - + effective_balance * epochs_since_finality / spec.inactivity_penalty_quotient / 2 + + effective_balance * epochs_since_finality.as_u64() + / spec.inactivity_penalty_quotient + / 2 } /// Returns the distance between the first included attestation for some validator and this @@ -802,17 +804,6 @@ impl From for AttestationParticipantsError { /* -impl From for SlotProcessingError { - fn from(e: CommitteesError) -> SlotProcessingError { - SlotProcessingError::CommitteesError(e) - } -} - -impl From for SlotProcessingError { - fn from(e: EpochError) -> SlotProcessingError { - SlotProcessingError::EpochProcessingError(e) - } -} */ impl From for InclusionError { diff --git a/eth2/types/src/spec/foundation.rs b/eth2/types/src/spec/foundation.rs index 0d8f34f0d..79abe4061 100644 --- a/eth2/types/src/spec/foundation.rs +++ b/eth2/types/src/spec/foundation.rs @@ -56,7 +56,7 @@ impl ChainSpec { * Time parameters */ slot_duration: 6, - min_attestation_inclusion_delay: Slot::new(4), + min_attestation_inclusion_delay: 4, epoch_length, seed_lookahead: Epoch::new(1), entry_exit_delay: 4, diff --git a/eth2/types/src/spec/mod.rs b/eth2/types/src/spec/mod.rs index ad0d78012..53c78a2c2 100644 --- a/eth2/types/src/spec/mod.rs +++ b/eth2/types/src/spec/mod.rs @@ -49,7 +49,7 @@ pub struct ChainSpec { * Time parameters */ pub slot_duration: u64, - pub min_attestation_inclusion_delay: Slot, + pub min_attestation_inclusion_delay: u64, pub epoch_length: u64, pub seed_lookahead: Epoch, pub entry_exit_delay: u64, From f3e556bca339c6ebcd3ed7580f016d9b374bb253 Mon Sep 17 00:00:00 2001 From: Paul Hauner Date: Tue, 12 Feb 2019 09:02:26 +1100 Subject: [PATCH 08/27] Remove validator_induction crate --- Cargo.toml | 1 - eth2/genesis/Cargo.toml | 1 - eth2/validator_induction/Cargo.toml | 10 -- eth2/validator_induction/src/inductor.rs | 180 ----------------------- eth2/validator_induction/src/lib.rs | 3 - 5 files changed, 195 deletions(-) delete mode 100644 eth2/validator_induction/Cargo.toml delete mode 100644 eth2/validator_induction/src/inductor.rs delete mode 100644 eth2/validator_induction/src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index 323d10dbe..b037aa807 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,7 +13,6 @@ members = [ "eth2/utils/slot_clock", "eth2/utils/ssz", "eth2/utils/vec_shuffle", - "eth2/validator_induction", "beacon_node", "beacon_node/db", "beacon_node/beacon_chain", diff --git a/eth2/genesis/Cargo.toml b/eth2/genesis/Cargo.toml index 09e5d1e88..db0dd19dd 100644 --- a/eth2/genesis/Cargo.toml +++ b/eth2/genesis/Cargo.toml @@ -8,4 +8,3 @@ edition = "2018" bls = { path = "../utils/bls" } ssz = { path = "../utils/ssz" } types = { path = "../types" } -validator_induction = { path = "../validator_induction" } diff --git a/eth2/validator_induction/Cargo.toml b/eth2/validator_induction/Cargo.toml deleted file mode 100644 index 5907014df..000000000 --- a/eth2/validator_induction/Cargo.toml +++ /dev/null @@ -1,10 +0,0 @@ -[package] -name = "validator_induction" -version = "0.1.0" -authors = ["Paul Hauner "] -edition = "2018" - -[dependencies] -bls = { path = "../utils/bls" } -hashing = { path = "../utils/hashing" } -types = { path = "../types" } diff --git a/eth2/validator_induction/src/inductor.rs b/eth2/validator_induction/src/inductor.rs deleted file mode 100644 index b0a4ba9e6..000000000 --- a/eth2/validator_induction/src/inductor.rs +++ /dev/null @@ -1,180 +0,0 @@ -use bls::verify_proof_of_possession; -use types::{BeaconState, ChainSpec, Deposit, Slot, Validator}; - -#[derive(Debug, PartialEq, Clone)] -pub enum ValidatorInductionError { - InvalidShard, - InvaidProofOfPossession, - InvalidWithdrawalCredentials, -} - -pub fn process_deposit( - state: &mut BeaconState, - deposit: &Deposit, - spec: &ChainSpec, -) -> Result<(), ValidatorInductionError> { - let deposit_input = &deposit.deposit_data.deposit_input; - let deposit_data = &deposit.deposit_data; - - // TODO: Update the signature validation as defined in the spec once issues #91 and #70 are completed - if !verify_proof_of_possession(&deposit_input.proof_of_possession, &deposit_input.pubkey) { - return Err(ValidatorInductionError::InvaidProofOfPossession); - } - - let validator_index = state - .validator_registry - .iter() - .position(|validator| validator.pubkey == deposit_input.pubkey); - - match validator_index { - Some(i) => { - if state.validator_registry[i].withdrawal_credentials - == deposit_input.withdrawal_credentials - { - state.validator_balances[i] += deposit_data.amount; - return Ok(()); - } - - Err(ValidatorInductionError::InvalidWithdrawalCredentials) - } - None => { - let validator = Validator { - pubkey: deposit_input.pubkey.clone(), - withdrawal_credentials: deposit_input.withdrawal_credentials, - proposer_slots: 0, - activation_slot: spec.far_future_slot, - exit_slot: spec.far_future_slot, - withdrawal_slot: spec.far_future_slot, - penalized_slot: spec.far_future_slot, - exit_count: 0, - status_flags: None, - latest_custody_reseed_slot: Slot::new(0), - penultimate_custody_reseed_slot: Slot::new(0), - }; - - let _index = state.validator_registry.len(); - state.validator_registry.push(validator); - state.validator_balances.push(deposit_data.amount); - Ok(()) - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use types::test_utils::{SeedableRng, TestRandom, XorShiftRng}; - - use bls::{create_proof_of_possession, Keypair}; - - /// The size of a validators deposit in GWei. - pub const DEPOSIT_GWEI: u64 = 32_000_000_000; - - fn get_deposit() -> Deposit { - let mut rng = XorShiftRng::from_seed([42; 16]); - let mut deposit = Deposit::random_for_test(&mut rng); - - let kp = Keypair::random(); - deposit.deposit_data.deposit_input.pubkey = kp.pk.clone(); - deposit.deposit_data.deposit_input.proof_of_possession = create_proof_of_possession(&kp); - deposit - } - - fn get_validator() -> Validator { - let mut rng = XorShiftRng::from_seed([42; 16]); - Validator::random_for_test(&mut rng) - } - - fn deposit_equals_record(dep: &Deposit, val: &Validator) -> bool { - (dep.deposit_data.deposit_input.pubkey == val.pubkey) - & (dep.deposit_data.deposit_input.withdrawal_credentials == val.withdrawal_credentials) - & (verify_proof_of_possession( - &dep.deposit_data.deposit_input.proof_of_possession, - &val.pubkey, - )) - } - - #[test] - fn test_process_deposit_valid_empty_validators() { - let mut state = BeaconState::default(); - let mut deposit = get_deposit(); - let spec = ChainSpec::foundation(); - deposit.deposit_data.amount = DEPOSIT_GWEI; - - let result = process_deposit(&mut state, &deposit, &spec); - - assert_eq!(result.unwrap(), ()); - assert!(deposit_equals_record( - &deposit, - &state.validator_registry[0] - )); - assert_eq!(state.validator_registry.len(), 1); - assert_eq!(state.validator_balances.len(), 1); - } - - #[test] - fn test_process_deposits_empty_validators() { - let mut state = BeaconState::default(); - let spec = ChainSpec::foundation(); - - for i in 0..5 { - let mut deposit = get_deposit(); - let result = process_deposit(&mut state, &deposit, &spec); - deposit.deposit_data.amount = DEPOSIT_GWEI; - assert_eq!(result.unwrap(), ()); - assert!(deposit_equals_record( - &deposit, - &state.validator_registry[i] - )); - assert_eq!(state.validator_registry.len(), i + 1); - assert_eq!(state.validator_balances.len(), i + 1); - } - } - - #[test] - fn test_process_deposit_top_out() { - let mut state = BeaconState::default(); - let spec = ChainSpec::foundation(); - - let mut deposit = get_deposit(); - let mut validator = get_validator(); - - deposit.deposit_data.amount = DEPOSIT_GWEI; - validator.pubkey = deposit.deposit_data.deposit_input.pubkey.clone(); - validator.withdrawal_credentials = - deposit.deposit_data.deposit_input.withdrawal_credentials; - - state.validator_registry.push(validator); - state.validator_balances.push(DEPOSIT_GWEI); - - let result = process_deposit(&mut state, &deposit, &spec); - - assert_eq!(result.unwrap(), ()); - assert!(deposit_equals_record( - &deposit, - &state.validator_registry[0] - )); - assert_eq!(state.validator_balances[0], DEPOSIT_GWEI * 2); - assert_eq!(state.validator_registry.len(), 1); - assert_eq!(state.validator_balances.len(), 1); - } - - #[test] - fn test_process_deposit_invalid_proof_of_possession() { - let mut state = BeaconState::default(); - let mut deposit = get_deposit(); - let spec = ChainSpec::foundation(); - deposit.deposit_data.amount = DEPOSIT_GWEI; - deposit.deposit_data.deposit_input.proof_of_possession = - create_proof_of_possession(&Keypair::random()); - - let result = process_deposit(&mut state, &deposit, &spec); - - assert_eq!( - result, - Err(ValidatorInductionError::InvaidProofOfPossession) - ); - assert_eq!(state.validator_registry.len(), 0); - assert_eq!(state.validator_balances.len(), 0); - } -} diff --git a/eth2/validator_induction/src/lib.rs b/eth2/validator_induction/src/lib.rs deleted file mode 100644 index 3a4fa9a14..000000000 --- a/eth2/validator_induction/src/lib.rs +++ /dev/null @@ -1,3 +0,0 @@ -mod inductor; - -pub use crate::inductor::{process_deposit, ValidatorInductionError}; From 3b75e931223b61c866c62853a9fed067b34a6e3f Mon Sep 17 00:00:00 2001 From: Paul Hauner Date: Tue, 12 Feb 2019 11:54:02 +1100 Subject: [PATCH 09/27] Add `genesis()` to `BeaconState` --- eth2/types/src/beacon_state.rs | 163 +++++++++++++++++++++++++++++---- 1 file changed, 147 insertions(+), 16 deletions(-) diff --git a/eth2/types/src/beacon_state.rs b/eth2/types/src/beacon_state.rs index 0d5b19eb6..1b959976b 100644 --- a/eth2/types/src/beacon_state.rs +++ b/eth2/types/src/beacon_state.rs @@ -1,10 +1,10 @@ use crate::test_utils::TestRandom; use crate::{ - validator::StatusFlags, validator_registry::get_active_validator_indices, AggregatePublicKey, - Attestation, AttestationData, Bitfield, ChainSpec, Crosslink, Epoch, Eth1Data, Eth1DataVote, - Fork, Hash256, PendingAttestation, Slot, Validator, + validator::StatusFlags, validator_registry::get_active_validator_indices, AttestationData, + Bitfield, ChainSpec, Crosslink, Deposit, Epoch, Eth1Data, Eth1DataVote, Fork, Hash256, + PendingAttestation, PublicKey, Signature, Slot, Validator, }; -use bls::bls_verify_aggregate; +use bls::verify_proof_of_possession; use honey_badger_split::SplitExt; use rand::RngCore; use serde_derive::Serialize; @@ -12,10 +12,6 @@ use ssz::{hash, Decodable, DecodeError, Encodable, SszStream, TreeHash}; use std::ops::Range; use vec_shuffle::shuffle; -// TODO: define elsehwere. -const PHASE_0_CUSTODY_BIT: bool = false; -const DOMAIN_ATTESTATION: u64 = 1; - pub enum Error { InsufficientValidators, BadBlockSignature, @@ -65,14 +61,6 @@ pub enum AttestationValidationError { AttestationParticipantsError(AttestationParticipantsError), } -macro_rules! ensure { - ($condition: expr, $result: expr) => { - if !$condition { - return Err($result); - } - }; -} - macro_rules! safe_add_assign { ($a: expr, $b: expr) => { $a = $a.saturating_add($b); @@ -125,6 +113,103 @@ pub struct BeaconState { } impl BeaconState { + /// Produce the first state of the Beacon Chain. + pub fn genesis( + genesis_time: u64, + initial_validator_deposits: Vec, + latest_eth1_data: Eth1Data, + spec: &ChainSpec, + ) -> BeaconState { + let initial_crosslink = Crosslink { + epoch: spec.genesis_epoch, + shard_block_root: spec.zero_hash, + }; + + let mut genesis_state = BeaconState { + /* + * Misc + */ + slot: spec.genesis_slot, + genesis_time, + fork: Fork { + previous_version: spec.genesis_fork_version, + current_version: spec.genesis_fork_version, + epoch: spec.genesis_epoch, + }, + + /* + * Validator registry + */ + validator_registry: vec![], // Set later in the function. + validator_balances: vec![], // Set later in the function. + validator_registry_update_epoch: spec.genesis_epoch, + + /* + * Randomness and committees + */ + latest_randao_mixes: vec![spec.zero_hash; spec.latest_randao_mixes_length as usize], + previous_epoch_start_shard: spec.genesis_start_shard, + current_epoch_start_shard: spec.genesis_start_shard, + previous_calculation_epoch: spec.genesis_epoch, + current_calculation_epoch: spec.genesis_epoch, + previous_epoch_seed: spec.zero_hash, + current_epoch_seed: spec.zero_hash, + + /* + * Finality + */ + previous_justified_epoch: spec.genesis_epoch, + justified_epoch: spec.genesis_epoch, + justification_bitfield: 0, + finalized_epoch: spec.genesis_epoch, + + /* + * Recent state + */ + latest_crosslinks: vec![initial_crosslink; spec.shard_count as usize], + latest_block_roots: vec![spec.zero_hash; spec.latest_block_roots_length as usize], + latest_index_roots: vec![spec.zero_hash; spec.latest_index_roots_length as usize], + latest_penalized_balances: vec![0; spec.latest_penalized_exit_length as usize], + latest_attestations: vec![], + batched_block_roots: vec![], + + /* + * PoW receipt root + */ + latest_eth1_data, + eth1_data_votes: vec![], + }; + + for deposit in initial_validator_deposits { + let _index = genesis_state.process_deposit( + deposit.deposit_data.deposit_input.pubkey, + deposit.deposit_data.amount, + deposit.deposit_data.deposit_input.proof_of_possession, + deposit.deposit_data.deposit_input.withdrawal_credentials, + spec, + ); + } + + for validator_index in 0..genesis_state.validator_registry.len() { + if genesis_state.get_effective_balance(validator_index, spec) >= spec.max_deposit_amount + { + genesis_state.activate_validator(validator_index, true, spec); + } + } + + let genesis_active_index_root = hash_tree_root(get_active_validator_indices( + &genesis_state.validator_registry, + spec.genesis_epoch, + )); + genesis_state.latest_index_roots = + vec![genesis_active_index_root; spec.latest_index_roots_length]; + genesis_state.current_epoch_seed = genesis_state + .generate_seed(spec.genesis_epoch, spec) + .expect("Unable to generate seed."); + + genesis_state + } + /// Return the tree hash root for this `BeaconState`. /// /// Spec v0.2.0 @@ -512,6 +597,48 @@ impl BeaconState { self.validator_registry_update_epoch = current_epoch; } + /// Process a validator deposit, returning the validator index if the deposit is valid. + /// + /// Spec v0.2.0 + pub fn process_deposit( + &mut self, + pubkey: PublicKey, + amount: u64, + proof_of_possession: Signature, + withdrawal_credentials: Hash256, + spec: &ChainSpec, + ) -> Result { + // TODO: ensure verify proof-of-possession represents the spec accurately. + if !verify_proof_of_possession(&proof_of_possession, &pubkey) { + return Err(()); + } + + if let Some(index) = self + .validator_registry + .iter() + .position(|v| v.pubkey == pubkey) + { + if self.validator_registry[index].withdrawal_credentials == withdrawal_credentials { + safe_add_assign!(self.validator_balances[index], amount); + Ok(index) + } else { + Err(()) + } + } else { + let validator = Validator { + pubkey, + withdrawal_credentials, + activation_epoch: spec.far_future_epoch, + exit_epoch: spec.far_future_epoch, + withdrawal_epoch: spec.far_future_epoch, + penalized_epoch: spec.far_future_epoch, + status_flags: None, + }; + self.validator_registry.push(validator); + self.validator_balances.push(amount); + Ok(self.validator_registry.len() - 1) + } + } /// Activate the validator of the given ``index``. /// @@ -790,6 +917,10 @@ impl BeaconState { } } +fn hash_tree_root(input: Vec) -> Hash256 { + Hash256::from(&input.hash_tree_root()[..]) +} + impl From for AttestationValidationError { fn from(e: AttestationParticipantsError) -> AttestationValidationError { AttestationValidationError::AttestationParticipantsError(e) From f27308e377f24c341284d480d2caf0b26429d5cb Mon Sep 17 00:00:00 2001 From: Paul Hauner Date: Tue, 12 Feb 2019 11:57:20 +1100 Subject: [PATCH 10/27] Tidy old comments --- eth2/state_processing/src/epoch_processable.rs | 6 ------ eth2/types/src/beacon_state.rs | 8 -------- 2 files changed, 14 deletions(-) diff --git a/eth2/state_processing/src/epoch_processable.rs b/eth2/state_processing/src/epoch_processable.rs index cf634e52f..3bc0d7cb9 100644 --- a/eth2/state_processing/src/epoch_processable.rs +++ b/eth2/state_processing/src/epoch_processable.rs @@ -582,12 +582,6 @@ impl EpochProcessable for BeaconState { 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_index_roots[(next_epoch.as_usize() + spec.entry_exit_delay as usize) % spec.latest_index_roots_length] = hash_tree_root(get_active_validator_indices( &self.validator_registry, diff --git a/eth2/types/src/beacon_state.rs b/eth2/types/src/beacon_state.rs index 1b959976b..e11b43e80 100644 --- a/eth2/types/src/beacon_state.rs +++ b/eth2/types/src/beacon_state.rs @@ -40,14 +40,6 @@ pub enum AttestationParticipantsError { CommitteesError(CommitteesError), } -/* -#[derive(Debug, PartialEq)] -pub enum SlotProcessingError { - CommitteesError(CommitteesError), - EpochProcessingError(EpochError), -} -*/ - #[derive(Debug, PartialEq)] pub enum AttestationValidationError { IncludedTooEarly, From cb97870c151383957c3b31e5f7812bc04b61c920 Mon Sep 17 00:00:00 2001 From: Paul Hauner Date: Tue, 12 Feb 2019 11:57:34 +1100 Subject: [PATCH 11/27] Add `genesis()` function to BeaconBlock --- eth2/types/src/beacon_block.rs | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/eth2/types/src/beacon_block.rs b/eth2/types/src/beacon_block.rs index 2e844859a..f68f79316 100644 --- a/eth2/types/src/beacon_block.rs +++ b/eth2/types/src/beacon_block.rs @@ -17,6 +17,28 @@ pub struct BeaconBlock { } impl BeaconBlock { + /// Produce the first block of the Beacon Chain. + pub fn genesis(state_root: Hash256, spec: &ChainSpec) -> BeaconBlock { + BeaconBlock { + slot: spec.genesis_slot, + parent_root: spec.zero_hash, + state_root, + randao_reveal: spec.empty_signature.clone(), + eth1_data: Eth1Data { + deposit_root: spec.zero_hash, + block_hash: spec.zero_hash, + }, + signature: spec.empty_signature.clone(), + body: BeaconBlockBody { + proposer_slashings: vec![], + attester_slashings: vec![], + attestations: vec![], + deposits: vec![], + exits: vec![], + }, + } + } + pub fn canonical_root(&self) -> Hash256 { Hash256::from(&self.hash_tree_root()[..]) } From 5e37e8a33a52f9379f4844dec18186d9c6ccedec Mon Sep 17 00:00:00 2001 From: Paul Hauner Date: Tue, 12 Feb 2019 12:57:17 +1100 Subject: [PATCH 12/27] Update `validator_client` for spec v0.2.0 --- .../src/block_producer_service/beacon_block_grpc_client.rs | 5 +---- validator_client/src/main.rs | 7 +++++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/validator_client/src/block_producer_service/beacon_block_grpc_client.rs b/validator_client/src/block_producer_service/beacon_block_grpc_client.rs index bbf978336..c1aee86e5 100644 --- a/validator_client/src/block_producer_service/beacon_block_grpc_client.rs +++ b/validator_client/src/block_producer_service/beacon_block_grpc_client.rs @@ -65,11 +65,8 @@ impl BeaconNode for BeaconBlockGrpcClient { signature, body: BeaconBlockBody { proposer_slashings: vec![], - casper_slashings: vec![], + attester_slashings: vec![], attestations: vec![], - custody_reseeds: vec![], - custody_challenges: vec![], - custody_responses: vec![], deposits: vec![], exits: vec![], }, diff --git a/validator_client/src/main.rs b/validator_client/src/main.rs index 0e1fd4a6b..8fa49e51c 100644 --- a/validator_client/src/main.rs +++ b/validator_client/src/main.rs @@ -88,9 +88,12 @@ fn main() { let spec = Arc::new(ChainSpec::foundation()); // Clock for determining the present slot. + // TODO: this shouldn't be a static time, instead it should be pulled from the beacon node. + // https://github.com/sigp/lighthouse/issues/160 + let genesis_time = 1_549_935_547; let slot_clock = { - info!(log, "Genesis time"; "unix_epoch_seconds" => spec.genesis_time); - let clock = SystemTimeSlotClock::new(spec.genesis_time, spec.slot_duration) + info!(log, "Genesis time"; "unix_epoch_seconds" => genesis_time); + let clock = SystemTimeSlotClock::new(genesis_time, spec.slot_duration) .expect("Unable to instantiate SystemTimeSlotClock."); Arc::new(clock) }; From 36f441c968ec10abc627caee977807ca2f1da4c4 Mon Sep 17 00:00:00 2001 From: Paul Hauner Date: Tue, 12 Feb 2019 12:58:12 +1100 Subject: [PATCH 13/27] Replace `genesis` crate with on-type defs The methods in the `gensis` crate have been moved to `genesis` methods on `BeaconState` and `BeaconBlock`. --- Cargo.toml | 1 - beacon_node/Cargo.toml | 1 - beacon_node/beacon_chain/Cargo.toml | 1 - .../beacon_chain/test_harness/Cargo.toml | 1 - eth2/genesis/Cargo.toml | 10 - eth2/genesis/src/beacon_block.rs | 93 --------- eth2/genesis/src/beacon_state.rs | 194 ------------------ eth2/genesis/src/lib.rs | 5 - 8 files changed, 306 deletions(-) delete mode 100644 eth2/genesis/Cargo.toml delete mode 100644 eth2/genesis/src/beacon_block.rs delete mode 100644 eth2/genesis/src/beacon_state.rs delete mode 100644 eth2/genesis/src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index b037aa807..1dd1899be 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,6 @@ members = [ "eth2/attester", "eth2/block_producer", - "eth2/genesis", "eth2/naive_fork_choice", "eth2/state_processing", "eth2/types", diff --git a/beacon_node/Cargo.toml b/beacon_node/Cargo.toml index e5893195e..d2fd087ce 100644 --- a/beacon_node/Cargo.toml +++ b/beacon_node/Cargo.toml @@ -14,7 +14,6 @@ clap = "2.32.0" db = { path = "db" } dirs = "1.0.3" futures = "0.1.23" -genesis = { path = "../eth2/genesis" } slog = "^2.2.3" slot_clock = { path = "../eth2/utils/slot_clock" } slog-term = "^2.4.0" diff --git a/beacon_node/beacon_chain/Cargo.toml b/beacon_node/beacon_chain/Cargo.toml index 5c930403c..b80d001a2 100644 --- a/beacon_node/beacon_chain/Cargo.toml +++ b/beacon_node/beacon_chain/Cargo.toml @@ -11,7 +11,6 @@ boolean-bitfield = { path = "../../eth2/utils/boolean-bitfield" } db = { path = "../db" } failure = "0.1" failure_derive = "0.1" -genesis = { path = "../../eth2/genesis" } hashing = { path = "../../eth2/utils/hashing" } parking_lot = "0.7" log = "0.4" diff --git a/beacon_node/beacon_chain/test_harness/Cargo.toml b/beacon_node/beacon_chain/test_harness/Cargo.toml index ce32b94c6..9a59e34ae 100644 --- a/beacon_node/beacon_chain/test_harness/Cargo.toml +++ b/beacon_node/beacon_chain/test_harness/Cargo.toml @@ -21,7 +21,6 @@ db = { path = "../../db" } parking_lot = "0.7" failure = "0.1" failure_derive = "0.1" -genesis = { path = "../../../eth2/genesis" } hashing = { path = "../../../eth2/utils/hashing" } log = "0.4" env_logger = "0.6.0" diff --git a/eth2/genesis/Cargo.toml b/eth2/genesis/Cargo.toml deleted file mode 100644 index db0dd19dd..000000000 --- a/eth2/genesis/Cargo.toml +++ /dev/null @@ -1,10 +0,0 @@ -[package] -name = "genesis" -version = "0.1.0" -authors = ["Paul Hauner "] -edition = "2018" - -[dependencies] -bls = { path = "../utils/bls" } -ssz = { path = "../utils/ssz" } -types = { path = "../types" } diff --git a/eth2/genesis/src/beacon_block.rs b/eth2/genesis/src/beacon_block.rs deleted file mode 100644 index 8b78f9e2d..000000000 --- a/eth2/genesis/src/beacon_block.rs +++ /dev/null @@ -1,93 +0,0 @@ -use types::{BeaconBlock, BeaconBlockBody, ChainSpec, Eth1Data, Hash256}; - -/// Generate a genesis BeaconBlock. -pub fn genesis_beacon_block(state_root: Hash256, spec: &ChainSpec) -> BeaconBlock { - BeaconBlock { - slot: spec.genesis_slot, - parent_root: spec.zero_hash, - state_root, - randao_reveal: spec.empty_signature.clone(), - eth1_data: Eth1Data { - deposit_root: spec.zero_hash, - block_hash: spec.zero_hash, - }, - signature: spec.empty_signature.clone(), - body: BeaconBlockBody { - proposer_slashings: vec![], - casper_slashings: vec![], - attestations: vec![], - custody_reseeds: vec![], - custody_challenges: vec![], - custody_responses: vec![], - deposits: vec![], - exits: vec![], - }, - } -} - -#[cfg(test)] -mod tests { - use super::*; - use bls::Signature; - - #[test] - fn test_state_root() { - let spec = ChainSpec::foundation(); - let state_root = Hash256::from("cats".as_bytes()); - - let block = genesis_beacon_block(state_root, &spec); - - assert_eq!(block.state_root, state_root); - } - - #[test] - fn test_zero_items() { - let spec = ChainSpec::foundation(); - - let state_root = Hash256::zero(); - - let genesis_block = genesis_beacon_block(state_root, &spec); - - assert!(genesis_block.slot == 0); - assert!(genesis_block.parent_root.is_zero()); - assert_eq!(genesis_block.randao_reveal, Signature::empty_signature()); - assert!(genesis_block.eth1_data.deposit_root.is_zero()); - assert!(genesis_block.eth1_data.block_hash.is_zero()); - } - - #[test] - fn test_beacon_body() { - let spec = ChainSpec::foundation(); - - let state_root = Hash256::zero(); - - let genesis_block = genesis_beacon_block(state_root, &spec); - - // Custody items are not being implemented until phase 1 so tests to be added later - - assert!(genesis_block.body.proposer_slashings.is_empty()); - assert!(genesis_block.body.casper_slashings.is_empty()); - assert!(genesis_block.body.attestations.is_empty()); - assert!(genesis_block.body.deposits.is_empty()); - assert!(genesis_block.body.exits.is_empty()); - } - - #[test] - fn test_signature() { - let spec = ChainSpec::foundation(); - - let state_root = Hash256::zero(); - - let genesis_block = genesis_beacon_block(state_root, &spec); - - // Signature should consist of [bytes48(0), bytes48(0)] - // Note this is implemented using Apache Milagro BLS which requires one extra byte -> 97bytes - let raw_sig = genesis_block.signature.as_raw(); - let raw_sig_bytes = raw_sig.as_bytes(); - - for item in raw_sig_bytes.iter() { - assert!(*item == 0); - } - assert_eq!(genesis_block.signature, Signature::empty_signature()); - } -} diff --git a/eth2/genesis/src/beacon_state.rs b/eth2/genesis/src/beacon_state.rs deleted file mode 100644 index 4ccb32e54..000000000 --- a/eth2/genesis/src/beacon_state.rs +++ /dev/null @@ -1,194 +0,0 @@ -use types::{BeaconState, ChainSpec, Crosslink, Fork}; - -pub fn genesis_beacon_state(spec: &ChainSpec) -> BeaconState { - let initial_crosslink = Crosslink { - slot: spec.genesis_slot, - shard_block_root: spec.zero_hash, - }; - - BeaconState { - /* - * Misc - */ - slot: spec.genesis_slot, - genesis_time: spec.genesis_time, - fork_data: Fork { - pre_fork_version: spec.genesis_fork_version, - post_fork_version: spec.genesis_fork_version, - fork_slot: spec.genesis_slot, - }, - /* - * Validator registry - */ - validator_registry: spec.initial_validators.clone(), - validator_balances: spec.initial_balances.clone(), - validator_registry_update_slot: spec.genesis_slot, - validator_registry_exit_count: 0, - validator_registry_delta_chain_tip: spec.zero_hash, - /* - * Randomness and committees - */ - latest_randao_mixes: vec![spec.zero_hash; spec.latest_randao_mixes_length as usize], - latest_vdf_outputs: vec![ - spec.zero_hash; - (spec.latest_randao_mixes_length / spec.epoch_length) as usize - ], - previous_epoch_start_shard: spec.genesis_start_shard, - current_epoch_start_shard: spec.genesis_start_shard, - previous_epoch_calculation_slot: spec.genesis_slot, - current_epoch_calculation_slot: spec.genesis_slot, - previous_epoch_seed: spec.zero_hash, - current_epoch_seed: spec.zero_hash, - /* - * Custody challenges - */ - custody_challenges: vec![], - /* - * Finality - */ - previous_justified_slot: spec.genesis_slot, - justified_slot: spec.genesis_slot, - justification_bitfield: 0, - finalized_slot: spec.genesis_slot, - /* - * Recent state - */ - latest_crosslinks: vec![initial_crosslink; spec.shard_count as usize], - latest_block_roots: vec![spec.zero_hash; spec.latest_block_roots_length as usize], - latest_penalized_balances: vec![0; spec.latest_penalized_exit_length as usize], - latest_attestations: vec![], - batched_block_roots: vec![], - /* - * PoW receipt root - */ - latest_eth1_data: spec.intial_eth1_data.clone(), - eth1_data_votes: vec![], - } -} - -#[cfg(test)] -mod tests { - use super::*; - use types::Hash256; - - #[test] - fn test_genesis_state() { - let spec = ChainSpec::foundation(); - - let state = genesis_beacon_state(&spec); - - assert_eq!( - state.validator_registry.len(), - spec.initial_validators.len() - ); - } - - #[test] - fn test_genesis_state_misc() { - let spec = ChainSpec::foundation(); - - let state = genesis_beacon_state(&spec); - - assert_eq!(state.slot, 0); - assert_eq!(state.genesis_time, spec.genesis_time); - assert_eq!(state.fork_data.pre_fork_version, 0); - assert_eq!(state.fork_data.post_fork_version, 0); - assert_eq!(state.fork_data.fork_slot, 0); - } - - #[test] - fn test_genesis_state_validators() { - let spec = ChainSpec::foundation(); - - let state = genesis_beacon_state(&spec); - - assert_eq!(state.validator_registry, spec.initial_validators); - assert_eq!(state.validator_balances, spec.initial_balances); - assert!(state.validator_registry_update_slot == 0); - assert!(state.validator_registry_exit_count == 0); - assert_eq!(state.validator_registry_delta_chain_tip, Hash256::zero()); - } - - #[test] - fn test_genesis_state_randomness_committees() { - let spec = ChainSpec::foundation(); - - let state = genesis_beacon_state(&spec); - - // Array of size 8,192 each being zero_hash - assert_eq!(state.latest_randao_mixes.len(), 8_192); - for item in state.latest_randao_mixes.iter() { - assert_eq!(*item, Hash256::zero()); - } - - // Array of size 8,192 each being a zero hash - assert_eq!(state.latest_vdf_outputs.len(), (8_192 / 64)); - for item in state.latest_vdf_outputs.iter() { - assert_eq!(*item, Hash256::zero()); - } - - // TODO: Check shard and committee shuffling requires solving issue: - // https://github.com/sigp/lighthouse/issues/151 - - // initial_shuffling = get_shuffling(Hash256::zero(), &state.validator_registry, 0, 0) - // initial_shuffling = initial_shuffling.append(initial_shuffling.clone()); - } - - // Custody not implemented until Phase 1 - #[test] - fn test_genesis_state_custody() {} - - #[test] - fn test_genesis_state_finanilty() { - let spec = ChainSpec::foundation(); - - let state = genesis_beacon_state(&spec); - - assert_eq!(state.previous_justified_slot, 0); - assert_eq!(state.justified_slot, 0); - assert_eq!(state.justification_bitfield, 0); - assert_eq!(state.finalized_slot, 0); - } - - #[test] - fn test_genesis_state_recent_state() { - let spec = ChainSpec::foundation(); - - let state = genesis_beacon_state(&spec); - - // Test latest_crosslinks - assert_eq!(state.latest_crosslinks.len(), 1_024); - for link in state.latest_crosslinks.iter() { - assert_eq!(link.slot, 0); - assert_eq!(link.shard_block_root, Hash256::zero()); - } - - // Test latest_block_roots - assert_eq!(state.latest_block_roots.len(), 8_192); - for block in state.latest_block_roots.iter() { - assert_eq!(*block, Hash256::zero()); - } - - // Test latest_penalized_balances - assert_eq!(state.latest_penalized_balances.len(), 8_192); - for item in state.latest_penalized_balances.iter() { - assert!(*item == 0); - } - - // Test latest_attestations - assert!(state.latest_attestations.is_empty()); - - // batched_block_roots - assert!(state.batched_block_roots.is_empty()); - } - - #[test] - fn test_genesis_state_deposit_root() { - let spec = ChainSpec::foundation(); - - let state = genesis_beacon_state(&spec); - - assert_eq!(&state.latest_eth1_data, &spec.intial_eth1_data); - assert!(state.eth1_data_votes.is_empty()); - } -} diff --git a/eth2/genesis/src/lib.rs b/eth2/genesis/src/lib.rs deleted file mode 100644 index 295bdbd3c..000000000 --- a/eth2/genesis/src/lib.rs +++ /dev/null @@ -1,5 +0,0 @@ -mod beacon_block; -mod beacon_state; - -pub use crate::beacon_block::genesis_beacon_block; -pub use crate::beacon_state::genesis_beacon_state; From 5fefc79521b7f0aa9d09941aee618c73af0c60d4 Mon Sep 17 00:00:00 2001 From: Paul Hauner Date: Tue, 12 Feb 2019 13:00:52 +1100 Subject: [PATCH 14/27] Update `BeaconChain` for spec v0.2.0 --- beacon_node/beacon_chain/Cargo.toml | 1 + .../src/attestation_aggregator.rs | 5 +- beacon_node/beacon_chain/src/beacon_chain.rs | 85 +++++++++---------- beacon_node/beacon_chain/src/lmd_ghost.rs | 6 +- eth2/state_processing/src/lib.rs | 5 +- 5 files changed, 53 insertions(+), 49 deletions(-) diff --git a/beacon_node/beacon_chain/Cargo.toml b/beacon_node/beacon_chain/Cargo.toml index b80d001a2..493984191 100644 --- a/beacon_node/beacon_chain/Cargo.toml +++ b/beacon_node/beacon_chain/Cargo.toml @@ -20,4 +20,5 @@ serde_derive = "1.0" serde_json = "1.0" slot_clock = { path = "../../eth2/utils/slot_clock" } ssz = { path = "../../eth2/utils/ssz" } +state_processing = { path = "../../eth2/state_processing" } types = { path = "../../eth2/types" } diff --git a/beacon_node/beacon_chain/src/attestation_aggregator.rs b/beacon_node/beacon_chain/src/attestation_aggregator.rs index 0f6aa388e..cfba01308 100644 --- a/beacon_node/beacon_chain/src/attestation_aggregator.rs +++ b/beacon_node/beacon_chain/src/attestation_aggregator.rs @@ -1,3 +1,4 @@ +use state_processing::validate_attestation_without_signature; use std::collections::{HashMap, HashSet}; use types::{ beacon_state::CommitteesError, AggregateSignature, Attestation, AttestationData, BeaconState, @@ -172,9 +173,7 @@ impl AttestationAggregator { self.store .values() .filter_map(|attestation| { - if state - .validate_attestation_without_signature(attestation, spec) - .is_ok() + if validate_attestation_without_signature(&state, attestation, spec).is_ok() && !known_attestation_data.contains(&attestation.data) { Some(attestation.clone()) diff --git a/beacon_node/beacon_chain/src/beacon_chain.rs b/beacon_node/beacon_chain/src/beacon_chain.rs index cde4d7cf6..55212ab20 100644 --- a/beacon_node/beacon_chain/src/beacon_chain.rs +++ b/beacon_node/beacon_chain/src/beacon_chain.rs @@ -1,24 +1,25 @@ -use db::{ - stores::{BeaconBlockStore, BeaconStateStore}, - ClientDB, DBError, -}; -use genesis::{genesis_beacon_block, genesis_beacon_state}; -use log::{debug, trace}; -use parking_lot::{RwLock, RwLockReadGuard}; -use slot_clock::SlotClock; -use ssz::ssz_encode; -use std::sync::Arc; -use types::{ - beacon_state::{BlockProcessingError, CommitteesError, SlotProcessingError}, - readers::{BeaconBlockReader, BeaconStateReader}, - AttestationData, BeaconBlock, BeaconBlockBody, BeaconState, ChainSpec, Eth1Data, - FreeAttestation, Hash256, PublicKey, Signature, Slot, -}; - use crate::attestation_aggregator::{AttestationAggregator, Outcome as AggregationOutcome}; use crate::attestation_targets::AttestationTargets; use crate::block_graph::BlockGraph; use crate::checkpoint::CheckPoint; +use db::{ + stores::{BeaconBlockStore, BeaconStateStore}, + ClientDB, DBError, +}; +use log::{debug, trace}; +use parking_lot::{RwLock, RwLockReadGuard}; +use slot_clock::SlotClock; +use ssz::ssz_encode; +use state_processing::{ + BlockProcessable, BlockProcessingError, SlotProcessable, SlotProcessingError, +}; +use std::sync::Arc; +use types::{ + beacon_state::CommitteesError, + readers::{BeaconBlockReader, BeaconStateReader}, + AttestationData, BeaconBlock, BeaconBlockBody, BeaconState, ChainSpec, Crosslink, Deposit, + Epoch, Eth1Data, FreeAttestation, Hash256, PublicKey, Signature, Slot, +}; #[derive(Debug, PartialEq)] pub enum Error { @@ -82,17 +83,25 @@ where state_store: Arc>, block_store: Arc>, slot_clock: U, + genesis_time: u64, + latest_eth1_data: Eth1Data, + initial_validator_deposits: Vec, spec: ChainSpec, ) -> Result { - if spec.initial_validators.is_empty() { + if initial_validator_deposits.is_empty() { return Err(Error::InsufficientValidators); } - let genesis_state = genesis_beacon_state(&spec); + let genesis_state = BeaconState::genesis( + genesis_time, + initial_validator_deposits, + latest_eth1_data, + &spec, + ); let state_root = genesis_state.canonical_root(); state_store.put(&state_root, &ssz_encode(&genesis_state)[..])?; - let genesis_block = genesis_beacon_block(state_root, &spec); + let genesis_block = BeaconBlock::genesis(state_root, &spec); let block_root = genesis_block.canonical_root(); block_store.put(&block_root, &ssz_encode(&genesis_block)[..])?; @@ -225,19 +234,6 @@ where None } - /// Returns the number of slots the validator has been required to propose. - /// - /// Returns `None` if the `validator_index` is invalid. - /// - /// Information is retrieved from the present `beacon_state.validator_registry`. - pub fn proposer_slots(&self, validator_index: usize) -> Option { - if let Some(validator) = self.state.read().validator_registry.get(validator_index) { - Some(validator.proposer_slots) - } else { - None - } - } - /// Reads the slot clock, returns `None` if the slot is unavailable. /// /// The slot might be unavailable due to an error with the system clock, or if the present time @@ -277,8 +273,8 @@ where } /// Returns the justified slot for the present state. - pub fn justified_slot(&self) -> Slot { - self.state.read().justified_slot + pub fn justified_epoch(&self) -> Epoch { + self.state.read().justified_epoch } /// Returns the attestation slot and shard for a given validator index. @@ -302,11 +298,14 @@ where /// Produce an `AttestationData` that is valid for the present `slot` and given `shard`. pub fn produce_attestation_data(&self, shard: u64) -> Result { - let justified_slot = self.justified_slot(); + let justified_epoch = self.justified_epoch(); let justified_block_root = self .state .read() - .get_block_root(justified_slot, &self.spec) + .get_block_root( + justified_epoch.start_slot(self.spec.epoch_length), + &self.spec, + ) .ok_or_else(|| Error::BadRecentBlockRoots)? .clone(); @@ -326,8 +325,11 @@ where beacon_block_root: self.head().beacon_block_root.clone(), epoch_boundary_root, shard_block_root: Hash256::zero(), - latest_crosslink_root: Hash256::zero(), - justified_slot, + latest_crosslink: Crosslink { + epoch: self.state.read().slot.epoch(self.spec.epoch_length), + shard_block_root: Hash256::zero(), + }, + justified_epoch, justified_block_root, }) } @@ -550,11 +552,8 @@ where signature: self.spec.empty_signature.clone(), // To be completed by a validator. body: BeaconBlockBody { proposer_slashings: vec![], - casper_slashings: vec![], + attester_slashings: vec![], attestations: attestations, - custody_reseeds: vec![], - custody_challenges: vec![], - custody_responses: vec![], deposits: vec![], exits: vec![], }, diff --git a/beacon_node/beacon_chain/src/lmd_ghost.rs b/beacon_node/beacon_chain/src/lmd_ghost.rs index 1af10b5e0..2c6fbe95a 100644 --- a/beacon_node/beacon_chain/src/lmd_ghost.rs +++ b/beacon_node/beacon_chain/src/lmd_ghost.rs @@ -68,8 +68,10 @@ where .into_beacon_state() .ok_or(Error::InvalidBeaconState(start_state_root))?; - let active_validator_indices = - get_active_validator_indices(&state.validator_registry, start.slot()); + let active_validator_indices = get_active_validator_indices( + &state.validator_registry, + start.slot().epoch(self.spec.epoch_length), + ); let mut attestation_targets = Vec::with_capacity(active_validator_indices.len()); for i in active_validator_indices { diff --git a/eth2/state_processing/src/lib.rs b/eth2/state_processing/src/lib.rs index ff5ae8660..18d1f7554 100644 --- a/eth2/state_processing/src/lib.rs +++ b/eth2/state_processing/src/lib.rs @@ -2,6 +2,9 @@ mod block_processable; mod epoch_processable; mod slot_processable; -pub use block_processable::{BlockProcessable, Error as BlockProcessingError}; +pub use block_processable::{ + validate_attestation, validate_attestation_without_signature, BlockProcessable, + Error as BlockProcessingError, +}; pub use epoch_processable::{EpochProcessable, Error as EpochProcessingError}; pub use slot_processable::{Error as SlotProcessingError, SlotProcessable}; From 12076bce769057f7a9c1ffdf079f0d9191e08631 Mon Sep 17 00:00:00 2001 From: Paul Hauner Date: Tue, 12 Feb 2019 16:57:47 +1100 Subject: [PATCH 15/27] Update test_harness to spec v0.2.0 --- .../test_harness/src/beacon_chain_harness.rs | 51 +++++++++++-------- .../validator_harness/direct_beacon_node.rs | 16 +----- 2 files changed, 31 insertions(+), 36 deletions(-) diff --git a/beacon_node/beacon_chain/test_harness/src/beacon_chain_harness.rs b/beacon_node/beacon_chain/test_harness/src/beacon_chain_harness.rs index 7cfe65b39..09080c60d 100644 --- a/beacon_node/beacon_chain/test_harness/src/beacon_chain_harness.rs +++ b/beacon_node/beacon_chain/test_harness/src/beacon_chain_harness.rs @@ -1,6 +1,7 @@ use super::ValidatorHarness; use beacon_chain::BeaconChain; pub use beacon_chain::{CheckPoint, Error as BeaconChainError}; +use bls::create_proof_of_possession; use db::{ stores::{BeaconBlockStore, BeaconStateStore}, MemoryDB, @@ -13,7 +14,10 @@ use std::fs::File; use std::io::prelude::*; use std::iter::FromIterator; use std::sync::Arc; -use types::{BeaconBlock, ChainSpec, FreeAttestation, Keypair, Slot, Validator}; +use types::{ + BeaconBlock, ChainSpec, Deposit, DepositData, DepositInput, Eth1Data, FreeAttestation, Hash256, + Keypair, Slot, +}; /// The beacon chain harness simulates a single beacon node with `validator_count` validators connected /// to it. Each validator is provided a borrow to the beacon chain, where it may read @@ -35,16 +39,17 @@ impl BeaconChainHarness { /// /// - A keypair, `BlockProducer` and `Attester` for each validator. /// - A new BeaconChain struct where the given validators are in the genesis. - pub fn new(mut spec: ChainSpec, validator_count: usize) -> Self { + pub fn new(spec: ChainSpec, validator_count: usize) -> Self { let db = Arc::new(MemoryDB::open()); let block_store = Arc::new(BeaconBlockStore::new(db.clone())); let state_store = Arc::new(BeaconStateStore::new(db.clone())); + let genesis_time = 1_549_935_547; // 12th Feb 2018 (arbitrary value in the past). let slot_clock = TestingSlotClock::new(spec.genesis_slot.as_u64()); - - // Remove the validators present in the spec (if any). - spec.initial_validators = Vec::with_capacity(validator_count); - spec.initial_balances = Vec::with_capacity(validator_count); + let latest_eth1_data = Eth1Data { + deposit_root: Hash256::zero(), + block_hash: Hash256::zero(), + }; debug!("Generating validator keypairs..."); @@ -54,25 +59,26 @@ impl BeaconChainHarness { .map(|_| Keypair::random()) .collect(); - debug!("Creating validator records..."); + debug!("Creating validator deposits..."); - spec.initial_validators = keypairs + let mut initial_validator_deposits = Vec::with_capacity(validator_count); + initial_validator_deposits = keypairs .par_iter() - .map(|keypair| Validator { - pubkey: keypair.pk.clone(), - activation_slot: Slot::new(0), - ..std::default::Default::default() + .map(|keypair| Deposit { + branch: vec![], // branch verification is not specified. + index: 0, // index verification is not specified. + deposit_data: DepositData { + amount: 32_000_000_000, // 32 ETH (in Gwei) + timestamp: genesis_time - 1, + deposit_input: DepositInput { + pubkey: keypair.pk.clone(), + withdrawal_credentials: Hash256::zero(), // Withdrawal not possible. + proof_of_possession: create_proof_of_possession(&keypair), + }, + }, }) .collect(); - debug!("Setting validator balances..."); - - spec.initial_balances = spec - .initial_validators - .par_iter() - .map(|_| 32_000_000_000) // 32 ETH - .collect(); - debug!("Creating the BeaconChain..."); // Create the Beacon Chain @@ -81,6 +87,9 @@ impl BeaconChainHarness { state_store.clone(), block_store.clone(), slot_clock, + genesis_time, + latest_eth1_data, + initial_validator_deposits, spec.clone(), ) .unwrap(), @@ -136,7 +145,7 @@ impl BeaconChainHarness { .beacon_chain .state .read() - .get_crosslink_committees_at_slot(present_slot, &self.spec) + .get_crosslink_committees_at_slot(present_slot, false, &self.spec) .unwrap() .iter() .fold(vec![], |mut acc, (committee, _slot)| { diff --git a/beacon_node/beacon_chain/test_harness/src/validator_harness/direct_beacon_node.rs b/beacon_node/beacon_chain/test_harness/src/validator_harness/direct_beacon_node.rs index f33eb65e8..79d2cbc83 100644 --- a/beacon_node/beacon_chain/test_harness/src/validator_harness/direct_beacon_node.rs +++ b/beacon_node/beacon_chain/test_harness/src/validator_harness/direct_beacon_node.rs @@ -11,7 +11,7 @@ use db::ClientDB; use parking_lot::RwLock; use slot_clock::SlotClock; use std::sync::Arc; -use types::{AttestationData, BeaconBlock, FreeAttestation, PublicKey, Signature, Slot}; +use types::{AttestationData, BeaconBlock, FreeAttestation, Signature, Slot}; // mod attester; // mod producer; @@ -70,20 +70,6 @@ impl AttesterBeaconNode for DirectBeaconNode { } impl BeaconBlockNode for DirectBeaconNode { - /// Requests the `proposer_nonce` from the `BeaconChain`. - fn proposer_nonce(&self, pubkey: &PublicKey) -> Result { - let validator_index = self - .beacon_chain - .validator_index(pubkey) - .ok_or_else(|| BeaconBlockNodeError::RemoteFailure("pubkey unknown.".to_string()))?; - - self.beacon_chain - .proposer_slots(validator_index) - .ok_or_else(|| { - BeaconBlockNodeError::RemoteFailure("validator_index unknown.".to_string()) - }) - } - /// Requests a new `BeaconBlock from the `BeaconChain`. fn produce_beacon_block( &self, From 9b14742e36beb7290c3137f90963660026b51974 Mon Sep 17 00:00:00 2001 From: Paul Hauner Date: Tue, 12 Feb 2019 16:58:31 +1100 Subject: [PATCH 16/27] Update beacon_node bin to spec v0.2.0 --- beacon_node/src/main.rs | 49 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 44 insertions(+), 5 deletions(-) diff --git a/beacon_node/src/main.rs b/beacon_node/src/main.rs index 25239a9f6..4b5984b13 100644 --- a/beacon_node/src/main.rs +++ b/beacon_node/src/main.rs @@ -8,6 +8,7 @@ use std::path::PathBuf; use crate::config::LighthouseConfig; use crate::rpc::start_server; use beacon_chain::BeaconChain; +use bls::create_proof_of_possession; use clap::{App, Arg}; use db::{ stores::{BeaconBlockStore, BeaconStateStore}, @@ -16,7 +17,7 @@ use db::{ use slog::{error, info, o, Drain}; use slot_clock::SystemTimeSlotClock; use std::sync::Arc; -use types::ChainSpec; +use types::{ChainSpec, Deposit, DepositData, DepositInput, Eth1Data, Hash256, Keypair}; fn main() { let decorator = slog_term::TermDecorator::new().build(); @@ -75,13 +76,51 @@ fn main() { let state_store = Arc::new(BeaconStateStore::new(db.clone())); // Slot clock - let slot_clock = SystemTimeSlotClock::new(spec.genesis_time, spec.slot_duration) + let genesis_time = 1_549_935_547; // 12th Feb 2018 (arbitrary value in the past). + let slot_clock = SystemTimeSlotClock::new(genesis_time, spec.slot_duration) .expect("Unable to load SystemTimeSlotClock"); + /* + * Generate some random data to start a chain with. + * + * This is will need to be replace for production usage. + */ + let latest_eth1_data = Eth1Data { + deposit_root: Hash256::zero(), + block_hash: Hash256::zero(), + }; + let keypairs: Vec = (0..10) + .collect::>() + .iter() + .map(|_| Keypair::random()) + .collect(); + let initial_validator_deposits = keypairs + .iter() + .map(|keypair| Deposit { + branch: vec![], // branch verification is not specified. + index: 0, // index verification is not specified. + deposit_data: DepositData { + amount: 32_000_000_000, // 32 ETH (in Gwei) + timestamp: genesis_time - 1, + deposit_input: DepositInput { + pubkey: keypair.pk.clone(), + withdrawal_credentials: Hash256::zero(), // Withdrawal not possible. + proof_of_possession: create_proof_of_possession(&keypair), + }, + }, + }) + .collect(); + // Genesis chain - // TODO: persist chain to storage. - let _chain_result = - BeaconChain::genesis(state_store.clone(), block_store.clone(), slot_clock, spec); + let _chain_result = BeaconChain::genesis( + state_store.clone(), + block_store.clone(), + slot_clock, + genesis_time, + latest_eth1_data, + initial_validator_deposits, + spec, + ); let _server = start_server(log.clone()); From fef22c31be609f784f3067067ede7e441286c7fe Mon Sep 17 00:00:00 2001 From: Paul Hauner Date: Tue, 12 Feb 2019 17:00:10 +1100 Subject: [PATCH 17/27] Update block_producer to spec v0.2.0 --- eth2/block_producer/src/lib.rs | 7 ++----- .../src/test_utils/simulated_beacon_node.rs | 18 +----------------- eth2/block_producer/src/traits.rs | 5 +---- 3 files changed, 4 insertions(+), 26 deletions(-) diff --git a/eth2/block_producer/src/lib.rs b/eth2/block_producer/src/lib.rs index 0dca583b2..20cd50d9c 100644 --- a/eth2/block_producer/src/lib.rs +++ b/eth2/block_producer/src/lib.rs @@ -134,10 +134,8 @@ impl BlockProducer Result { let randao_reveal = { - let producer_nonce = self.beacon_node.proposer_nonce(&self.pubkey)?; - - // TODO: add domain, etc to this message. - let message = ssz_encode(&producer_nonce); + // TODO: add domain, etc to this message. Also ensure result matches `into_to_bytes32`. + let message = ssz_encode(&slot.epoch(self.spec.epoch_length)); match self.signer.sign_randao_reveal(&message) { None => return Ok(PollOutcome::SignerRejection(slot)), @@ -254,7 +252,6 @@ mod tests { // Configure responses from the BeaconNode. beacon_node.set_next_produce_result(Ok(Some(BeaconBlock::random_for_test(&mut rng)))); beacon_node.set_next_publish_result(Ok(PublishOutcome::ValidBlock)); - beacon_node.set_next_nonce_result(Ok(0)); // One slot before production slot... slot_clock.set_slot(produce_slot.as_u64() - 1); diff --git a/eth2/block_producer/src/test_utils/simulated_beacon_node.rs b/eth2/block_producer/src/test_utils/simulated_beacon_node.rs index 39d03dbe7..ec2695d56 100644 --- a/eth2/block_producer/src/test_utils/simulated_beacon_node.rs +++ b/eth2/block_producer/src/test_utils/simulated_beacon_node.rs @@ -1,6 +1,6 @@ use crate::traits::{BeaconNode, BeaconNodeError, PublishOutcome}; use std::sync::RwLock; -use types::{BeaconBlock, PublicKey, Signature, Slot}; +use types::{BeaconBlock, Signature, Slot}; type NonceResult = Result; type ProduceResult = Result, BeaconNodeError>; @@ -9,9 +9,6 @@ type PublishResult = Result; /// A test-only struct used to simulate a Beacon Node. #[derive(Default)] pub struct SimulatedBeaconNode { - pub nonce_input: RwLock>, - pub nonce_result: RwLock>, - pub produce_input: RwLock>, pub produce_result: RwLock>, @@ -20,11 +17,6 @@ pub struct SimulatedBeaconNode { } impl SimulatedBeaconNode { - /// Set the result to be returned when `produce_beacon_block` is called. - pub fn set_next_nonce_result(&self, result: NonceResult) { - *self.nonce_result.write().unwrap() = Some(result); - } - /// Set the result to be returned when `produce_beacon_block` is called. pub fn set_next_produce_result(&self, result: ProduceResult) { *self.produce_result.write().unwrap() = Some(result); @@ -37,14 +29,6 @@ impl SimulatedBeaconNode { } impl BeaconNode for SimulatedBeaconNode { - fn proposer_nonce(&self, pubkey: &PublicKey) -> NonceResult { - *self.nonce_input.write().unwrap() = Some(pubkey.clone()); - match *self.nonce_result.read().unwrap() { - Some(ref r) => r.clone(), - None => panic!("SimulatedBeaconNode: nonce_result == None"), - } - } - /// Returns the value specified by the `set_next_produce_result`. fn produce_beacon_block(&self, slot: Slot, randao_reveal: &Signature) -> ProduceResult { *self.produce_input.write().unwrap() = Some((slot, randao_reveal.clone())); diff --git a/eth2/block_producer/src/traits.rs b/eth2/block_producer/src/traits.rs index 5467bee87..5eb27bce7 100644 --- a/eth2/block_producer/src/traits.rs +++ b/eth2/block_producer/src/traits.rs @@ -1,4 +1,4 @@ -use types::{BeaconBlock, PublicKey, Signature, Slot}; +use types::{BeaconBlock, Signature, Slot}; #[derive(Debug, PartialEq, Clone)] pub enum BeaconNodeError { @@ -14,9 +14,6 @@ pub enum PublishOutcome { /// Defines the methods required to produce and publish blocks on a Beacon Node. pub trait BeaconNode: Send + Sync { - /// Requests the proposer nonce (presently named `proposer_slots`). - fn proposer_nonce(&self, pubkey: &PublicKey) -> Result; - /// Request that the node produces a block. /// /// Returns Ok(None) if the Beacon Node is unable to produce at the given slot. From 606d927a84523873640d29647afbc06aafa5f4fd Mon Sep 17 00:00:00 2001 From: Paul Hauner Date: Tue, 12 Feb 2019 17:00:51 +1100 Subject: [PATCH 18/27] Update validator_client for spec v0.2.0 --- .../src/block_producer_service/beacon_block_grpc_client.rs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/validator_client/src/block_producer_service/beacon_block_grpc_client.rs b/validator_client/src/block_producer_service/beacon_block_grpc_client.rs index c1aee86e5..6e038ae8f 100644 --- a/validator_client/src/block_producer_service/beacon_block_grpc_client.rs +++ b/validator_client/src/block_producer_service/beacon_block_grpc_client.rs @@ -20,12 +20,6 @@ impl BeaconBlockGrpcClient { } impl BeaconNode for BeaconBlockGrpcClient { - fn proposer_nonce(&self, pubkey: &PublicKey) -> Result { - // TODO: this might not be required. - // - // See: https://github.com/ethereum/eth2.0-specs/pull/496 - panic!("Not implemented.") - } /// Request a Beacon Node (BN) to produce a new block at the supplied slot. /// /// Returns `None` if it is not possible to produce at the supplied slot. For example, if the From 5039001ebaa7eacecbd0cf47b3a0af733dc09e1e Mon Sep 17 00:00:00 2001 From: Paul Hauner Date: Tue, 12 Feb 2019 17:20:58 +1100 Subject: [PATCH 19/27] Rename file to resolve clippy lint Lint was "module inception" --- .../test_harness/src/validator_harness/mod.rs | 135 +++++++++++++++++- .../validator_harness/validator_harness.rs | 133 ----------------- 2 files changed, 133 insertions(+), 135 deletions(-) delete mode 100644 beacon_node/beacon_chain/test_harness/src/validator_harness/validator_harness.rs diff --git a/beacon_node/beacon_chain/test_harness/src/validator_harness/mod.rs b/beacon_node/beacon_chain/test_harness/src/validator_harness/mod.rs index 837334ade..04b29f899 100644 --- a/beacon_node/beacon_chain/test_harness/src/validator_harness/mod.rs +++ b/beacon_node/beacon_chain/test_harness/src/validator_harness/mod.rs @@ -1,6 +1,137 @@ mod direct_beacon_node; mod direct_duties; mod local_signer; -mod validator_harness; -pub use self::validator_harness::ValidatorHarness; +use attester::PollOutcome as AttestationPollOutcome; +use attester::{Attester, Error as AttestationPollError}; +use beacon_chain::BeaconChain; +use block_producer::PollOutcome as BlockPollOutcome; +use block_producer::{BlockProducer, Error as BlockPollError}; +use db::MemoryDB; +use direct_beacon_node::DirectBeaconNode; +use direct_duties::DirectDuties; +use local_signer::LocalSigner; +use slot_clock::TestingSlotClock; +use std::sync::Arc; +use types::{BeaconBlock, ChainSpec, FreeAttestation, Keypair, Slot}; + +#[derive(Debug, PartialEq)] +pub enum BlockProduceError { + DidNotProduce(BlockPollOutcome), + PollError(BlockPollError), +} + +#[derive(Debug, PartialEq)] +pub enum AttestationProduceError { + DidNotProduce(AttestationPollOutcome), + PollError(AttestationPollError), +} + +/// A `BlockProducer` and `Attester` which sign using a common keypair. +/// +/// The test validator connects directly to a borrowed `BeaconChain` struct. It is useful for +/// testing that the core proposer and attester logic is functioning. Also for supporting beacon +/// chain tests. +pub struct ValidatorHarness { + pub block_producer: BlockProducer< + TestingSlotClock, + DirectBeaconNode, + DirectDuties, + LocalSigner, + >, + pub attester: Attester< + TestingSlotClock, + DirectBeaconNode, + DirectDuties, + LocalSigner, + >, + pub spec: Arc, + pub epoch_map: Arc>, + pub keypair: Keypair, + pub beacon_node: Arc>, + pub slot_clock: Arc, + pub signer: Arc, +} + +impl ValidatorHarness { + /// Create a new ValidatorHarness that signs with the given keypair, operates per the given spec and connects to the + /// supplied beacon node. + /// + /// A `BlockProducer` and `Attester` is created.. + pub fn new( + keypair: Keypair, + beacon_chain: Arc>, + spec: Arc, + ) -> Self { + let slot_clock = Arc::new(TestingSlotClock::new(spec.genesis_slot.as_u64())); + let signer = Arc::new(LocalSigner::new(keypair.clone())); + let beacon_node = Arc::new(DirectBeaconNode::new(beacon_chain.clone())); + let epoch_map = Arc::new(DirectDuties::new(keypair.pk.clone(), beacon_chain.clone())); + + let block_producer = BlockProducer::new( + spec.clone(), + keypair.pk.clone(), + epoch_map.clone(), + slot_clock.clone(), + beacon_node.clone(), + signer.clone(), + ); + + let attester = Attester::new( + epoch_map.clone(), + slot_clock.clone(), + beacon_node.clone(), + signer.clone(), + ); + + Self { + block_producer, + attester, + spec, + epoch_map, + keypair, + beacon_node, + slot_clock, + signer, + } + } + + /// Run the `poll` function on the `BlockProducer` and produce a block. + /// + /// An error is returned if the producer refuses to produce. + pub fn produce_block(&mut self) -> Result { + // Using `DirectBeaconNode`, the validator will always return sucessufully if it tries to + // publish a block. + match self.block_producer.poll() { + Ok(BlockPollOutcome::BlockProduced(_)) => {} + Ok(outcome) => return Err(BlockProduceError::DidNotProduce(outcome)), + Err(error) => return Err(BlockProduceError::PollError(error)), + }; + Ok(self + .beacon_node + .last_published_block() + .expect("Unable to obtain produced block.")) + } + + /// Run the `poll` function on the `Attester` and produce a `FreeAttestation`. + /// + /// An error is returned if the attester refuses to attest. + pub fn produce_free_attestation(&mut self) -> Result { + match self.attester.poll() { + Ok(AttestationPollOutcome::AttestationProduced(_)) => {} + Ok(outcome) => return Err(AttestationProduceError::DidNotProduce(outcome)), + Err(error) => return Err(AttestationProduceError::PollError(error)), + }; + Ok(self + .beacon_node + .last_published_free_attestation() + .expect("Unable to obtain produced attestation.")) + } + + /// Set the validators slot clock to the specified slot. + /// + /// The validators slot clock will always read this value until it is set to something else. + pub fn set_slot(&mut self, slot: Slot) { + self.slot_clock.set_slot(slot.as_u64()) + } +} diff --git a/beacon_node/beacon_chain/test_harness/src/validator_harness/validator_harness.rs b/beacon_node/beacon_chain/test_harness/src/validator_harness/validator_harness.rs deleted file mode 100644 index 5ffcc5a4a..000000000 --- a/beacon_node/beacon_chain/test_harness/src/validator_harness/validator_harness.rs +++ /dev/null @@ -1,133 +0,0 @@ -use super::direct_beacon_node::DirectBeaconNode; -use super::direct_duties::DirectDuties; -use super::local_signer::LocalSigner; -use attester::PollOutcome as AttestationPollOutcome; -use attester::{Attester, Error as AttestationPollError}; -use beacon_chain::BeaconChain; -use block_producer::PollOutcome as BlockPollOutcome; -use block_producer::{BlockProducer, Error as BlockPollError}; -use db::MemoryDB; -use slot_clock::TestingSlotClock; -use std::sync::Arc; -use types::{BeaconBlock, ChainSpec, FreeAttestation, Keypair, Slot}; - -#[derive(Debug, PartialEq)] -pub enum BlockProduceError { - DidNotProduce(BlockPollOutcome), - PollError(BlockPollError), -} - -#[derive(Debug, PartialEq)] -pub enum AttestationProduceError { - DidNotProduce(AttestationPollOutcome), - PollError(AttestationPollError), -} - -/// A `BlockProducer` and `Attester` which sign using a common keypair. -/// -/// The test validator connects directly to a borrowed `BeaconChain` struct. It is useful for -/// testing that the core proposer and attester logic is functioning. Also for supporting beacon -/// chain tests. -pub struct ValidatorHarness { - pub block_producer: BlockProducer< - TestingSlotClock, - DirectBeaconNode, - DirectDuties, - LocalSigner, - >, - pub attester: Attester< - TestingSlotClock, - DirectBeaconNode, - DirectDuties, - LocalSigner, - >, - pub spec: Arc, - pub epoch_map: Arc>, - pub keypair: Keypair, - pub beacon_node: Arc>, - pub slot_clock: Arc, - pub signer: Arc, -} - -impl ValidatorHarness { - /// Create a new ValidatorHarness that signs with the given keypair, operates per the given spec and connects to the - /// supplied beacon node. - /// - /// A `BlockProducer` and `Attester` is created.. - pub fn new( - keypair: Keypair, - beacon_chain: Arc>, - spec: Arc, - ) -> Self { - let slot_clock = Arc::new(TestingSlotClock::new(spec.genesis_slot.as_u64())); - let signer = Arc::new(LocalSigner::new(keypair.clone())); - let beacon_node = Arc::new(DirectBeaconNode::new(beacon_chain.clone())); - let epoch_map = Arc::new(DirectDuties::new(keypair.pk.clone(), beacon_chain.clone())); - - let block_producer = BlockProducer::new( - spec.clone(), - keypair.pk.clone(), - epoch_map.clone(), - slot_clock.clone(), - beacon_node.clone(), - signer.clone(), - ); - - let attester = Attester::new( - epoch_map.clone(), - slot_clock.clone(), - beacon_node.clone(), - signer.clone(), - ); - - Self { - block_producer, - attester, - spec, - epoch_map, - keypair, - beacon_node, - slot_clock, - signer, - } - } - - /// Run the `poll` function on the `BlockProducer` and produce a block. - /// - /// An error is returned if the producer refuses to produce. - pub fn produce_block(&mut self) -> Result { - // Using `DirectBeaconNode`, the validator will always return sucessufully if it tries to - // publish a block. - match self.block_producer.poll() { - Ok(BlockPollOutcome::BlockProduced(_)) => {} - Ok(outcome) => return Err(BlockProduceError::DidNotProduce(outcome)), - Err(error) => return Err(BlockProduceError::PollError(error)), - }; - Ok(self - .beacon_node - .last_published_block() - .expect("Unable to obtain produced block.")) - } - - /// Run the `poll` function on the `Attester` and produce a `FreeAttestation`. - /// - /// An error is returned if the attester refuses to attest. - pub fn produce_free_attestation(&mut self) -> Result { - match self.attester.poll() { - Ok(AttestationPollOutcome::AttestationProduced(_)) => {} - Ok(outcome) => return Err(AttestationProduceError::DidNotProduce(outcome)), - Err(error) => return Err(AttestationProduceError::PollError(error)), - }; - Ok(self - .beacon_node - .last_published_free_attestation() - .expect("Unable to obtain produced attestation.")) - } - - /// Set the validators slot clock to the specified slot. - /// - /// The validators slot clock will always read this value until it is set to something else. - pub fn set_slot(&mut self, slot: Slot) { - self.slot_clock.set_slot(slot.as_u64()) - } -} From 5c1d0dcea51e26e12e4caeacda4d7cc0388a9992 Mon Sep 17 00:00:00 2001 From: Paul Hauner Date: Wed, 13 Feb 2019 07:32:31 +1100 Subject: [PATCH 20/27] Fix various clippy lints --- .../test_harness/src/beacon_chain_harness.rs | 5 +- .../validator_harness/direct_beacon_node.rs | 2 +- .../src/validator_harness/direct_duties.rs | 2 +- beacon_node/src/rpc/beacon_block.rs | 2 +- .../src/test_utils/simulated_beacon_node.rs | 1 - .../state_processing/src/block_processable.rs | 2 +- .../state_processing/src/epoch_processable.rs | 31 ++++------ eth2/state_processing/src/slot_processable.rs | 2 - eth2/types/src/attestation.rs | 1 - eth2/types/src/beacon_state.rs | 16 ++---- eth2/types/src/slot_epoch.rs | 26 +++++++-- eth2/types/src/validator.rs | 4 +- .../beacon_block_grpc_client.rs | 5 +- .../block_producer_service.rs | 53 ----------------- .../src/block_producer_service/mod.rs | 57 ++++++++++++++++++- validator_client/src/duties/epoch_duties.rs | 2 +- validator_client/src/duties/mod.rs | 4 +- 17 files changed, 105 insertions(+), 110 deletions(-) delete mode 100644 validator_client/src/block_producer_service/block_producer_service.rs diff --git a/beacon_node/beacon_chain/test_harness/src/beacon_chain_harness.rs b/beacon_node/beacon_chain/test_harness/src/beacon_chain_harness.rs index 09080c60d..f551c94c2 100644 --- a/beacon_node/beacon_chain/test_harness/src/beacon_chain_harness.rs +++ b/beacon_node/beacon_chain/test_harness/src/beacon_chain_harness.rs @@ -61,8 +61,7 @@ impl BeaconChainHarness { debug!("Creating validator deposits..."); - let mut initial_validator_deposits = Vec::with_capacity(validator_count); - initial_validator_deposits = keypairs + let initial_validator_deposits = keypairs .par_iter() .map(|keypair| Deposit { branch: vec![], // branch verification is not specified. @@ -235,7 +234,7 @@ impl BeaconChainHarness { } /// Write the output of `chain_dump` to a JSON file. - pub fn dump_to_file(&self, filename: String, chain_dump: &Vec) { + pub fn dump_to_file(&self, filename: String, chain_dump: &[CheckPoint]) { let json = serde_json::to_string(chain_dump).unwrap(); let mut file = File::create(filename).unwrap(); file.write_all(json.as_bytes()) diff --git a/beacon_node/beacon_chain/test_harness/src/validator_harness/direct_beacon_node.rs b/beacon_node/beacon_chain/test_harness/src/validator_harness/direct_beacon_node.rs index 79d2cbc83..7c822ced0 100644 --- a/beacon_node/beacon_chain/test_harness/src/validator_harness/direct_beacon_node.rs +++ b/beacon_node/beacon_chain/test_harness/src/validator_harness/direct_beacon_node.rs @@ -80,7 +80,7 @@ impl BeaconBlockNode for DirectBeaconNode { .beacon_chain .produce_block(randao_reveal.clone()) .ok_or_else(|| { - BeaconBlockNodeError::RemoteFailure(format!("Did not produce block.")) + BeaconBlockNodeError::RemoteFailure("Did not produce block.".to_string()) })?; if block.slot == slot { diff --git a/beacon_node/beacon_chain/test_harness/src/validator_harness/direct_duties.rs b/beacon_node/beacon_chain/test_harness/src/validator_harness/direct_duties.rs index eac56679b..cabbbd8a7 100644 --- a/beacon_node/beacon_chain/test_harness/src/validator_harness/direct_duties.rs +++ b/beacon_node/beacon_chain/test_harness/src/validator_harness/direct_duties.rs @@ -60,7 +60,7 @@ impl AttesterDutiesReader for DirectDuties { } Ok(Some(_)) => Ok(None), Ok(None) => Err(AttesterDutiesReaderError::UnknownEpoch), - Err(_) => panic!("Error when getting validator attestation shard."), + Err(_) => unreachable!("Error when getting validator attestation shard."), } } else { Err(AttesterDutiesReaderError::UnknownValidator) diff --git a/beacon_node/src/rpc/beacon_block.rs b/beacon_node/src/rpc/beacon_block.rs index a047365ef..96f64e0dd 100644 --- a/beacon_node/src/rpc/beacon_block.rs +++ b/beacon_node/src/rpc/beacon_block.rs @@ -25,7 +25,7 @@ impl BeaconBlockService for BeaconBlockServiceInstance { // TODO: build a legit block. let mut block = BeaconBlockProto::new(); block.set_slot(req.get_slot()); - block.set_block_root("cats".as_bytes().to_vec()); + block.set_block_root(b"cats".to_vec()); let mut resp = ProduceBeaconBlockResponse::new(); resp.set_block(block); diff --git a/eth2/block_producer/src/test_utils/simulated_beacon_node.rs b/eth2/block_producer/src/test_utils/simulated_beacon_node.rs index ec2695d56..c0a12c1ac 100644 --- a/eth2/block_producer/src/test_utils/simulated_beacon_node.rs +++ b/eth2/block_producer/src/test_utils/simulated_beacon_node.rs @@ -2,7 +2,6 @@ use crate::traits::{BeaconNode, BeaconNodeError, PublishOutcome}; use std::sync::RwLock; use types::{BeaconBlock, Signature, Slot}; -type NonceResult = Result; type ProduceResult = Result, BeaconNodeError>; type PublishResult = Result; diff --git a/eth2/state_processing/src/block_processable.rs b/eth2/state_processing/src/block_processable.rs index 3750529cd..76c87698a 100644 --- a/eth2/state_processing/src/block_processable.rs +++ b/eth2/state_processing/src/block_processable.rs @@ -333,7 +333,7 @@ fn validate_attestation_signature_optional( ensure!( (attestation.data.latest_crosslink == state.latest_crosslinks[attestation.data.shard as usize]) - || (attestation.data.latest_crosslink + | (attestation.data.latest_crosslink == state.latest_crosslinks[attestation.data.shard as usize]), AttestationValidationError::BadLatestCrosslinkRoot ); diff --git a/eth2/state_processing/src/epoch_processable.rs b/eth2/state_processing/src/epoch_processable.rs index 3bc0d7cb9..4afa725ce 100644 --- a/eth2/state_processing/src/epoch_processable.rs +++ b/eth2/state_processing/src/epoch_processable.rs @@ -226,7 +226,7 @@ impl EpochProcessable for BeaconState { */ let mut new_justified_epoch = self.justified_epoch; - self.justification_bitfield = self.justification_bitfield << 1; + self.justification_bitfield <<= 1; // If > 2/3 of the total balance attested to the previous epoch boundary // @@ -277,8 +277,7 @@ impl EpochProcessable for BeaconState { // - The presently justified epoch was two epochs ago. // // Then, set the finalized epoch to two epochs ago. - if ((self.justification_bitfield >> 0) % 8 == 0b111) - & (self.justified_epoch == previous_epoch - 1) + if (self.justification_bitfield % 8 == 0b111) & (self.justified_epoch == previous_epoch - 1) { self.finalized_epoch = self.justified_epoch; trace!("epoch - 2 was finalized (3rd condition)."); @@ -289,9 +288,7 @@ impl EpochProcessable for BeaconState { // - Set the previous epoch to be justified. // // Then, set the finalized epoch to be the previous epoch. - if ((self.justification_bitfield >> 0) % 4 == 0b11) - & (self.justified_epoch == previous_epoch) - { + if (self.justification_bitfield % 4 == 0b11) & (self.justified_epoch == previous_epoch) { self.finalized_epoch = self.justified_epoch; trace!("epoch - 1 was finalized (4th condition)."); } @@ -386,10 +383,8 @@ impl EpochProcessable for BeaconState { base_reward * previous_epoch_justified_attesting_balance / previous_total_balance ); - } else { - if active_validator_indices_hashset.contains(&index) { - safe_sub_assign!(self.validator_balances[index], base_reward); - } + } else if active_validator_indices_hashset.contains(&index) { + safe_sub_assign!(self.validator_balances[index], base_reward); } if previous_epoch_boundary_attester_indices_hashset.contains(&index) { @@ -398,10 +393,8 @@ impl EpochProcessable for BeaconState { base_reward * previous_epoch_boundary_attesting_balance / previous_total_balance ); - } else { - if active_validator_indices_hashset.contains(&index) { - safe_sub_assign!(self.validator_balances[index], base_reward); - } + } else if active_validator_indices_hashset.contains(&index) { + safe_sub_assign!(self.validator_balances[index], base_reward); } if previous_epoch_head_attester_indices_hashset.contains(&index) { @@ -410,10 +403,8 @@ impl EpochProcessable for BeaconState { base_reward * previous_epoch_head_attesting_balance / previous_total_balance ); - } else { - if active_validator_indices_hashset.contains(&index) { - safe_sub_assign!(self.validator_balances[index], base_reward); - } + } else if active_validator_indices_hashset.contains(&index) { + safe_sub_assign!(self.validator_balances[index], base_reward); } } @@ -505,7 +496,7 @@ impl EpochProcessable for BeaconState { 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), + winning_root.attesting_validator_indices.iter().cloned(), ); for index in 0..self.validator_balances.len() { @@ -664,7 +655,7 @@ fn winning_root( } let candidate_root = WinningRoot { - shard_block_root: shard_block_root.clone(), + shard_block_root: *shard_block_root, attesting_validator_indices, total_attesting_balance, total_balance, diff --git a/eth2/state_processing/src/slot_processable.rs b/eth2/state_processing/src/slot_processable.rs index f0e776f57..7726c5071 100644 --- a/eth2/state_processing/src/slot_processable.rs +++ b/eth2/state_processing/src/slot_processable.rs @@ -30,8 +30,6 @@ where self.slot += 1; - let block_proposer = self.get_beacon_proposer_index(self.slot, spec)?; - self.latest_randao_mixes[self.slot.as_usize() % spec.latest_randao_mixes_length] = self.latest_randao_mixes[(self.slot.as_usize() - 1) % spec.latest_randao_mixes_length]; diff --git a/eth2/types/src/attestation.rs b/eth2/types/src/attestation.rs index afffc2e2f..eb375d490 100644 --- a/eth2/types/src/attestation.rs +++ b/eth2/types/src/attestation.rs @@ -1,6 +1,5 @@ use super::{AggregatePublicKey, AggregateSignature, AttestationData, Bitfield, Hash256}; use crate::test_utils::TestRandom; -use bls::bls_verify_aggregate; use rand::RngCore; use serde_derive::Serialize; use ssz::{hash, Decodable, DecodeError, Encodable, SszStream, TreeHash}; diff --git a/eth2/types/src/beacon_state.rs b/eth2/types/src/beacon_state.rs index e11b43e80..ed53bfea9 100644 --- a/eth2/types/src/beacon_state.rs +++ b/eth2/types/src/beacon_state.rs @@ -283,7 +283,7 @@ impl BeaconState { shuffled_active_validator_indices .honey_badger_split(committees_per_epoch as usize) - .filter_map(|slice: &[usize]| Some(slice.to_vec())) + .map(|slice: &[usize]| slice.to_vec()) .collect() } @@ -522,11 +522,9 @@ impl BeaconState { .filter(|i| eligible(*i)) .collect(); 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); - withdrawn_so_far += 1; - if withdrawn_so_far >= spec.max_withdrawals_per_epoch { + for (withdrawn_so_far, index) in eligable_indices.iter().enumerate() { + self.prepare_validator_for_withdrawal(*index); + if withdrawn_so_far as u64 >= spec.max_withdrawals_per_epoch { break; } } @@ -796,11 +794,7 @@ impl BeaconState { for (i, a) in attestations.iter().enumerate() { let participants = self.get_attestation_participants(&a.data, &a.aggregation_bitfield, spec)?; - if participants - .iter() - .find(|i| **i == validator_index) - .is_some() - { + if participants.iter().any(|i| *i == validator_index) { included_attestations.push(i); } } diff --git a/eth2/types/src/slot_epoch.rs b/eth2/types/src/slot_epoch.rs index a810bb102..fb4f8d942 100644 --- a/eth2/types/src/slot_epoch.rs +++ b/eth2/types/src/slot_epoch.rs @@ -13,9 +13,10 @@ use crate::test_utils::TestRandom; use rand::RngCore; use serde_derive::Serialize; use slog; -use ssz::{hash, Decodable, DecodeError, Encodable, SszStream, TreeHash}; +use ssz::{hash, ssz_encode, Decodable, DecodeError, Encodable, SszStream, TreeHash}; use std::cmp::{Ord, Ordering}; use std::fmt; +use std::hash::{Hash, Hasher}; use std::iter::Iterator; use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Rem, Sub, SubAssign}; @@ -243,6 +244,18 @@ macro_rules! impl_ssz { }; } +macro_rules! impl_hash { + ($type: ident) => { + // Implemented to stop clippy lint: + // https://rust-lang.github.io/rust-clippy/master/index.html#derive_hash_xor_eq + impl Hash for $type { + fn hash(&self, state: &mut H) { + ssz_encode(self).hash(state) + } + } + }; +} + macro_rules! impl_common { ($type: ident) => { impl_from_into_u64!($type); @@ -252,13 +265,14 @@ macro_rules! impl_common { impl_math!($type); impl_display!($type); impl_ssz!($type); + impl_hash!($type); }; } -#[derive(Eq, Debug, Clone, Copy, Default, Serialize, Hash)] +#[derive(Eq, Debug, Clone, Copy, Default, Serialize)] pub struct Slot(u64); -#[derive(Eq, Debug, Clone, Copy, Default, Serialize, Hash)] +#[derive(Eq, Debug, Clone, Copy, Default, Serialize)] pub struct Epoch(u64); impl_common!(Slot); @@ -269,7 +283,7 @@ impl Slot { Slot(slot) } - pub fn epoch(&self, epoch_length: u64) -> Epoch { + pub fn epoch(self, epoch_length: u64) -> Epoch { Epoch::from(self.0 / epoch_length) } @@ -287,11 +301,11 @@ impl Epoch { Epoch(u64::max_value()) } - pub fn start_slot(&self, epoch_length: u64) -> Slot { + pub fn start_slot(self, epoch_length: u64) -> Slot { Slot::from(self.0.saturating_mul(epoch_length)) } - pub fn end_slot(&self, epoch_length: u64) -> Slot { + pub fn end_slot(self, epoch_length: u64) -> Slot { Slot::from( self.0 .saturating_add(1) diff --git a/eth2/types/src/validator.rs b/eth2/types/src/validator.rs index 50eb65486..047817a86 100644 --- a/eth2/types/src/validator.rs +++ b/eth2/types/src/validator.rs @@ -78,7 +78,7 @@ impl Default for Validator { impl TestRandom for StatusFlags { fn random_for_test(rng: &mut T) -> Self { let options = vec![StatusFlags::InitiatedExit, StatusFlags::Withdrawable]; - options[(rng.next_u32() as usize) % options.len()].clone() + options[(rng.next_u32() as usize) % options.len()] } } @@ -130,7 +130,7 @@ impl TreeHash for Validator { 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 u64::from(status_flag_to_byte(self.status_flags)).hash_tree_root()); hash(&result) } } diff --git a/validator_client/src/block_producer_service/beacon_block_grpc_client.rs b/validator_client/src/block_producer_service/beacon_block_grpc_client.rs index 6e038ae8f..39ef7fcdc 100644 --- a/validator_client/src/block_producer_service/beacon_block_grpc_client.rs +++ b/validator_client/src/block_producer_service/beacon_block_grpc_client.rs @@ -5,7 +5,7 @@ use protos::services::{ use protos::services_grpc::BeaconBlockServiceClient; use ssz::{ssz_encode, Decodable}; use std::sync::Arc; -use types::{BeaconBlock, BeaconBlockBody, Eth1Data, Hash256, PublicKey, Signature, Slot}; +use types::{BeaconBlock, BeaconBlockBody, Eth1Data, Hash256, Signature, Slot}; /// A newtype designed to wrap the gRPC-generated service so the `BeaconNode` trait may be /// implemented upon it. @@ -27,7 +27,8 @@ impl BeaconNode for BeaconBlockGrpcClient { fn produce_beacon_block( &self, slot: Slot, - randao_reveal: &Signature, + // TODO: use randao_reveal, when proto APIs have been updated. + _randao_reveal: &Signature, ) -> Result, BeaconNodeError> { let mut req = ProduceBeaconBlockRequest::new(); req.set_slot(slot.as_u64()); diff --git a/validator_client/src/block_producer_service/block_producer_service.rs b/validator_client/src/block_producer_service/block_producer_service.rs deleted file mode 100644 index 5e335e383..000000000 --- a/validator_client/src/block_producer_service/block_producer_service.rs +++ /dev/null @@ -1,53 +0,0 @@ -use block_producer::{ - BeaconNode, BlockProducer, DutiesReader, PollOutcome as BlockProducerPollOutcome, Signer, -}; -use slog::{error, info, warn, Logger}; -use slot_clock::SlotClock; -use std::time::Duration; - -pub struct BlockProducerService { - pub block_producer: BlockProducer, - pub poll_interval_millis: u64, - pub log: Logger, -} - -impl BlockProducerService { - /// Run a loop which polls the block producer each `poll_interval_millis` millseconds. - /// - /// Logs the results of the polls. - pub fn run(&mut self) { - loop { - match self.block_producer.poll() { - Err(error) => { - error!(self.log, "Block producer poll error"; "error" => format!("{:?}", error)) - } - Ok(BlockProducerPollOutcome::BlockProduced(slot)) => { - info!(self.log, "Produced block"; "slot" => slot) - } - Ok(BlockProducerPollOutcome::SlashableBlockNotProduced(slot)) => { - warn!(self.log, "Slashable block was not signed"; "slot" => slot) - } - Ok(BlockProducerPollOutcome::BlockProductionNotRequired(slot)) => { - info!(self.log, "Block production not required"; "slot" => slot) - } - Ok(BlockProducerPollOutcome::ProducerDutiesUnknown(slot)) => { - error!(self.log, "Block production duties unknown"; "slot" => slot) - } - Ok(BlockProducerPollOutcome::SlotAlreadyProcessed(slot)) => { - warn!(self.log, "Attempted to re-process slot"; "slot" => slot) - } - Ok(BlockProducerPollOutcome::BeaconNodeUnableToProduceBlock(slot)) => { - error!(self.log, "Beacon node unable to produce block"; "slot" => slot) - } - Ok(BlockProducerPollOutcome::SignerRejection(slot)) => { - error!(self.log, "The cryptographic signer refused to sign the block"; "slot" => slot) - } - Ok(BlockProducerPollOutcome::ValidatorIsUnknown(slot)) => { - error!(self.log, "The Beacon Node does not recognise the validator"; "slot" => slot) - } - }; - - std::thread::sleep(Duration::from_millis(self.poll_interval_millis)); - } - } -} diff --git a/validator_client/src/block_producer_service/mod.rs b/validator_client/src/block_producer_service/mod.rs index 52aac688b..82c3f2537 100644 --- a/validator_client/src/block_producer_service/mod.rs +++ b/validator_client/src/block_producer_service/mod.rs @@ -1,5 +1,58 @@ mod beacon_block_grpc_client; -mod block_producer_service; +// mod block_producer_service; + +use block_producer::{ + BeaconNode, BlockProducer, DutiesReader, PollOutcome as BlockProducerPollOutcome, Signer, +}; +use slog::{error, info, warn, Logger}; +use slot_clock::SlotClock; +use std::time::Duration; pub use self::beacon_block_grpc_client::BeaconBlockGrpcClient; -pub use self::block_producer_service::BlockProducerService; + +pub struct BlockProducerService { + pub block_producer: BlockProducer, + pub poll_interval_millis: u64, + pub log: Logger, +} + +impl BlockProducerService { + /// Run a loop which polls the block producer each `poll_interval_millis` millseconds. + /// + /// Logs the results of the polls. + pub fn run(&mut self) { + loop { + match self.block_producer.poll() { + Err(error) => { + error!(self.log, "Block producer poll error"; "error" => format!("{:?}", error)) + } + Ok(BlockProducerPollOutcome::BlockProduced(slot)) => { + info!(self.log, "Produced block"; "slot" => slot) + } + Ok(BlockProducerPollOutcome::SlashableBlockNotProduced(slot)) => { + warn!(self.log, "Slashable block was not signed"; "slot" => slot) + } + Ok(BlockProducerPollOutcome::BlockProductionNotRequired(slot)) => { + info!(self.log, "Block production not required"; "slot" => slot) + } + Ok(BlockProducerPollOutcome::ProducerDutiesUnknown(slot)) => { + error!(self.log, "Block production duties unknown"; "slot" => slot) + } + Ok(BlockProducerPollOutcome::SlotAlreadyProcessed(slot)) => { + warn!(self.log, "Attempted to re-process slot"; "slot" => slot) + } + Ok(BlockProducerPollOutcome::BeaconNodeUnableToProduceBlock(slot)) => { + error!(self.log, "Beacon node unable to produce block"; "slot" => slot) + } + Ok(BlockProducerPollOutcome::SignerRejection(slot)) => { + error!(self.log, "The cryptographic signer refused to sign the block"; "slot" => slot) + } + Ok(BlockProducerPollOutcome::ValidatorIsUnknown(slot)) => { + error!(self.log, "The Beacon Node does not recognise the validator"; "slot" => slot) + } + }; + + std::thread::sleep(Duration::from_millis(self.poll_interval_millis)); + } + } +} diff --git a/validator_client/src/duties/epoch_duties.rs b/validator_client/src/duties/epoch_duties.rs index d93b8ac2f..b555eee28 100644 --- a/validator_client/src/duties/epoch_duties.rs +++ b/validator_client/src/duties/epoch_duties.rs @@ -47,7 +47,7 @@ impl EpochDutiesMap { pub fn get(&self, epoch: Epoch) -> Result, EpochDutiesMapError> { let map = self.map.read().map_err(|_| EpochDutiesMapError::Poisoned)?; match map.get(&epoch) { - Some(duties) => Ok(Some(duties.clone())), + Some(duties) => Ok(Some(*duties)), None => Ok(None), } } diff --git a/validator_client/src/duties/mod.rs b/validator_client/src/duties/mod.rs index 936034aca..febab4755 100644 --- a/validator_client/src/duties/mod.rs +++ b/validator_client/src/duties/mod.rs @@ -12,7 +12,7 @@ use self::traits::{BeaconNode, BeaconNodeError}; use bls::PublicKey; use slot_clock::SlotClock; use std::sync::Arc; -use types::{ChainSpec, Epoch, Slot}; +use types::{ChainSpec, Epoch}; #[derive(Debug, PartialEq, Clone, Copy)] pub enum PollOutcome { @@ -33,7 +33,6 @@ pub enum Error { SlotClockError, SlotUnknowable, EpochMapPoisoned, - EpochLengthIsZero, BeaconNodeError(BeaconNodeError), } @@ -103,6 +102,7 @@ mod tests { use super::*; use bls::Keypair; use slot_clock::TestingSlotClock; + use types::Slot; // TODO: implement more thorough testing. // https://github.com/sigp/lighthouse/issues/160 From 05ed778ccc7bdfc3ffe8b9d718bf6cfd685ada9e Mon Sep 17 00:00:00 2001 From: Paul Hauner Date: Wed, 13 Feb 2019 07:39:00 +1100 Subject: [PATCH 21/27] Remove unused var from `block_producer` It was made redundant when `proposer_slots` was removed. --- .../beacon_chain/test_harness/src/validator_harness/mod.rs | 1 - eth2/block_producer/src/lib.rs | 7 +------ validator_client/src/main.rs | 3 +-- 3 files changed, 2 insertions(+), 9 deletions(-) diff --git a/beacon_node/beacon_chain/test_harness/src/validator_harness/mod.rs b/beacon_node/beacon_chain/test_harness/src/validator_harness/mod.rs index 04b29f899..c84a993a5 100644 --- a/beacon_node/beacon_chain/test_harness/src/validator_harness/mod.rs +++ b/beacon_node/beacon_chain/test_harness/src/validator_harness/mod.rs @@ -70,7 +70,6 @@ impl ValidatorHarness { let block_producer = BlockProducer::new( spec.clone(), - keypair.pk.clone(), epoch_map.clone(), slot_clock.clone(), beacon_node.clone(), diff --git a/eth2/block_producer/src/lib.rs b/eth2/block_producer/src/lib.rs index 20cd50d9c..f6a0fd6df 100644 --- a/eth2/block_producer/src/lib.rs +++ b/eth2/block_producer/src/lib.rs @@ -4,7 +4,7 @@ mod traits; use slot_clock::SlotClock; use ssz::ssz_encode; use std::sync::Arc; -use types::{BeaconBlock, ChainSpec, PublicKey, Slot}; +use types::{BeaconBlock, ChainSpec, Slot}; pub use self::traits::{ BeaconNode, BeaconNodeError, DutiesReader, DutiesReaderError, PublishOutcome, Signer, @@ -48,7 +48,6 @@ pub enum Error { /// Relies upon an external service to keep the `EpochDutiesMap` updated. pub struct BlockProducer { pub last_processed_slot: Option, - pubkey: PublicKey, spec: Arc, epoch_map: Arc, slot_clock: Arc, @@ -60,7 +59,6 @@ impl BlockProducer, - pubkey: PublicKey, epoch_map: Arc, slot_clock: Arc, beacon_node: Arc, @@ -68,7 +66,6 @@ impl BlockProducer Self { Self { last_processed_slot: None, - pubkey, spec, epoch_map, slot_clock, @@ -238,11 +235,9 @@ mod tests { let produce_epoch = produce_slot.epoch(spec.epoch_length); epoch_map.map.insert(produce_epoch, produce_slot); let epoch_map = Arc::new(epoch_map); - let keypair = Keypair::random(); let mut block_producer = BlockProducer::new( spec.clone(), - keypair.pk.clone(), epoch_map.clone(), slot_clock.clone(), beacon_node.clone(), diff --git a/validator_client/src/main.rs b/validator_client/src/main.rs index 8fa49e51c..98be9159a 100644 --- a/validator_client/src/main.rs +++ b/validator_client/src/main.rs @@ -142,7 +142,6 @@ fn main() { // Spawn a new thread to perform block production for the validator. let producer_thread = { let spec = spec.clone(); - let pubkey = keypair.pk.clone(); let signer = Arc::new(LocalSigner::new(keypair.clone())); let duties_map = duties_map.clone(); let slot_clock = slot_clock.clone(); @@ -150,7 +149,7 @@ fn main() { let client = Arc::new(BeaconBlockGrpcClient::new(beacon_block_grpc_client.clone())); thread::spawn(move || { let block_producer = - BlockProducer::new(spec, pubkey, duties_map, slot_clock, client, signer); + BlockProducer::new(spec, duties_map, slot_clock, client, signer); let mut block_producer_service = BlockProducerService { block_producer, poll_interval_millis, From 4824b43808960e1cbc3662cdf1df3d9569139a14 Mon Sep 17 00:00:00 2001 From: Paul Hauner Date: Wed, 13 Feb 2019 07:46:59 +1100 Subject: [PATCH 22/27] Fix various clippy lints --- eth2/state_processing/src/block_processable.rs | 2 +- eth2/utils/boolean-bitfield/src/lib.rs | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/eth2/state_processing/src/block_processable.rs b/eth2/state_processing/src/block_processable.rs index 76c87698a..5f9fe1f7f 100644 --- a/eth2/state_processing/src/block_processable.rs +++ b/eth2/state_processing/src/block_processable.rs @@ -194,7 +194,7 @@ fn per_block_processing_signature_optional( ), Error::BadProposerSlashing ); - state.penalize_validator(proposer_slashing.proposer_index as usize, spec); + state.penalize_validator(proposer_slashing.proposer_index as usize, spec)?; } /* diff --git a/eth2/utils/boolean-bitfield/src/lib.rs b/eth2/utils/boolean-bitfield/src/lib.rs index 16992c3fa..a0fce1f0a 100644 --- a/eth2/utils/boolean-bitfield/src/lib.rs +++ b/eth2/utils/boolean-bitfield/src/lib.rs @@ -81,6 +81,11 @@ impl BooleanBitfield { self.0.len() } + /// Returns true if `self.len() == 0` + pub fn is_empty(&self) -> bool { + self.len() == 0 + } + /// Returns the number of bytes required to represent this bitfield. pub fn num_bytes(&self) -> usize { self.to_bytes().len() From 61bbbab33d879fe2fa9d77a98ec6e71a5222c60d Mon Sep 17 00:00:00 2001 From: Paul Hauner Date: Wed, 13 Feb 2019 09:26:29 +1100 Subject: [PATCH 23/27] Fix clippy lints in lmd_ghost --- beacon_node/beacon_chain/src/lmd_ghost.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/beacon_node/beacon_chain/src/lmd_ghost.rs b/beacon_node/beacon_chain/src/lmd_ghost.rs index 2c6fbe95a..badd89267 100644 --- a/beacon_node/beacon_chain/src/lmd_ghost.rs +++ b/beacon_node/beacon_chain/src/lmd_ghost.rs @@ -57,16 +57,16 @@ where let start = self .block_store .get_reader(&start_hash)? - .ok_or(Error::MissingBeaconBlock(*start_hash))?; + .ok_or_else(|| Error::MissingBeaconBlock(*start_hash))?; let start_state_root = start.state_root(); let state = self .state_store .get_reader(&start_state_root)? - .ok_or(Error::MissingBeaconState(start_state_root))? + .ok_or_else(|| Error::MissingBeaconState(start_state_root))? .into_beacon_state() - .ok_or(Error::InvalidBeaconState(start_state_root))?; + .ok_or_else(|| Error::InvalidBeaconState(start_state_root))?; let active_validator_indices = get_active_validator_indices( &state.validator_registry, @@ -90,7 +90,7 @@ where &self.block_graph.leaves(), )?; - if child_hashes_and_slots.len() == 0 { + if child_hashes_and_slots.is_empty() { break; } @@ -126,7 +126,7 @@ fn get_vote_count( for target in attestation_targets { let (root_at_slot, _) = block_store .block_at_slot(&block_root, slot)? - .ok_or(Error::MissingBeaconBlock(*block_root))?; + .ok_or_else(|| Error::MissingBeaconBlock(*block_root))?; if root_at_slot == *target { count += 1; } @@ -165,7 +165,7 @@ fn get_child_hashes_and_slots( break; } - current_hash = parent_root.clone(); + current_hash = parent_root; } else { return Err(Error::MissingBeaconBlock(current_hash)); } From d2a10005207ff16b7d59b0024cb2e889d49ca4ec Mon Sep 17 00:00:00 2001 From: Paul Hauner Date: Wed, 13 Feb 2019 10:28:38 +1100 Subject: [PATCH 24/27] Fix clippy lints in `beacon_chain` --- .../src/attestation_aggregator.rs | 1 + .../beacon_chain/src/attestation_targets.rs | 1 + beacon_node/beacon_chain/src/beacon_chain.rs | 68 +++++++------------ beacon_node/beacon_chain/src/block_graph.rs | 1 + 4 files changed, 26 insertions(+), 45 deletions(-) diff --git a/beacon_node/beacon_chain/src/attestation_aggregator.rs b/beacon_node/beacon_chain/src/attestation_aggregator.rs index cfba01308..149f0d60d 100644 --- a/beacon_node/beacon_chain/src/attestation_aggregator.rs +++ b/beacon_node/beacon_chain/src/attestation_aggregator.rs @@ -17,6 +17,7 @@ const PHASE_0_CUSTODY_BIT: bool = false; /// /// Note: `Attestations` are stored in memory and never deleted. This is not scalable and must be /// rectified in a future revision. +#[derive(Default)] pub struct AttestationAggregator { store: HashMap, Attestation>, } diff --git a/beacon_node/beacon_chain/src/attestation_targets.rs b/beacon_node/beacon_chain/src/attestation_targets.rs index 6585e4a47..e1259c5f8 100644 --- a/beacon_node/beacon_chain/src/attestation_targets.rs +++ b/beacon_node/beacon_chain/src/attestation_targets.rs @@ -1,6 +1,7 @@ use std::collections::HashMap; use types::Hash256; +#[derive(Default)] pub struct AttestationTargets { map: HashMap, } diff --git a/beacon_node/beacon_chain/src/beacon_chain.rs b/beacon_node/beacon_chain/src/beacon_chain.rs index 55212ab20..6ea2d40de 100644 --- a/beacon_node/beacon_chain/src/beacon_chain.rs +++ b/beacon_node/beacon_chain/src/beacon_chain.rs @@ -67,7 +67,6 @@ pub struct BeaconChain { pub attestation_aggregator: RwLock, canonical_head: RwLock, finalized_head: RwLock, - justified_head: RwLock, pub state: RwLock, pub latest_attestation_targets: RwLock, pub spec: ChainSpec, @@ -106,25 +105,19 @@ where block_store.put(&block_root, &ssz_encode(&genesis_block)[..])?; let block_graph = BlockGraph::new(); - block_graph.add_leaf(&Hash256::zero(), block_root.clone()); + block_graph.add_leaf(&Hash256::zero(), block_root); let finalized_head = RwLock::new(CheckPoint::new( genesis_block.clone(), - block_root.clone(), + block_root, genesis_state.clone(), - state_root.clone(), - )); - let justified_head = RwLock::new(CheckPoint::new( - genesis_block.clone(), - block_root.clone(), - genesis_state.clone(), - state_root.clone(), + state_root, )); let canonical_head = RwLock::new(CheckPoint::new( genesis_block.clone(), - block_root.clone(), + block_root, genesis_state.clone(), - state_root.clone(), + state_root, )); let attestation_aggregator = RwLock::new(AttestationAggregator::new()); @@ -137,11 +130,10 @@ where block_graph, attestation_aggregator, state: RwLock::new(genesis_state.clone()), - justified_head, finalized_head, canonical_head, latest_attestation_targets, - spec: spec, + spec, }) } @@ -211,7 +203,7 @@ where for _ in state_slot.as_u64()..slot.as_u64() { self.state .write() - .per_slot_processing(head_block_root.clone(), &self.spec)?; + .per_slot_processing(head_block_root, &self.spec)?; } Ok(()) } @@ -299,30 +291,28 @@ where /// Produce an `AttestationData` that is valid for the present `slot` and given `shard`. pub fn produce_attestation_data(&self, shard: u64) -> Result { let justified_epoch = self.justified_epoch(); - let justified_block_root = self + let justified_block_root = *self .state .read() .get_block_root( justified_epoch.start_slot(self.spec.epoch_length), &self.spec, ) - .ok_or_else(|| Error::BadRecentBlockRoots)? - .clone(); + .ok_or_else(|| Error::BadRecentBlockRoots)?; - let epoch_boundary_root = self + let epoch_boundary_root = *self .state .read() .get_block_root( self.state.read().current_epoch_start_slot(&self.spec), &self.spec, ) - .ok_or_else(|| Error::BadRecentBlockRoots)? - .clone(); + .ok_or_else(|| Error::BadRecentBlockRoots)?; Ok(AttestationData { slot: self.state.read().slot, shard, - beacon_block_root: self.head().beacon_block_root.clone(), + beacon_block_root: self.head().beacon_block_root, epoch_boundary_root, shard_block_root: Hash256::zero(), latest_crosslink: Crosslink { @@ -359,7 +349,7 @@ where let targets = self.latest_attestation_targets.read(); match targets.get(validator_index) { - Some(hash) => Some(hash.clone()), + Some(hash) => Some(*hash), None => None, } } @@ -449,15 +439,11 @@ where let parent_state = self .state_store .get_reader(&parent_state_root)? - .ok_or(Error::DBInconsistent(format!( - "Missing state {}", - parent_state_root - )))? + .ok_or_else(|| Error::DBInconsistent(format!("Missing state {}", parent_state_root)))? .into_beacon_state() - .ok_or(Error::DBInconsistent(format!( - "State SSZ invalid {}", - parent_state_root - )))?; + .ok_or_else(|| { + Error::DBInconsistent(format!("State SSZ invalid {}", parent_state_root)) + })?; // TODO: check the block proposer signature BEFORE doing a state transition. This will // significantly lower exposure surface to DoS attacks. @@ -465,7 +451,7 @@ where // Transition the parent state to the present slot. let mut state = parent_state; for _ in state.slot.as_u64()..present_slot.as_u64() { - if let Err(e) = state.per_slot_processing(parent_block_root.clone(), &self.spec) { + if let Err(e) = state.per_slot_processing(parent_block_root, &self.spec) { return Ok(BlockProcessingOutcome::InvalidBlock( InvalidBlock::SlotProcessingError(e), )); @@ -493,20 +479,14 @@ where self.state_store.put(&state_root, &ssz_encode(&state)[..])?; // Update the block DAG. - self.block_graph - .add_leaf(&parent_block_root, block_root.clone()); + self.block_graph.add_leaf(&parent_block_root, block_root); // If the parent block was the parent_block, automatically update the canonical head. // // TODO: this is a first-in-best-dressed scenario that is not ideal; fork_choice should be // run instead. if self.head().beacon_block_root == parent_block_root { - self.update_canonical_head( - block.clone(), - block_root.clone(), - state.clone(), - state_root.clone(), - ); + self.update_canonical_head(block.clone(), block_root, state.clone(), state_root); // Update the local state variable. *self.state.write() = state.clone(); } @@ -535,15 +515,13 @@ where attestations.len() ); - let parent_root = state - .get_block_root(state.slot.saturating_sub(1_u64), &self.spec)? - .clone(); + let parent_root = *state.get_block_root(state.slot.saturating_sub(1_u64), &self.spec)?; let mut block = BeaconBlock { slot: state.slot, parent_root, state_root: Hash256::zero(), // Updated after the state is calculated. - randao_reveal: randao_reveal, + randao_reveal, eth1_data: Eth1Data { // TODO: replace with real data deposit_root: Hash256::zero(), @@ -553,7 +531,7 @@ where body: BeaconBlockBody { proposer_slashings: vec![], attester_slashings: vec![], - attestations: attestations, + attestations, deposits: vec![], exits: vec![], }, diff --git a/beacon_node/beacon_chain/src/block_graph.rs b/beacon_node/beacon_chain/src/block_graph.rs index 5af851243..0aa227081 100644 --- a/beacon_node/beacon_chain/src/block_graph.rs +++ b/beacon_node/beacon_chain/src/block_graph.rs @@ -11,6 +11,7 @@ use types::Hash256; /// Presently, the DAG root (genesis block) is not tracked. /// /// The BlogGraph is thread-safe due to internal RwLocks. +#[derive(Default)] pub struct BlockGraph { pub leaves: RwLock>, } From 683147035b00f4c915fd6e2d9a37a21b8ce52799 Mon Sep 17 00:00:00 2001 From: Paul Hauner Date: Wed, 13 Feb 2019 10:28:57 +1100 Subject: [PATCH 25/27] Fix clippy lints in block and epoch processing --- eth2/state_processing/src/block_processable.rs | 11 +++++++---- eth2/state_processing/src/epoch_processable.rs | 4 ++++ 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/eth2/state_processing/src/block_processable.rs b/eth2/state_processing/src/block_processable.rs index 5f9fe1f7f..f043a723d 100644 --- a/eth2/state_processing/src/block_processable.rs +++ b/eth2/state_processing/src/block_processable.rs @@ -4,8 +4,8 @@ use log::debug; use ssz::{ssz_encode, TreeHash}; use types::{ beacon_state::{AttestationValidationError, CommitteesError}, - AggregatePublicKey, Attestation, BeaconBlock, BeaconState, ChainSpec, Epoch, Exit, Fork, - Hash256, PendingAttestation, PublicKey, Signature, + AggregatePublicKey, Attestation, BeaconBlock, BeaconState, ChainSpec, Crosslink, Epoch, Exit, + Fork, Hash256, PendingAttestation, PublicKey, Signature, }; // TODO: define elsehwere. @@ -330,11 +330,14 @@ fn validate_attestation_signature_optional( .ok_or(AttestationValidationError::NoBlockRoot)?, AttestationValidationError::WrongJustifiedRoot ); + let potential_crosslink = Crosslink { + shard_block_root: attestation.data.shard_block_root, + epoch: attestation.data.slot.epoch(spec.epoch_length), + }; ensure!( (attestation.data.latest_crosslink == state.latest_crosslinks[attestation.data.shard as usize]) - | (attestation.data.latest_crosslink - == state.latest_crosslinks[attestation.data.shard as usize]), + | (attestation.data.latest_crosslink == potential_crosslink), AttestationValidationError::BadLatestCrosslinkRoot ); if verify_signature { diff --git a/eth2/state_processing/src/epoch_processable.rs b/eth2/state_processing/src/epoch_processable.rs index 4afa725ce..aece61184 100644 --- a/eth2/state_processing/src/epoch_processable.rs +++ b/eth2/state_processing/src/epoch_processable.rs @@ -52,6 +52,10 @@ pub trait EpochProcessable { } impl EpochProcessable for BeaconState { + // Cyclomatic complexity is ignored. It would be ideal to split this function apart, however it + // remains monolithic to allow for easier spec updates. Once the spec is more stable we can + // optimise. + #[allow(clippy::cyclomatic_complexity)] fn per_epoch_processing(&mut self, spec: &ChainSpec) -> Result<(), Error> { let current_epoch = self.current_epoch(spec); let previous_epoch = self.previous_epoch(spec); From 722a1573675f753d7ff5b6510e8d888aba7d9bfb Mon Sep 17 00:00:00 2001 From: Paul Hauner Date: Wed, 13 Feb 2019 10:29:09 +1100 Subject: [PATCH 26/27] Silence clippy lints from generated protos --- protos/src/lib.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/protos/src/lib.rs b/protos/src/lib.rs index a9684909d..2759263e7 100644 --- a/protos/src/lib.rs +++ b/protos/src/lib.rs @@ -1,2 +1,5 @@ +// The protobuf code-generator is not up-to-date with clippy, therefore we silence some warnings. +#[allow(renamed_and_removed_lints)] pub mod services; +#[allow(renamed_and_removed_lints)] pub mod services_grpc; From 492d4749d775b6d18b7b95726a6b78265860d91a Mon Sep 17 00:00:00 2001 From: Paul Hauner Date: Wed, 13 Feb 2019 10:32:56 +1100 Subject: [PATCH 27/27] Fix clippy lint --- eth2/types/src/beacon_block.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eth2/types/src/beacon_block.rs b/eth2/types/src/beacon_block.rs index f68f79316..f6977595a 100644 --- a/eth2/types/src/beacon_block.rs +++ b/eth2/types/src/beacon_block.rs @@ -55,7 +55,7 @@ impl BeaconBlock { shard: spec.beacon_chain_shard_number, block_root: block_without_signature_root, }; - Hash256::from_slice(&proposal.hash_tree_root()[..]) + Hash256::from(&proposal.hash_tree_root()[..]) } }