diff --git a/beacon_node/beacon_chain/src/beacon_chain.rs b/beacon_node/beacon_chain/src/beacon_chain.rs index 9e3f6b52c..8602d32c7 100644 --- a/beacon_node/beacon_chain/src/beacon_chain.rs +++ b/beacon_node/beacon_chain/src/beacon_chain.rs @@ -88,6 +88,8 @@ impl BeaconChain { genesis_block: BeaconBlock, spec: ChainSpec, ) -> Result { + genesis_state.build_all_caches(&spec)?; + let state_root = genesis_state.canonical_root(); store.put(&state_root, &genesis_state)?; @@ -105,8 +107,6 @@ impl BeaconChain { state_root, )); - genesis_state.build_all_caches(&spec)?; - Ok(Self { spec, slot_clock, diff --git a/beacon_node/beacon_chain/src/lib.rs b/beacon_node/beacon_chain/src/lib.rs index 4aa1370d9..67b0cee47 100644 --- a/beacon_node/beacon_chain/src/lib.rs +++ b/beacon_node/beacon_chain/src/lib.rs @@ -5,6 +5,7 @@ mod fork_choice; pub mod iter; mod metrics; mod persisted_beacon_chain; +mod test_utils; pub use self::beacon_chain::{BeaconChain, BeaconChainTypes, BlockProcessingOutcome}; pub use self::checkpoint::CheckPoint; diff --git a/beacon_node/beacon_chain/src/test_utils.rs b/beacon_node/beacon_chain/src/test_utils.rs new file mode 100644 index 000000000..be370ff65 --- /dev/null +++ b/beacon_node/beacon_chain/src/test_utils.rs @@ -0,0 +1,147 @@ +use crate::{BeaconChain, BeaconChainTypes, BlockProcessingOutcome}; +use lmd_ghost::{LmdGhost, ThreadSafeReducedTree}; +use slot_clock::SlotClock; +use slot_clock::TestingSlotClock; +use std::marker::PhantomData; +use std::sync::Arc; +use store::MemoryStore; +use tree_hash::{SignedRoot, TreeHash}; +use types::{ + test_utils::TestingBeaconStateBuilder, BeaconBlock, ChainSpec, Domain, EthSpec, Hash256, + Keypair, MinimalEthSpec, Signature, +}; + +pub struct CommonTypes +where + L: LmdGhost, + E: EthSpec, +{ + _phantom_l: PhantomData, + _phantom_e: PhantomData, +} + +impl BeaconChainTypes for CommonTypes +where + L: LmdGhost, + E: EthSpec, +{ + type Store = MemoryStore; + type SlotClock = TestingSlotClock; + type LmdGhost = L; + type EthSpec = E; +} + +pub struct BeaconChainHarness +where + L: LmdGhost, + E: EthSpec, +{ + chain: BeaconChain>, + keypairs: Vec, + spec: ChainSpec, +} + +impl BeaconChainHarness +where + L: LmdGhost, + E: EthSpec, +{ + pub fn new(validator_count: usize) -> Self { + let spec = E::default_spec(); + + let store = Arc::new(MemoryStore::open()); + + let state_builder = + TestingBeaconStateBuilder::from_default_keypairs_file_if_exists(validator_count, &spec); + let (genesis_state, keypairs) = state_builder.build(); + + let mut genesis_block = BeaconBlock::empty(&spec); + genesis_block.state_root = Hash256::from_slice(&genesis_state.tree_hash_root()); + + // Slot clock + let slot_clock = TestingSlotClock::new( + spec.genesis_slot, + genesis_state.genesis_time, + spec.seconds_per_slot, + ); + + let chain = BeaconChain::from_genesis( + store, + slot_clock, + genesis_state, + genesis_block, + spec.clone(), + ) + .expect("Terminate if beacon chain generation fails"); + + Self { + chain, + keypairs, + spec, + } + } + + pub fn extend_canonical_chain(&self) { + self.chain.slot_clock.advance_slot(); + self.chain.catchup_state().expect("should catchup state"); + + let block = self.build_block(); + let outcome = self + .chain + .process_block(block) + .expect("should process block"); + assert_eq!(outcome, BlockProcessingOutcome::Processed); + } + + fn build_block(&self) -> BeaconBlock { + let slot = self.chain.read_slot_clock().unwrap(); + + let sk = { + let proposer = self + .chain + .block_proposer(slot) + .expect("should get block propoer"); + &self.keypairs[proposer].sk + }; + + let fork = &self.chain.head().beacon_state.fork; + + let randao_reveal = { + let epoch = slot.epoch(E::slots_per_epoch()); + let message = epoch.tree_hash_root(); + let domain = self.spec.get_domain(epoch, Domain::Randao, fork); + Signature::new(&message, domain, sk) + }; + + let (mut block, _state) = self + .chain + .produce_block(randao_reveal) + .expect("should producer block"); + + block.signature = { + let message = block.signed_root(); + let epoch = block.slot.epoch(E::slots_per_epoch()); + let domain = self.spec.get_domain(epoch, Domain::BeaconProposer, fork); + Signature::new(&message, domain, sk) + }; + + block + } +} + +#[cfg(test)] +mod test { + use super::*; + + pub const VALIDATOR_COUNT: usize = 16; + + #[test] + fn build_on_genesis() { + let harness: BeaconChainHarness< + ThreadSafeReducedTree, + MinimalEthSpec, + > = BeaconChainHarness::new(VALIDATOR_COUNT); + + harness.extend_canonical_chain(); + } +} diff --git a/eth2/utils/slot_clock/src/testing_slot_clock.rs b/eth2/utils/slot_clock/src/testing_slot_clock.rs index fc9b7201b..ab00d2baa 100644 --- a/eth2/utils/slot_clock/src/testing_slot_clock.rs +++ b/eth2/utils/slot_clock/src/testing_slot_clock.rs @@ -15,6 +15,10 @@ impl TestingSlotClock { pub fn set_slot(&self, slot: u64) { *self.slot.write().expect("TestingSlotClock poisoned.") = Slot::from(slot); } + + pub fn advance_slot(&self) { + self.set_slot(self.present_slot().unwrap().unwrap().as_u64() + 1) + } } impl SlotClock for TestingSlotClock {