diff --git a/beacon_chain/chain/src/block_preprocessing.rs b/beacon_chain/chain/src/block_context.rs similarity index 51% rename from beacon_chain/chain/src/block_preprocessing.rs rename to beacon_chain/chain/src/block_context.rs index 263782d30..05219e3dd 100644 --- a/beacon_chain/chain/src/block_preprocessing.rs +++ b/beacon_chain/chain/src/block_context.rs @@ -1,33 +1,24 @@ -extern crate ssz_helpers; -extern crate validation; - use db::{ ClientDB, }; use db::stores::{ BeaconBlockAtSlotError, }; -use self::validation::block_validation::{ +use validation::block_validation::{ BeaconBlockValidationContext, - SszBeaconBlockValidationError, }; use super::{ BeaconChain, - BeaconChainError, }; -use self::ssz_helpers::ssz_beacon_block::{ +use ssz_helpers::ssz_beacon_block::{ SszBeaconBlock, - SszBeaconBlockError, }; use std::sync::Arc; use types::{ - BeaconBlock, Hash256, }; -pub use self::validation::block_validation::BeaconBlockStatus; - -pub enum BeaconChainBlockError { +pub enum BlockValidationContextError { UnknownCrystallizedState, UnknownActiveState, UnknownAttesterProposerMaps, @@ -35,37 +26,20 @@ pub enum BeaconChainBlockError { UnknownJustifiedBlock, BlockAlreadyKnown, BlockSlotLookupError(BeaconBlockAtSlotError), - BadSsz(SszBeaconBlockError), - BlockValidationError(SszBeaconBlockValidationError), - DBError(String), } -impl From for BeaconChainBlockError { - fn from(e: BeaconBlockAtSlotError) -> BeaconChainBlockError { - BeaconChainBlockError::BlockSlotLookupError(e) +impl From for BlockValidationContextError { + fn from(e: BeaconBlockAtSlotError) -> BlockValidationContextError { + BlockValidationContextError::BlockSlotLookupError(e) } } -impl From for BeaconChainBlockError { - fn from(e: SszBeaconBlockValidationError) -> BeaconChainBlockError { - BeaconChainBlockError::BlockValidationError(e) - } -} - -pub type BlockStatusTriple = (BeaconBlockStatus, Hash256, BeaconBlock); - impl BeaconChain where T: ClientDB + Sized { - fn block_preprocessing(&self, ssz: &[u8], present_slot: u64) - -> Result + pub(crate) fn block_validation_context(&self, block: &SszBeaconBlock, present_slot: u64) + -> Result, BlockValidationContextError> { - /* - * Generate a SszBlock to read directly from the serialized SSZ. - */ - let block = SszBeaconBlock::from_slice(ssz)?; - let block_hash = Hash256::from(&block.block_hash()[..]); - /* * Load the crystallized state for this block from our caches. * @@ -73,7 +47,7 @@ impl BeaconChain */ let cry_state_root = Hash256::from(block.cry_state_root()); let cry_state = self.crystallized_states.get(&cry_state_root) - .ok_or(BeaconChainBlockError::UnknownCrystallizedState)?; + .ok_or(BlockValidationContextError::UnknownCrystallizedState)?; /* * Load the active state for this block from our caches. @@ -82,7 +56,7 @@ impl BeaconChain */ let act_state_root = Hash256::from(block.act_state_root()); let act_state = self.active_states.get(&act_state_root) - .ok_or(BeaconChainBlockError::UnknownActiveState)?; + .ok_or(BlockValidationContextError::UnknownActiveState)?; /* * Learn the last justified slot from the crystallized state and load @@ -90,21 +64,18 @@ impl BeaconChain */ let last_justified_slot = cry_state.last_justified_slot; let parent_block_hash = block.parent_hash() - .ok_or(BeaconChainBlockError::NoParentHash)?; + .ok_or(BlockValidationContextError::NoParentHash)?; let (last_justified_block_hash, _) = self.store.block.block_at_slot( &parent_block_hash, last_justified_slot)? - .ok_or(BeaconChainBlockError::UnknownJustifiedBlock)?; + .ok_or(BlockValidationContextError::UnknownJustifiedBlock)?; /* * Load the attester and proposer maps for the crystallized state. */ let (attester_map, proposer_map) = self.attester_proposer_maps.get(&cry_state_root) - .ok_or(BeaconChainBlockError::UnknownAttesterProposerMaps)?; + .ok_or(BlockValidationContextError::UnknownAttesterProposerMaps)?; - /* - * Build a block validation context to test the block against. - */ - let validation_context = BeaconBlockValidationContext { + Ok(BeaconBlockValidationContext { present_slot, cycle_length: self.config.cycle_length, last_justified_slot: cry_state.last_justified_slot, @@ -116,17 +87,6 @@ impl BeaconChain block_store: self.store.block.clone(), validator_store: self.store.validator.clone(), pow_store: self.store.pow_chain.clone(), - }; - let (block_status, deserialized_block) = validation_context.validate_ssz_block(&block_hash, &block)?; - match deserialized_block { - Some(b) => Ok((block_status, block_hash, b)), - None => Err(BeaconChainBlockError::BlockAlreadyKnown) - } - } -} - -impl From for BeaconChainBlockError { - fn from(e: SszBeaconBlockError) -> BeaconChainBlockError { - BeaconChainBlockError::BadSsz(e) + }) } } diff --git a/beacon_chain/chain/src/block_processing.rs b/beacon_chain/chain/src/block_processing.rs new file mode 100644 index 000000000..5ba44cab0 --- /dev/null +++ b/beacon_chain/chain/src/block_processing.rs @@ -0,0 +1,109 @@ +use super::{ + BeaconChain, + ClientDB, +}; +use super::block_context::{ + BlockValidationContextError, +}; +use ssz_helpers::ssz_beacon_block::{ + SszBeaconBlock, + SszBeaconBlockError, +}; +use types::{ + Hash256, +}; +use validation::block_validation::{ + BeaconBlockStatus, + SszBeaconBlockValidationError, +}; + +pub enum BlockProcessingOutcome { + BlockAlreadyKnown, + NewCanonicalBlock, + NewForkBlock, +} + +pub enum BlockProcessingError { + ContextGenerationError(BlockValidationContextError), + DeserializationFailed(SszBeaconBlockError), + ValidationFailed(SszBeaconBlockValidationError), +} + +impl BeaconChain + where T: ClientDB + Sized +{ + pub fn process_block(&mut self, ssz: &[u8], present_slot: u64) + -> Result<(BlockProcessingOutcome, Hash256), BlockProcessingError> + { + /* + * Generate a SszBlock to read directly from the serialized SSZ. + */ + let ssz_block = SszBeaconBlock::from_slice(ssz)?; + let block_hash = Hash256::from(&ssz_block.block_hash()[..]); + let parent_hash = ssz_block.parent_hash() + .ok_or(BlockProcessingError::ValidationFailed( + SszBeaconBlockValidationError::UnknownParentHash))?; + + /* + * Generate the context in which to validate this block. + */ + let validation_context = self.block_validation_context(&ssz_block, present_slot)?; + + /* + * Validate the block against the context, checking signatures, parent_hashes, etc. + */ + let (block_status, block) = validation_context.validate_ssz_block(&block_hash, &block)?; + + match block_status { + /* + * + */ + BeaconBlockStatus::KnownBlock => { + Ok((BlockProcessingOutcome::BlockAlreadyKnown, block_hash)) + } + BeaconBlockStatus::NewBlock => { + let head_hash_index = { + match self.head_block_hashes.iter().position(|x| *x == Hash256::from(parent_hash)) { + Some(i) => i, + None => { + self.head_block_hashes.push(block_hash); + self.head_block_hashes.len() - 1 + } + } + }; + + if head_hash_index == self.canonical_head_block_hash { + Ok((BlockProcessingOutcome::NewCanonicalBlock, block_hash)) + } else { + Ok((BlockProcessingOutcome::NewForkBlock, block_hash)) + } + } + } + } + + pub fn extend_chain( + &self, + block: &Block, + block_hash: &Hash256, + head_hash_index: usize) + -> Result<> +} + + +impl From for BlockProcessingError { + fn from(e: BlockValidationContextError) -> Self { + BlockProcessingError::ContextGenerationError(e) + } +} + +impl From for BlockProcessingError { + fn from(e: SszBeaconBlockError) -> Self { + BlockProcessingError::DeserializationFailed(e) + } +} + +impl From for BlockProcessingError { + fn from(e: SszBeaconBlockValidationError) -> Self { + BlockProcessingError::ValidationFailed(e) + } +} diff --git a/beacon_chain/chain/src/lib.rs b/beacon_chain/chain/src/lib.rs index ac62a4068..42aab7ef0 100644 --- a/beacon_chain/chain/src/lib.rs +++ b/beacon_chain/chain/src/lib.rs @@ -1,10 +1,13 @@ extern crate db; extern crate types; +extern crate ssz_helpers; +extern crate validation; extern crate validator_induction; extern crate validator_shuffling; mod stores; -mod block_preprocessing; +mod block_context; +mod block_processing; mod maps; mod genesis; @@ -43,10 +46,10 @@ impl From for BeaconChainError { pub struct BeaconChain { /// The last slot which has been finalized, this is common to all forks. pub last_finalized_slot: u64, - /// The hash of the head of the canonical chain. - pub canonical_latest_block_hash: Hash256, - /// A vec of hashes of heads of fork (non-canonical) chains. - pub fork_latest_block_hashes: Vec, + /// A vec of all block heads (tips of chains). + pub head_block_hashes: Vec, + /// The index of the canonical block in `head_block_hashes`. + pub canonical_head_block_hash: usize, /// A map where the value is an active state the the key is its hash. pub active_states: HashMap, /// A map where the value is crystallized state the the key is its hash. @@ -72,7 +75,8 @@ impl BeaconChain let (active_state, crystallized_state) = genesis_states(&config)?; let canonical_latest_block_hash = Hash256::zero(); - let fork_latest_block_hashes = vec![]; + let head_block_hashes = vec![canonical_latest_block_hash]; + let canonical_head_block_hash = 0; let mut active_states = HashMap::new(); let mut crystallized_states = HashMap::new(); let mut attester_proposer_maps = HashMap::new(); @@ -88,8 +92,8 @@ impl BeaconChain Ok(Self{ last_finalized_slot: 0, - canonical_latest_block_hash, - fork_latest_block_hashes, + head_block_hashes, + canonical_head_block_hash, active_states, crystallized_states, attester_proposer_maps, @@ -97,6 +101,10 @@ impl BeaconChain config, }) } + + pub fn canonical_block_hash(self) -> Hash256 { + self.head_block_hashes[self.canonical_head_block_hash] + } } @@ -128,7 +136,7 @@ mod tests { let (act, cry) = genesis_states(&config).unwrap(); assert_eq!(chain.last_finalized_slot, 0); - assert_eq!(chain.canonical_latest_block_hash, Hash256::zero()); + assert_eq!(chain.canonical_block_hash(), Hash256::zero()); let stored_act = chain.active_states.get(&Hash256::zero()).unwrap(); assert_eq!(act, *stored_act); diff --git a/lighthouse/db/src/stores/beacon_block_store.rs b/lighthouse/db/src/stores/beacon_block_store.rs index 2caf225a0..81c916b31 100644 --- a/lighthouse/db/src/stores/beacon_block_store.rs +++ b/lighthouse/db/src/stores/beacon_block_store.rs @@ -51,13 +51,6 @@ impl BeaconBlockStore { self.db.exists(DB_COLUMN, hash) } - pub fn block_exists_in_canonical_chain(&self, hash: &[u8]) - -> Result - { - // TODO: implement logic for canonical chain - self.db.exists(DB_COLUMN, hash) - } - /// Retrieve the block at a slot given a "head_hash" and a slot. /// /// A "head_hash" must be a block hash with a slot number greater than or equal to the desired