diff --git a/beacon_node/beacon_chain/src/block_processing.rs b/beacon_node/beacon_chain/src/block_processing.rs index f99958dfbd..32670f51d2 100644 --- a/beacon_node/beacon_chain/src/block_processing.rs +++ b/beacon_node/beacon_chain/src/block_processing.rs @@ -8,16 +8,23 @@ use types::{ }; #[derive(Debug, PartialEq)] -pub enum Outcome { - FutureSlot, +pub enum ValidBlock { Processed, - NewCanonicalBlock, - NewReorgBlock, - NewForkBlock, +} + +#[derive(Debug, PartialEq)] +pub enum InvalidBlock { + FutureSlot, StateTransitionFailed(TransitionError), StateRootMismatch, } +#[derive(Debug, PartialEq)] +pub enum Outcome { + ValidBlock(ValidBlock), + InvalidBlock(InvalidBlock), +} + #[derive(Debug, PartialEq)] pub enum Error { DBError(String), @@ -70,7 +77,7 @@ where // Block from future slots (i.e., greater than the present slot) should not be processed. if block.slot() > present_slot { - return Ok(Outcome::FutureSlot); + return Ok(Outcome::InvalidBlock(InvalidBlock::FutureSlot)); } let parent_block_root = block.parent_root(); @@ -80,7 +87,8 @@ where .get_reader(&parent_block_root)? .ok_or(Error::MissingParentBlock(parent_block_root))?; - let parent_state_root = parent_block.parent_root(); + let parent_state_root = parent_block.state_root(); + let parent_state = self .state_store .get_reader(&parent_state_root)? @@ -90,13 +98,17 @@ where let state = match self.state_transition(parent_state, &block) { Ok(state) => state, - Err(error) => return Ok(Outcome::StateTransitionFailed(error)), + Err(error) => { + return Ok(Outcome::InvalidBlock(InvalidBlock::StateTransitionFailed( + error, + ))) + } }; let state_root = state.canonical_root(); if block.state_root != state_root { - return Ok(Outcome::StateRootMismatch); + return Ok(Outcome::InvalidBlock(InvalidBlock::StateRootMismatch)); } // Store the block and state. @@ -119,7 +131,7 @@ where } // The block was sucessfully processed. - Ok(Outcome::Processed) + Ok(Outcome::ValidBlock(ValidBlock::Processed)) } } diff --git a/beacon_node/beacon_chain/src/finalized_head.rs b/beacon_node/beacon_chain/src/finalized_head.rs new file mode 100644 index 0000000000..28c883b4cf --- /dev/null +++ b/beacon_node/beacon_chain/src/finalized_head.rs @@ -0,0 +1,34 @@ +use crate::{BeaconChain, CheckPoint, ClientDB, SlotClock}; +use std::sync::RwLockReadGuard; +use types::{BeaconBlock, BeaconState, Hash256}; + +impl BeaconChain +where + T: ClientDB, + U: SlotClock, +{ + pub fn update_finalized_head( + &self, + new_beacon_block: BeaconBlock, + new_beacon_block_root: Hash256, + new_beacon_state: BeaconState, + new_beacon_state_root: Hash256, + ) { + let mut finalized_head = self + .finalized_head + .write() + .expect("CRITICAL: finalized_head poisioned."); + finalized_head.update( + new_beacon_block, + new_beacon_block_root, + new_beacon_state, + new_beacon_state_root, + ); + } + + pub fn finalized_head(&self) -> RwLockReadGuard { + self.finalized_head + .read() + .expect("CRITICAL: finalized_head poisioned.") + } +} diff --git a/beacon_node/beacon_chain/src/lib.rs b/beacon_node/beacon_chain/src/lib.rs index b52dfdd73e..8ae6d0f904 100644 --- a/beacon_node/beacon_chain/src/lib.rs +++ b/beacon_node/beacon_chain/src/lib.rs @@ -1,8 +1,9 @@ mod attestation_targets; mod block_graph; -mod block_processing; +pub mod block_processing; pub mod block_production; mod canonical_head; +mod finalized_head; mod info; mod lmd_ghost; mod state_transition; @@ -17,9 +18,8 @@ use genesis::{genesis_beacon_block, genesis_beacon_state, GenesisError}; use slot_clock::SlotClock; use spec::ChainSpec; use ssz::ssz_encode; -use std::collections::{HashMap, HashSet}; use std::sync::{Arc, RwLock}; -use types::{BeaconBlock, BeaconState, Hash256, PublicKey}; +use types::{BeaconBlock, BeaconState, Hash256}; pub use self::block_processing::Outcome as BlockProcessingOutcome; diff --git a/beacon_node/beacon_chain/src/lmd_ghost.rs b/beacon_node/beacon_chain/src/lmd_ghost.rs index e4aaa34835..6116834bb4 100644 --- a/beacon_node/beacon_chain/src/lmd_ghost.rs +++ b/beacon_node/beacon_chain/src/lmd_ghost.rs @@ -32,7 +32,31 @@ where U: SlotClock, Error: From<::Error>, { - pub fn slow_lmd_ghost(&mut self, start_hash: &Hash256) -> Result { + pub fn fork_choice(&self) -> Result<(), Error> { + let present_head = &self.finalized_head().beacon_block_root; + + let new_head = self.slow_lmd_ghost(&self.finalized_head().beacon_block_root)?; + + if new_head != *present_head { + let block = self + .block_store + .get_deserialized(&new_head)? + .ok_or_else(|| Error::MissingBeaconBlock(new_head))?; + let block_root = block.canonical_root(); + + let state = self + .state_store + .get_deserialized(&block.state_root)? + .ok_or_else(|| Error::MissingBeaconState(block.state_root))?; + let state_root = state.canonical_root(); + + self.update_canonical_head(block, block_root, state, state_root); + } + + Ok(()) + } + + pub fn slow_lmd_ghost(&self, start_hash: &Hash256) -> Result { let start = self .block_store .get_reader(&start_hash)? diff --git a/beacon_node/beacon_chain/tests/chain.rs b/beacon_node/beacon_chain/tests/chain.rs index 3c0a9b8f92..52d001eed3 100644 --- a/beacon_node/beacon_chain/tests/chain.rs +++ b/beacon_node/beacon_chain/tests/chain.rs @@ -20,8 +20,9 @@ fn rig_can_generate_validators() { let validators = generate_validators(2, &chain); chain.spec = inject_validators_into_spec(chain.spec.clone(), &validators[..]); */ - let mut rig = TestRig::new(ChainSpec::foundation()); - rig.generate_validators(2); + let validator_count = 2; + let mut rig = TestRig::new(ChainSpec::foundation(), validator_count); + rig.produce_next_slot(); } /* diff --git a/beacon_node/beacon_chain/tests/utils/direct_beacon_node.rs b/beacon_node/beacon_chain/tests/utils/direct_beacon_node.rs index 130a4d1360..9fcd7c2862 100644 --- a/beacon_node/beacon_chain/tests/utils/direct_beacon_node.rs +++ b/beacon_node/beacon_chain/tests/utils/direct_beacon_node.rs @@ -1,22 +1,27 @@ +use beacon_chain::block_processing::{Error as ProcessingError, Outcome as ProcessingOutcome}; use beacon_chain::{block_production::Error as BlockProductionError, BeaconChain}; -use block_producer::{BeaconNode as BeaconBlockNode, BeaconNodeError as BeaconBlockNodeError}; +use block_producer::{ + BeaconNode as BeaconBlockNode, BeaconNodeError as BeaconBlockNodeError, PublishOutcome, +}; use db::ClientDB; use slot_clock::SlotClock; +use std::sync::Arc; use types::{BeaconBlock, PublicKey, Signature}; -pub struct DirectBeaconNode<'a, T: ClientDB, U: SlotClock> { - beacon_chain: &'a BeaconChain, +pub struct DirectBeaconNode { + beacon_chain: Arc>, } -impl<'a, T: ClientDB, U: SlotClock> DirectBeaconNode<'a, T, U> { - pub fn new(beacon_chain: &'a BeaconChain) -> Self { +impl DirectBeaconNode { + pub fn new(beacon_chain: Arc>) -> Self { Self { beacon_chain } } } -impl<'a, T: ClientDB, U: SlotClock> BeaconBlockNode for DirectBeaconNode<'a, T, U> +impl BeaconBlockNode for DirectBeaconNode where BlockProductionError: From<::Error>, + ProcessingError: From<::Error>, { fn proposer_nonce(&self, pubkey: &PublicKey) -> Result { let validator_index = self @@ -51,8 +56,16 @@ where { } } - /// Returns the value specified by the `set_next_publish_result`. - fn publish_beacon_block(&self, block: BeaconBlock) -> Result { - Err(BeaconBlockNodeError::DecodeFailure) + fn publish_beacon_block( + &self, + block: BeaconBlock, + ) -> Result { + match self.beacon_chain.process_block(block) { + Ok(ProcessingOutcome::ValidBlock(_)) => Ok(PublishOutcome::ValidBlock), + Ok(ProcessingOutcome::InvalidBlock(reason)) => { + Ok(PublishOutcome::InvalidBlock(format!("{:?}", reason))) + } + Err(error) => Err(BeaconBlockNodeError::RemoteFailure(format!("{:?}", error))), + } } } diff --git a/beacon_node/beacon_chain/tests/utils/direct_duties.rs b/beacon_node/beacon_chain/tests/utils/direct_duties.rs index 75177ac149..ff2cd7223f 100644 --- a/beacon_node/beacon_chain/tests/utils/direct_duties.rs +++ b/beacon_node/beacon_chain/tests/utils/direct_duties.rs @@ -2,15 +2,16 @@ use beacon_chain::{block_production::Error as BlockProductionError, BeaconChain} use block_producer::{DutiesReader, DutiesReaderError}; use db::ClientDB; use slot_clock::SlotClock; +use std::sync::Arc; use types::PublicKey; -pub struct DirectDuties<'a, T: ClientDB, U: SlotClock> { - beacon_chain: &'a BeaconChain, +pub struct DirectDuties { + beacon_chain: Arc>, pubkey: PublicKey, } -impl<'a, T: ClientDB, U: SlotClock> DirectDuties<'a, T, U> { - pub fn new(pubkey: PublicKey, beacon_chain: &'a BeaconChain) -> Self { +impl DirectDuties { + pub fn new(pubkey: PublicKey, beacon_chain: Arc>) -> Self { Self { beacon_chain, pubkey, @@ -18,11 +19,11 @@ impl<'a, T: ClientDB, U: SlotClock> DirectDuties<'a, T, U> { } } -impl<'a, T: ClientDB, U: SlotClock> DutiesReader for DirectDuties<'a, T, U> +impl DutiesReader for DirectDuties where BlockProductionError: From<::Error>, { - fn is_block_production_slot(&self, _epoch: u64, slot: u64) -> Result { + fn is_block_production_slot(&self, slot: u64) -> Result { let validator_index = self .beacon_chain .validator_index(&self.pubkey) diff --git a/beacon_node/beacon_chain/tests/utils/test_rig.rs b/beacon_node/beacon_chain/tests/utils/test_rig.rs index 0bdf0a36bb..7bd494b23a 100644 --- a/beacon_node/beacon_chain/tests/utils/test_rig.rs +++ b/beacon_node/beacon_chain/tests/utils/test_rig.rs @@ -11,75 +11,77 @@ use spec::ChainSpec; use std::sync::{Arc, RwLock}; use types::{Keypair, Validator}; -pub struct TestRig<'a> { +pub struct TestRig { db: Arc, - beacon_chain: BeaconChain, + beacon_chain: Arc>, block_store: Arc>, state_store: Arc>, - validators: Vec>, + validators: Vec, } -impl<'a> TestRig<'a> { - pub fn new(spec: ChainSpec) -> Self { +impl TestRig { + pub fn new(mut 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 slot_clock = TestingSlotClock::new(0); - let mut beacon_chain = - BeaconChain::genesis(state_store.clone(), block_store.clone(), slot_clock, spec) - .unwrap(); + // 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 validators = generate_validators(validator_count, &beacon_chain); - beacon_chain.spec = inject_validators_into_spec(beacon_chain.spec.clone(), &validators[..]); - */ + // Insert `validator_count` new `Validator` records into the spec, retaining the keypairs + // for later user. + let mut keypairs = Vec::with_capacity(validator_count); + for _ in 0..validator_count { + let keypair = Keypair::random(); + + spec.initial_validators.push(Validator { + pubkey: keypair.pk.clone(), + ..std::default::Default::default() + }); + spec.initial_balances.push(32_000_000_000); // 32 ETH + + keypairs.push(keypair); + } + + // Create the Beacon Chain + let beacon_chain = Arc::new( + BeaconChain::genesis(state_store.clone(), block_store.clone(), slot_clock, spec) + .unwrap(), + ); + + // Spawn the test validator instances. + let mut validators = Vec::with_capacity(validator_count); + for keypair in keypairs { + validators.push(TestValidator::new(keypair.clone(), beacon_chain.clone())); + } Self { db, beacon_chain, block_store, state_store, - validators: vec![], + validators, } } - pub fn generate_validators(&'a mut self, validator_count: usize) { - self.validators = Vec::with_capacity(validator_count); - for _ in 0..validator_count { - self.validators.push(TestValidator::new(&self.beacon_chain)); - } - self.beacon_chain.spec = - inject_validators_into_spec(self.beacon_chain.spec.clone(), &self.validators[..]); - } - - pub fn process_next_slot(&mut self) { + pub fn produce_next_slot(&mut self) { let slot = self .beacon_chain .present_slot() .expect("Unable to determine slot.") + 1; + self.beacon_chain.slot_clock.set_slot(slot); - let block_proposer = self + let proposer = self .beacon_chain .block_proposer(slot) .expect("Unable to determine proposer."); - let validator = self - .validators - .get(block_proposer) - .expect("Block proposer unknown"); + self.validators[proposer].set_slot(slot); + self.validators[proposer].produce_block().unwrap(); } } - -fn inject_validators_into_spec(mut spec: ChainSpec, validators: &[TestValidator]) -> ChainSpec { - spec.initial_validators = Vec::with_capacity(validators.len()); - spec.initial_balances = Vec::with_capacity(validators.len()); - for validator in validators { - spec.initial_validators.push(validator.validator_record()); - spec.initial_balances.push(32_000_000_000); // 32 ETH - } - spec -} diff --git a/beacon_node/beacon_chain/tests/utils/validator.rs b/beacon_node/beacon_chain/tests/utils/validator.rs index a6a348dced..755ea51d10 100644 --- a/beacon_node/beacon_chain/tests/utils/validator.rs +++ b/beacon_node/beacon_chain/tests/utils/validator.rs @@ -1,36 +1,46 @@ use super::{DirectBeaconNode, DirectDuties}; use beacon_chain::BeaconChain; #[cfg(test)] -use block_producer::{test_utils::TestSigner, BlockProducer}; +use block_producer::{test_utils::TestSigner, BlockProducer, Error as PollError}; use db::MemoryDB; use slot_clock::TestingSlotClock; use spec::ChainSpec; use std::sync::{Arc, RwLock}; use types::{Keypair, Validator}; -pub struct TestValidator<'a> { +pub use block_producer::PollOutcome; + +#[derive(Debug, PartialEq)] +pub enum ProduceError { + DidNotProduce(PollOutcome), + PollError(PollError), +} + +pub struct TestValidator { block_producer: BlockProducer< TestingSlotClock, - DirectBeaconNode<'a, MemoryDB, TestingSlotClock>, - DirectDuties<'a, MemoryDB, TestingSlotClock>, + DirectBeaconNode, + DirectDuties, TestSigner, >, spec: Arc, - epoch_map: Arc>, + epoch_map: Arc>, keypair: Keypair, - beacon_node: Arc>, - slot_clock: Arc>, + beacon_node: Arc>, + slot_clock: Arc, signer: Arc, } -impl<'a> TestValidator<'a> { - pub fn new(beacon_chain: &'a BeaconChain) -> Self { +impl TestValidator { + pub fn new( + keypair: Keypair, + beacon_chain: Arc>, + ) -> Self { let spec = Arc::new(ChainSpec::foundation()); - let keypair = Keypair::random(); - let slot_clock = Arc::new(RwLock::new(TestingSlotClock::new(0))); + let slot_clock = Arc::new(TestingSlotClock::new(0)); let signer = Arc::new(TestSigner::new(keypair.clone())); - let beacon_node = Arc::new(DirectBeaconNode::new(beacon_chain)); - let epoch_map = Arc::new(DirectDuties::new(keypair.pk.clone(), beacon_chain)); + 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(), @@ -52,10 +62,15 @@ impl<'a> TestValidator<'a> { } } - pub fn validator_record(&self) -> Validator { - Validator { - pubkey: self.keypair.pk.clone(), - ..std::default::Default::default() + pub fn produce_block(&mut self) -> Result { + match self.block_producer.poll() { + Ok(PollOutcome::BlockProduced(slot)) => Ok(PollOutcome::BlockProduced(slot)), + Ok(outcome) => Err(ProduceError::DidNotProduce(outcome)), + Err(error) => Err(ProduceError::PollError(error)), } } + + pub fn set_slot(&mut self, slot: u64) { + self.slot_clock.set_slot(slot) + } }