From ec4a658fe77afdd20e0556a3b1a1f697bbbb34e7 Mon Sep 17 00:00:00 2001 From: Paul Hauner Date: Fri, 15 Feb 2019 16:12:24 +1100 Subject: [PATCH 1/2] Add beacon state test builder, tidy errors --- .../src/attestation_aggregator.rs | 4 +- beacon_node/beacon_chain/src/beacon_chain.rs | 16 +-- .../state_processing/src/block_processable.rs | 29 ++++- .../state_processing/src/epoch_processable.rs | 19 ++- eth2/state_processing/src/slot_processable.rs | 10 +- eth2/types/Cargo.toml | 3 + eth2/types/src/beacon_state.rs | 121 +++++++----------- eth2/types/src/beacon_state_tests.rs | 74 +++++++++++ eth2/types/src/lib.rs | 1 + 9 files changed, 173 insertions(+), 104 deletions(-) create mode 100644 eth2/types/src/beacon_state_tests.rs diff --git a/beacon_node/beacon_chain/src/attestation_aggregator.rs b/beacon_node/beacon_chain/src/attestation_aggregator.rs index 149f0d60d..6fbc11612 100644 --- a/beacon_node/beacon_chain/src/attestation_aggregator.rs +++ b/beacon_node/beacon_chain/src/attestation_aggregator.rs @@ -1,7 +1,7 @@ use state_processing::validate_attestation_without_signature; use std::collections::{HashMap, HashSet}; use types::{ - beacon_state::CommitteesError, AggregateSignature, Attestation, AttestationData, BeaconState, + beacon_state::BeaconStateError, AggregateSignature, Attestation, AttestationData, BeaconState, Bitfield, ChainSpec, FreeAttestation, Signature, }; @@ -79,7 +79,7 @@ impl AttestationAggregator { state: &BeaconState, free_attestation: &FreeAttestation, spec: &ChainSpec, - ) -> Result { + ) -> Result { let (slot, shard, committee_index) = some_or_invalid!( state.attestation_slot_and_shard_for_validator( free_attestation.validator_index as usize, diff --git a/beacon_node/beacon_chain/src/beacon_chain.rs b/beacon_node/beacon_chain/src/beacon_chain.rs index 41ceb4e29..c16337fd4 100644 --- a/beacon_node/beacon_chain/src/beacon_chain.rs +++ b/beacon_node/beacon_chain/src/beacon_chain.rs @@ -14,7 +14,7 @@ use state_processing::{ }; use std::sync::Arc; use types::{ - beacon_state::CommitteesError, + beacon_state::BeaconStateError, readers::{BeaconBlockReader, BeaconStateReader}, AttestationData, BeaconBlock, BeaconBlockBody, BeaconState, ChainSpec, Crosslink, Deposit, Epoch, Eth1Data, FreeAttestation, Hash256, PublicKey, Signature, Slot, @@ -24,7 +24,7 @@ use types::{ pub enum Error { InsufficientValidators, BadRecentBlockRoots, - CommitteesError(CommitteesError), + BeaconStateError(BeaconStateError), DBInconsistent(String), DBError(String), ForkChoiceError(ForkChoiceError), @@ -99,7 +99,7 @@ where initial_validator_deposits, latest_eth1_data, &spec, - ); + )?; let state_root = genesis_state.canonical_root(); state_store.put(&state_root, &ssz_encode(&genesis_state)[..])?; @@ -252,7 +252,7 @@ where /// /// Information is read from the present `beacon_state` shuffling, so only information from the /// present and prior epoch is available. - pub fn block_proposer(&self, slot: Slot) -> Result { + pub fn block_proposer(&self, slot: Slot) -> Result { let index = self .state .read() @@ -273,7 +273,7 @@ where pub fn validator_attestion_slot_and_shard( &self, validator_index: usize, - ) -> Result, CommitteesError> { + ) -> Result, BeaconStateError> { if let Some((slot, shard, _committee)) = self .state .read() @@ -588,8 +588,8 @@ impl From for Error { } } -impl From for Error { - fn from(e: CommitteesError) -> Error { - Error::CommitteesError(e) +impl From for Error { + fn from(e: BeaconStateError) -> Error { + Error::BeaconStateError(e) } } diff --git a/eth2/state_processing/src/block_processable.rs b/eth2/state_processing/src/block_processable.rs index f043a723d..3ae3c1b67 100644 --- a/eth2/state_processing/src/block_processable.rs +++ b/eth2/state_processing/src/block_processable.rs @@ -3,7 +3,7 @@ use hashing::hash; use log::debug; use ssz::{ssz_encode, TreeHash}; use types::{ - beacon_state::{AttestationValidationError, CommitteesError}, + beacon_state::{AttestationParticipantsError, BeaconStateError}, AggregatePublicKey, Attestation, BeaconBlock, BeaconState, ChainSpec, Crosslink, Epoch, Exit, Fork, Hash256, PendingAttestation, PublicKey, Signature, }; @@ -41,10 +41,23 @@ pub enum Error { BadCustodyReseeds, BadCustodyChallenges, BadCustodyResponses, - CommitteesError(CommitteesError), + BeaconStateError(BeaconStateError), SlotProcessingError(SlotProcessingError), } +#[derive(Debug, PartialEq)] +pub enum AttestationValidationError { + IncludedTooEarly, + IncludedTooLate, + WrongJustifiedSlot, + WrongJustifiedRoot, + BadLatestCrosslinkRoot, + BadSignature, + ShardBlockRootNotZero, + NoBlockRoot, + AttestationParticipantsError(AttestationParticipantsError), +} + macro_rules! ensure { ($condition: expr, $result: expr) => { if !$condition { @@ -390,9 +403,9 @@ impl From for Error { } } -impl From for Error { - fn from(e: CommitteesError) -> Error { - Error::CommitteesError(e) +impl From for Error { + fn from(e: BeaconStateError) -> Error { + Error::BeaconStateError(e) } } @@ -401,3 +414,9 @@ impl From for Error { Error::SlotProcessingError(e) } } + +impl From for AttestationValidationError { + fn from(e: AttestationParticipantsError) -> AttestationValidationError { + AttestationValidationError::AttestationParticipantsError(e) + } +} diff --git a/eth2/state_processing/src/epoch_processable.rs b/eth2/state_processing/src/epoch_processable.rs index aece61184..658449d6f 100644 --- a/eth2/state_processing/src/epoch_processable.rs +++ b/eth2/state_processing/src/epoch_processable.rs @@ -5,7 +5,7 @@ use ssz::TreeHash; use std::collections::{HashMap, HashSet}; use std::iter::FromIterator; use types::{ - beacon_state::{AttestationParticipantsError, CommitteesError, InclusionError}, + beacon_state::{AttestationParticipantsError, BeaconStateError, InclusionError}, validator_registry::get_active_validator_indices, BeaconState, ChainSpec, Crosslink, Epoch, Hash256, PendingAttestation, }; @@ -27,7 +27,7 @@ pub enum Error { NoBlockRoots, BaseRewardQuotientIsZero, NoRandaoSeed, - CommitteesError(CommitteesError), + BeaconStateError(BeaconStateError), AttestationParticipantsError(AttestationParticipantsError), InclusionError(InclusionError), WinningRootError(WinningRootError), @@ -559,9 +559,7 @@ impl EpochProcessable for BeaconState { self.current_epoch_start_shard = (self.current_epoch_start_shard + self.get_current_epoch_committee_count(spec) as u64) % spec.shard_count; - self.current_epoch_seed = self - .generate_seed(self.current_calculation_epoch, spec) - .ok_or_else(|| Error::NoRandaoSeed)?; + self.current_epoch_seed = self.generate_seed(self.current_calculation_epoch, spec)? } else { let epochs_since_last_registry_update = current_epoch - self.validator_registry_update_epoch; @@ -569,9 +567,8 @@ impl EpochProcessable for BeaconState { & 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.current_epoch_seed = + self.generate_seed(self.current_calculation_epoch, spec)? } } @@ -689,9 +686,9 @@ impl From for Error { } } -impl From for Error { - fn from(e: CommitteesError) -> Error { - Error::CommitteesError(e) +impl From for Error { + fn from(e: BeaconStateError) -> Error { + Error::BeaconStateError(e) } } diff --git a/eth2/state_processing/src/slot_processable.rs b/eth2/state_processing/src/slot_processable.rs index 7726c5071..9e3b611fd 100644 --- a/eth2/state_processing/src/slot_processable.rs +++ b/eth2/state_processing/src/slot_processable.rs @@ -1,9 +1,9 @@ use crate::{EpochProcessable, EpochProcessingError}; -use types::{beacon_state::CommitteesError, BeaconState, ChainSpec, Hash256}; +use types::{beacon_state::BeaconStateError, BeaconState, ChainSpec, Hash256}; #[derive(Debug, PartialEq)] pub enum Error { - CommitteesError(CommitteesError), + BeaconStateError(BeaconStateError), EpochProcessingError(EpochProcessingError), } @@ -49,9 +49,9 @@ 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: BeaconStateError) -> Error { + Error::BeaconStateError(e) } } diff --git a/eth2/types/Cargo.toml b/eth2/types/Cargo.toml index 5c5672297..099717a2c 100644 --- a/eth2/types/Cargo.toml +++ b/eth2/types/Cargo.toml @@ -19,3 +19,6 @@ serde_json = "1.0" slog = "^2.2.3" ssz = { path = "../utils/ssz" } vec_shuffle = { path = "../utils/vec_shuffle" } + +[dev-dependencies] +env_logger = "0.6.0" diff --git a/eth2/types/src/beacon_state.rs b/eth2/types/src/beacon_state.rs index ed53bfea9..112639792 100644 --- a/eth2/types/src/beacon_state.rs +++ b/eth2/types/src/beacon_state.rs @@ -9,48 +9,31 @@ use honey_badger_split::SplitExt; use rand::RngCore; use serde_derive::Serialize; use ssz::{hash, Decodable, DecodeError, Encodable, SszStream, TreeHash}; -use std::ops::Range; use vec_shuffle::shuffle; -pub enum Error { - InsufficientValidators, - BadBlockSignature, - InvalidEpoch(Slot, Range), - CommitteesError(CommitteesError), -} - #[derive(Debug, PartialEq)] -pub enum CommitteesError { - InvalidEpoch, - InsufficientNumberOfValidators, - BadRandao, +pub enum BeaconStateError { + EpochOutOfBounds, + InsufficientRandaoMixes, + InsufficientValidators, + InsufficientBlockRoots, + InsufficientIndexRoots, + InsufficientAttestations, + InsufficientCommittees, } #[derive(Debug, PartialEq)] pub enum InclusionError { - NoIncludedAttestations, + /// The validator did not participate in an attestation in this period. + NoAttestationsForValidator, AttestationParticipantsError(AttestationParticipantsError), } #[derive(Debug, PartialEq)] pub enum AttestationParticipantsError { + /// There is no committee for the given shard in the given epoch. NoCommitteeForShard, - NoCommittees, - BadBitfieldLength, - CommitteesError(CommitteesError), -} - -#[derive(Debug, PartialEq)] -pub enum AttestationValidationError { - IncludedTooEarly, - IncludedTooLate, - WrongJustifiedSlot, - WrongJustifiedRoot, - BadLatestCrosslinkRoot, - BadSignature, - ShardBlockRootNotZero, - NoBlockRoot, - AttestationParticipantsError(AttestationParticipantsError), + BeaconStateError(BeaconStateError), } macro_rules! safe_add_assign { @@ -111,7 +94,7 @@ impl BeaconState { initial_validator_deposits: Vec, latest_eth1_data: Eth1Data, spec: &ChainSpec, - ) -> BeaconState { + ) -> Result { let initial_crosslink = Crosslink { epoch: spec.genesis_epoch, shard_block_root: spec.zero_hash, @@ -195,11 +178,9 @@ impl BeaconState { )); 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.current_epoch_seed = genesis_state.generate_seed(spec.genesis_epoch, spec)?; - genesis_state + Ok(genesis_state) } /// Return the tree hash root for this `BeaconState`. @@ -322,7 +303,7 @@ impl BeaconState { + 1; let latest_index_root = current_epoch + spec.entry_exit_delay; - if (epoch <= earliest_index_root) & (epoch >= latest_index_root) { + if (epoch >= earliest_index_root) & (epoch <= latest_index_root) { Some(self.latest_index_roots[epoch.as_usize() % spec.latest_index_roots_length]) } else { None @@ -332,12 +313,27 @@ impl BeaconState { /// 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()); + pub fn generate_seed( + &self, + epoch: Epoch, + spec: &ChainSpec, + ) -> Result { + let mut input = self + .get_randao_mix(epoch, spec) + .ok_or_else(|| BeaconStateError::InsufficientRandaoMixes)? + .to_vec(); + + input.append( + &mut self + .get_active_index_root(epoch, spec) + .ok_or_else(|| BeaconStateError::InsufficientIndexRoots)? + .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[..])[..])) + + Ok(Hash256::from(&hash(&input[..])[..])) } /// Return the list of ``(committee, shard)`` tuples for the ``slot``. @@ -351,7 +347,7 @@ impl BeaconState { slot: Slot, registry_change: bool, spec: &ChainSpec, - ) -> Result, u64)>, CommitteesError> { + ) -> Result, u64)>, BeaconStateError> { let epoch = slot.epoch(spec.epoch_length); let current_epoch = self.current_epoch(spec); let previous_epoch = if current_epoch == spec.genesis_epoch { @@ -381,9 +377,7 @@ impl BeaconState { 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)?; + let next_seed = self.generate_seed(next_epoch, spec)?; ( next_seed, (self.current_epoch_start_shard + current_committees_per_epoch) @@ -392,9 +386,7 @@ impl BeaconState { } 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)?; + let next_seed = self.generate_seed(next_epoch, spec)?; (next_seed, self.current_epoch_start_shard) } else { (self.current_epoch_seed, self.current_epoch_start_shard) @@ -406,7 +398,7 @@ impl BeaconState { shuffling_start_shard, ) } else { - panic!("Epoch out-of-bounds.") + return Err(BeaconStateError::EpochOutOfBounds); }; let shuffling = self.get_shuffling(seed, shuffling_epoch, spec); @@ -434,7 +426,7 @@ impl BeaconState { &self, validator_index: usize, spec: &ChainSpec, - ) -> Result, CommitteesError> { + ) -> Result, BeaconStateError> { 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, false, spec)? { @@ -464,16 +456,15 @@ impl BeaconState { &self, slot: Slot, spec: &ChainSpec, - ) -> Result { + ) -> Result { let committees = self.get_crosslink_committees_at_slot(slot, false, spec)?; committees .first() - .ok_or(CommitteesError::InsufficientNumberOfValidators) + .ok_or(BeaconStateError::InsufficientValidators) .and_then(|(first_committee, _)| { 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. + .ok_or(BeaconStateError::InsufficientValidators)?; Ok(first_committee[index]) }) } @@ -682,7 +673,7 @@ impl BeaconState { &mut self, validator_index: usize, spec: &ChainSpec, - ) -> Result<(), CommitteesError> { + ) -> Result<(), BeaconStateError> { self.exit_validator(validator_index, spec); let current_epoch = self.current_epoch(spec); @@ -802,7 +793,7 @@ impl BeaconState { let earliest_attestation_index = included_attestations .iter() .min_by_key(|i| attestations[**i].inclusion_slot) - .ok_or_else(|| InclusionError::NoIncludedAttestations)?; + .ok_or_else(|| InclusionError::NoAttestationsForValidator)?; Ok(attestations[*earliest_attestation_index].clone()) } @@ -907,34 +898,18 @@ 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) +impl From for AttestationParticipantsError { + fn from(e: BeaconStateError) -> AttestationParticipantsError { + AttestationParticipantsError::BeaconStateError(e) } } -impl From for AttestationParticipantsError { - fn from(e: CommitteesError) -> AttestationParticipantsError { - AttestationParticipantsError::CommitteesError(e) - } -} - -/* - -*/ - impl From for InclusionError { fn from(e: AttestationParticipantsError) -> InclusionError { InclusionError::AttestationParticipantsError(e) } } -impl From for Error { - fn from(e: CommitteesError) -> Error { - Error::CommitteesError(e) - } -} - impl Encodable for BeaconState { fn ssz_append(&self, s: &mut SszStream) { s.append(&self.slot); diff --git a/eth2/types/src/beacon_state_tests.rs b/eth2/types/src/beacon_state_tests.rs new file mode 100644 index 000000000..bba99b2d2 --- /dev/null +++ b/eth2/types/src/beacon_state_tests.rs @@ -0,0 +1,74 @@ +use crate::{ + beacon_state::BeaconStateError, BeaconState, ChainSpec, Deposit, DepositData, DepositInput, + Eth1Data, Hash256, Keypair, +}; +use bls::create_proof_of_possession; + +struct BeaconStateTestBuilder { + pub genesis_time: u64, + pub initial_validator_deposits: Vec, + pub latest_eth1_data: Eth1Data, + pub spec: ChainSpec, + pub keypairs: Vec, +} + +impl BeaconStateTestBuilder { + pub fn with_random_validators(validator_count: usize) -> Self { + let genesis_time = 10_000_000; + let keypairs: Vec = (0..validator_count) + .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(); + let latest_eth1_data = Eth1Data { + deposit_root: Hash256::zero(), + block_hash: Hash256::zero(), + }; + let spec = ChainSpec::foundation(); + + Self { + genesis_time, + initial_validator_deposits, + latest_eth1_data, + spec, + keypairs, + } + } + + pub fn build(&self) -> Result { + BeaconState::genesis( + self.genesis_time, + self.initial_validator_deposits.clone(), + self.latest_eth1_data.clone(), + &self.spec, + ) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + pub fn can_produce_genesis_block() { + let builder = BeaconStateTestBuilder::with_random_validators(2); + + builder.build().unwrap(); + } +} diff --git a/eth2/types/src/lib.rs b/eth2/types/src/lib.rs index 233d1cc3e..d33c48532 100644 --- a/eth2/types/src/lib.rs +++ b/eth2/types/src/lib.rs @@ -7,6 +7,7 @@ pub mod attester_slashing; pub mod beacon_block; pub mod beacon_block_body; pub mod beacon_state; +pub mod beacon_state_tests; pub mod casper_slashing; pub mod crosslink; pub mod deposit; From 1c57eb47f9aa8916b6ef6a677aee5a345d6e0af1 Mon Sep 17 00:00:00 2001 From: Paul Hauner Date: Fri, 15 Feb 2019 17:07:52 +1100 Subject: [PATCH 2/2] Move `BeaconStateTestBuilder` into test mod --- eth2/types/src/beacon_state_tests.rs | 126 +++++++++++++-------------- 1 file changed, 62 insertions(+), 64 deletions(-) diff --git a/eth2/types/src/beacon_state_tests.rs b/eth2/types/src/beacon_state_tests.rs index bba99b2d2..cae815f33 100644 --- a/eth2/types/src/beacon_state_tests.rs +++ b/eth2/types/src/beacon_state_tests.rs @@ -1,69 +1,67 @@ -use crate::{ - beacon_state::BeaconStateError, BeaconState, ChainSpec, Deposit, DepositData, DepositInput, - Eth1Data, Hash256, Keypair, -}; -use bls::create_proof_of_possession; - -struct BeaconStateTestBuilder { - pub genesis_time: u64, - pub initial_validator_deposits: Vec, - pub latest_eth1_data: Eth1Data, - pub spec: ChainSpec, - pub keypairs: Vec, -} - -impl BeaconStateTestBuilder { - pub fn with_random_validators(validator_count: usize) -> Self { - let genesis_time = 10_000_000; - let keypairs: Vec = (0..validator_count) - .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(); - let latest_eth1_data = Eth1Data { - deposit_root: Hash256::zero(), - block_hash: Hash256::zero(), - }; - let spec = ChainSpec::foundation(); - - Self { - genesis_time, - initial_validator_deposits, - latest_eth1_data, - spec, - keypairs, - } - } - - pub fn build(&self) -> Result { - BeaconState::genesis( - self.genesis_time, - self.initial_validator_deposits.clone(), - self.latest_eth1_data.clone(), - &self.spec, - ) - } -} - #[cfg(test)] mod tests { - use super::*; + use crate::{ + beacon_state::BeaconStateError, BeaconState, ChainSpec, Deposit, DepositData, DepositInput, + Eth1Data, Hash256, Keypair, + }; + use bls::create_proof_of_possession; + + struct BeaconStateTestBuilder { + pub genesis_time: u64, + pub initial_validator_deposits: Vec, + pub latest_eth1_data: Eth1Data, + pub spec: ChainSpec, + pub keypairs: Vec, + } + + impl BeaconStateTestBuilder { + pub fn with_random_validators(validator_count: usize) -> Self { + let genesis_time = 10_000_000; + let keypairs: Vec = (0..validator_count) + .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(); + let latest_eth1_data = Eth1Data { + deposit_root: Hash256::zero(), + block_hash: Hash256::zero(), + }; + let spec = ChainSpec::foundation(); + + Self { + genesis_time, + initial_validator_deposits, + latest_eth1_data, + spec, + keypairs, + } + } + + pub fn build(&self) -> Result { + BeaconState::genesis( + self.genesis_time, + self.initial_validator_deposits.clone(), + self.latest_eth1_data.clone(), + &self.spec, + ) + } + } #[test] pub fn can_produce_genesis_block() {