diff --git a/Cargo.toml b/Cargo.toml index cc77c734f..60b600efd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,7 +33,6 @@ name = "lighthouse" [workspace] members = [ "beacon_chain/attestation_validation", - "beacon_chain/block_validation", "beacon_chain/chain", "beacon_chain/naive_fork_choice", "beacon_chain/state-transition", diff --git a/beacon_chain/block_validation/Cargo.toml b/beacon_chain/block_validation/Cargo.toml deleted file mode 100644 index 5a68759c6..000000000 --- a/beacon_chain/block_validation/Cargo.toml +++ /dev/null @@ -1,14 +0,0 @@ -[package] -name = "block_validation" -version = "0.1.0" -authors = ["Paul Hauner "] - -[dependencies] -attestation_validation = { path = "../attestation_validation" } -bls = { path = "../utils/bls" } -db = { path = "../../lighthouse/db" } -hashing = { path = "../utils/hashing" } -rayon = "1.0.2" -ssz = { path = "../utils/ssz" } -ssz_helpers = { path = "../utils/ssz_helpers" } -types = { path = "../types" } diff --git a/beacon_chain/block_validation/src/block_validation.rs b/beacon_chain/block_validation/src/block_validation.rs deleted file mode 100644 index 5f295d7fc..000000000 --- a/beacon_chain/block_validation/src/block_validation.rs +++ /dev/null @@ -1,355 +0,0 @@ -extern crate rayon; - -use self::rayon::prelude::*; - -use super::attestation_validation::{AttestationValidationContext, AttestationValidationError}; -use super::db::stores::{BeaconBlockStore, PoWChainStore, ValidatorStore}; -use super::db::{ClientDB, DBError}; -use super::ssz::{Decodable, DecodeError}; -use super::ssz_helpers::attestation_ssz_splitter::{ - split_all_attestations, split_one_attestation, AttestationSplitError, -}; -use super::ssz_helpers::ssz_beacon_block::{SszBeaconBlock, SszBeaconBlockError}; -use super::types::Hash256; -use super::types::{AttestationRecord, AttesterMap, BeaconBlock, ProposerMap}; -use std::sync::{Arc, RwLock}; - -#[derive(Debug, PartialEq)] -pub enum SszBeaconBlockValidationError { - FutureSlot, - SlotAlreadyFinalized, - UnknownPoWChainRef, - UnknownParentHash, - BadAttestationSsz, - BadAncestorHashesSsz, - BadSpecialsSsz, - ParentSlotHigherThanBlockSlot, - AttestationValidationError(AttestationValidationError), - AttestationSignatureFailed, - ProposerAttestationHasObliqueHashes, - NoProposerSignature, - BadProposerMap, - RwLockPoisoned, - DBError(String), -} - -/// The context against which a block should be validated. -pub struct BeaconBlockValidationContext -where - T: ClientDB + Sized, -{ - /// The slot as determined by the system time. - pub present_slot: u64, - /// The cycle_length as determined by the chain configuration. - pub cycle_length: u8, - /// The last justified slot as per the client's view of the canonical chain. - pub last_justified_slot: u64, - /// The last justified block hash as per the client's view of the canonical chain. - pub last_justified_block_hash: Hash256, - /// The last finalized slot as per the client's view of the canonical chain. - pub last_finalized_slot: u64, - /// A vec of the hashes of the blocks preceeding the present slot. - pub recent_block_hashes: Arc>, - /// A map of slots to a block proposer validation index. - pub proposer_map: Arc, - /// A map of (slot, shard_id) to the attestation set of validation indices. - pub attester_map: Arc, - /// The store containing block information. - pub block_store: Arc>, - /// The store containing validator information. - pub validator_store: Arc>, - /// The store containing information about the proof-of-work chain. - pub pow_store: Arc>, -} - -impl BeaconBlockValidationContext -where - T: ClientDB, -{ - /// Validate some SszBeaconBlock against a block validation context. An SszBeaconBlock varies from a BeaconBlock in - /// that is a read-only structure that reads directly from encoded SSZ. - /// - /// The reason to validate an SzzBeaconBlock is to avoid decoding it in its entirety if there is - /// a suspicion that the block might be invalid. Such a suspicion should be applied to - /// all blocks coming from the network. - /// - /// This function will determine if the block is new, already known or invalid (either - /// intrinsically or due to some application error.) - /// - /// Note: this function does not implement randao_reveal checking as it is not in the - /// specification. - #[allow(dead_code)] - pub fn validate_ssz_block( - &self, - b: &SszBeaconBlock, - ) -> Result - where - T: ClientDB + Sized, - { - /* - * If the block slot corresponds to a slot in the future, return immediately with an error. - * - * It is up to the calling fn to determine what should be done with "future" blocks (e.g., - * cache or discard). - */ - let block_slot = b.slot(); - if block_slot > self.present_slot { - return Err(SszBeaconBlockValidationError::FutureSlot); - } - - /* - * If the block is unknown (assumed unknown because we checked the db earlier in this - * function) and it comes from a slot that is already finalized, drop the block. - * - * If a slot is finalized, there's no point in considering any other blocks for that slot. - * - * TODO: We can more strongly throw away blocks based on the `last_finalized_block` related - * to this `last_finalized_slot`. Namely, any block in a future slot must include the - * `last_finalized_block` in it's chain. - */ - if block_slot <= self.last_finalized_slot { - return Err(SszBeaconBlockValidationError::SlotAlreadyFinalized); - } - - /* - * If the PoW chain hash is not known to us, drop it. - * - * We only accept blocks that reference a known PoW hash. - * - * Note: it is not clear what a "known" PoW chain ref is. Likely it means the block hash is - * "sufficienty deep in the canonical PoW chain". This should be clarified as the spec - * crystallizes. - */ - let pow_chain_reference = b.pow_chain_reference(); - if !self.pow_store.block_hash_exists(b.pow_chain_reference())? { - return Err(SszBeaconBlockValidationError::UnknownPoWChainRef); - } - - /* - * Store a slice of the serialized attestations from the block SSZ. - */ - let attestations_ssz = &b.attestations_without_length(); - - /* - * Get a slice of the first serialized attestation (the 0'th) and decode it into - * a full AttestationRecord object. - * - * The first attestation must be validated separately as it must contain a signature of the - * proposer of the previous block (this is checked later in this function). - */ - let (first_attestation_ssz, next_index) = split_one_attestation(&attestations_ssz, 0)?; - let (first_attestation, _) = AttestationRecord::ssz_decode(&first_attestation_ssz, 0)?; - - /* - * The first attestation may not have oblique hashes. - * - * The presence of oblique hashes in the first attestation would indicate that the proposer - * of the previous block is attesting to some other block than the one they produced. - */ - if !first_attestation.oblique_parent_hashes.is_empty() { - return Err(SszBeaconBlockValidationError::ProposerAttestationHasObliqueHashes); - } - - /* - * Read the parent hash from the block we are validating then attempt to load - * that parent block ssz from the database. - * - * If that parent doesn't exist in the database or is invalid, reject the block. - * - * Also, read the slot from the parent block for later use. - */ - let parent_hash = b - .parent_hash() - .ok_or(SszBeaconBlockValidationError::BadAncestorHashesSsz)?; - let parent_block_slot = match self.block_store.get_serialized_block(&parent_hash)? { - None => return Err(SszBeaconBlockValidationError::UnknownParentHash), - Some(ssz) => { - let parent_block = SszBeaconBlock::from_slice(&ssz[..])?; - parent_block.slot() - } - }; - - /* - * The parent block slot must be less than the block slot. - * - * In other words, the parent must come before the child. - */ - if parent_block_slot >= block_slot { - return Err(SszBeaconBlockValidationError::ParentSlotHigherThanBlockSlot); - } - - /* - * TODO: Validate the first attestation. - */ - - /* - * Attempt to read load the parent block proposer from the proposer map. Return with an - * error if it fails. - * - * If the signature of proposer for the parent slot was not present in the first (0'th) - * attestation of this block, reject the block. - */ - let parent_block_proposer = self - .proposer_map - .get(&parent_block_slot) - .ok_or(SszBeaconBlockValidationError::BadProposerMap)?; - if !attestation_voters.contains(&parent_block_proposer) { - return Err(SszBeaconBlockValidationError::NoProposerSignature); - } - - /* - * Split the remaining attestations into a vector of slices, each containing - * a single serialized attestation record. - */ - let other_attestations = split_all_attestations(attestations_ssz, next_index)?; - - /* - * Verify each other AttestationRecord. - * - * This uses the `rayon` library to do "sometimes" parallelization. Put simply, - * if there are some spare threads, the verification of attestation records will happen - * concurrently. - * - * There is a thread-safe `failure` variable which is set whenever an attestation fails - * validation. This is so all attestation validation is halted if a single bad attestation - * is found. - */ - let failure: RwLock> = RwLock::new(None); - let mut deserialized_attestations: Vec = other_attestations - .par_iter() - .filter_map(|attestation_ssz| { - /* - * If some thread has set the `failure` variable to `Some(error)` the abandon - * attestation serialization and validation. Also, fail early if the lock has been - * poisoned. - */ - match failure.read() { - Ok(ref option) if option.is_none() => (), - _ => return None, - } - /* - * If there has not been a failure yet, attempt to serialize and validate the - * attestation. - */ - match AttestationRecord::ssz_decode(&attestation_ssz, 0) { - /* - * Deserialization failed, therefore the block is invalid. - */ - Err(e) => { - /* - * If the failure lock isn't poisoned, set it to some error. - */ - if let Ok(mut f) = failure.write() { - *f = Some(SszBeaconBlockValidationError::from(e)); - } - None - } - /* - * Deserialization succeeded and the attestation should be validated. - */ - Ok((attestation, _)) => { - match attestation_validation_context.validate_attestation(&attestation) { - /* - * Attestation validation failed with some error. - */ - Err(e) => { - /* - * If the failure lock isn't poisoned, set it to some error. - */ - if let Ok(mut f) = failure.write() { - *f = Some(SszBeaconBlockValidationError::from(e)); - } - None - } - /* - * Attestation validation succeded. - */ - Ok(_) => Some(attestation), - } - } - } - }).collect(); - - match failure.into_inner() { - Err(_) => return Err(SszBeaconBlockValidationError::RwLockPoisoned), - Ok(failure) => match failure { - Some(error) => return Err(error), - _ => (), - }, - } - - /* - * Add the first attestation to the vec of deserialized attestations at - * index 0. - */ - deserialized_attestations.insert(0, first_attestation); - - let (ancestor_hashes, _) = Decodable::ssz_decode(&b.ancestor_hashes(), 0) - .map_err(|_| SszBeaconBlockValidationError::BadAncestorHashesSsz)?; - let (specials, _) = Decodable::ssz_decode(&b.specials(), 0) - .map_err(|_| SszBeaconBlockValidationError::BadSpecialsSsz)?; - - /* - * If we have reached this point, the block is a new valid block that is worthy of - * processing. - */ - let block = BeaconBlock { - slot: block_slot, - randao_reveal: Hash256::from(b.randao_reveal()), - pow_chain_reference: Hash256::from(pow_chain_reference), - ancestor_hashes, - active_state_root: Hash256::from(b.act_state_root()), - crystallized_state_root: Hash256::from(b.cry_state_root()), - attestations: deserialized_attestations, - specials, - }; - Ok(block) - } -} - -impl From for SszBeaconBlockValidationError { - fn from(e: DBError) -> Self { - SszBeaconBlockValidationError::DBError(e.message) - } -} - -impl From for SszBeaconBlockValidationError { - fn from(e: AttestationSplitError) -> Self { - match e { - AttestationSplitError::TooShort => SszBeaconBlockValidationError::BadAttestationSsz, - } - } -} - -impl From for SszBeaconBlockValidationError { - fn from(e: SszBeaconBlockError) -> Self { - match e { - SszBeaconBlockError::TooShort => { - SszBeaconBlockValidationError::DBError("Bad parent block in db.".to_string()) - } - SszBeaconBlockError::TooLong => { - SszBeaconBlockValidationError::DBError("Bad parent block in db.".to_string()) - } - } - } -} - -impl From for SszBeaconBlockValidationError { - fn from(e: DecodeError) -> Self { - match e { - DecodeError::TooShort => SszBeaconBlockValidationError::BadAttestationSsz, - DecodeError::TooLong => SszBeaconBlockValidationError::BadAttestationSsz, - } - } -} - -impl From for SszBeaconBlockValidationError { - fn from(e: AttestationValidationError) -> Self { - SszBeaconBlockValidationError::AttestationValidationError(e) - } -} - -/* - * Tests for block validation are contained in the root directory "tests" directory (AKA - * "integration tests directory"). - */ diff --git a/beacon_chain/block_validation/src/lib.rs b/beacon_chain/block_validation/src/lib.rs deleted file mode 100644 index c4eae2bd8..000000000 --- a/beacon_chain/block_validation/src/lib.rs +++ /dev/null @@ -1,9 +0,0 @@ -extern crate attestation_validation; -extern crate bls; -extern crate db; -extern crate hashing; -extern crate ssz; -extern crate ssz_helpers; -extern crate types; - -pub mod block_validation; diff --git a/beacon_chain/chain/src/block_context.rs b/beacon_chain/chain/src/block_context.rs deleted file mode 100644 index 75c4352ec..000000000 --- a/beacon_chain/chain/src/block_context.rs +++ /dev/null @@ -1,93 +0,0 @@ -use super::BeaconChain; -use db::stores::BeaconBlockAtSlotError; -use db::ClientDB; -use ssz_helpers::ssz_beacon_block::SszBeaconBlock; -use std::sync::Arc; -use types::Hash256; -use validation::block_validation::BeaconBlockValidationContext; - -pub enum BlockValidationContextError { - UnknownCrystallizedState, - UnknownActiveState, - UnknownAttesterProposerMaps, - NoParentHash, - UnknownJustifiedBlock, - BlockAlreadyKnown, - BlockSlotLookupError(BeaconBlockAtSlotError), -} - -impl From for BlockValidationContextError { - fn from(e: BeaconBlockAtSlotError) -> BlockValidationContextError { - BlockValidationContextError::BlockSlotLookupError(e) - } -} - -impl BeaconChain -where - T: ClientDB + Sized, -{ - pub(crate) fn block_validation_context( - &self, - block: &SszBeaconBlock, - parent_block: &SszBeaconBlock, - present_slot: u64, - ) -> Result, BlockValidationContextError> { - /* - * Load the crystallized state for this block from our caches. - * - * Fail if the crystallized state is unknown. - */ - let cry_state_root = Hash256::from(parent_block.cry_state_root()); - let cry_state = self - .crystallized_states - .get(&cry_state_root) - .ok_or(BlockValidationContextError::UnknownCrystallizedState)?; - - /* - * Load the active state for this block from our caches. - * - * Fail if the active state is unknown. - */ - let act_state_root = Hash256::from(parent_block.act_state_root()); - let act_state = self - .active_states - .get(&act_state_root) - .ok_or(BlockValidationContextError::UnknownActiveState)?; - - /* - * Learn the last justified slot from the crystallized state and load - * the hash of this block from the database - */ - let last_justified_slot = cry_state.last_justified_slot; - let parent_block_hash = block - .parent_hash() - .ok_or(BlockValidationContextError::NoParentHash)?; - let (last_justified_block_hash, _) = self - .store - .block - .block_at_slot(&parent_block_hash, last_justified_slot)? - .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(BlockValidationContextError::UnknownAttesterProposerMaps)?; - - Ok(BeaconBlockValidationContext { - present_slot, - cycle_length: self.config.cycle_length, - last_justified_slot: cry_state.last_justified_slot, - last_justified_block_hash: Hash256::from(&last_justified_block_hash[..]), - last_finalized_slot: self.last_finalized_slot, - recent_block_hashes: Arc::new(act_state.recent_block_hashes.clone()), - proposer_map: proposer_map.clone(), - attester_map: attester_map.clone(), - block_store: self.store.block.clone(), - validator_store: self.store.validator.clone(), - pow_store: self.store.pow_chain.clone(), - }) - } -} diff --git a/beacon_chain/chain/src/block_processing.rs b/beacon_chain/chain/src/block_processing.rs index bfad68c15..8954e48d8 100644 --- a/beacon_chain/chain/src/block_processing.rs +++ b/beacon_chain/chain/src/block_processing.rs @@ -1,9 +1,5 @@ -use super::block_context::BlockValidationContextError; -use super::state_transition::StateTransitionError; use super::BeaconChain; -use db::{ClientDB, DBError}; -use naive_fork_choice::{naive_fork_choice, ForkChoiceError}; -use ssz_helpers::ssz_beacon_block::{SszBeaconBlock, SszBeaconBlockError}; +use db::ClientDB; use types::Hash256; pub enum BlockProcessingOutcome { @@ -13,18 +9,8 @@ pub enum BlockProcessingOutcome { NewForkBlock, } -pub enum BlockProcessingError { - ParentBlockNotFound, - ActiveStateRootInvalid, - CrystallizedStateRootInvalid, - NoHeadHashes, - UnknownParentHash, - ForkChoiceFailed(ForkChoiceError), - ContextGenerationFailed(BlockValidationContextError), - DeserializationFailed(SszBeaconBlockError), - ValidationFailed, - StateTransitionFailed(StateTransitionError), - DBError(String), +pub enum Error { + NotImplemented, } impl BeaconChain @@ -35,206 +21,9 @@ where &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()[..]); - - /* - * If this block is already known, return immediately and indicate the the block is - * known. Don't attempt to deserialize the block. - */ - if self.store.block.block_exists(&block_hash)? { - return Ok((BlockProcessingOutcome::BlockAlreadyKnown, block_hash)); - } - - /* - * Determine the hash of the blocks parent - */ - let parent_hash = ssz_block - .parent_hash() - .ok_or(BlockProcessingError::UnknownParentHash)?; - - /* - * Load the parent block from the database and create an SszBeaconBlock for reading it. - */ - let parent_block_ssz_bytes = self - .store - .block - .get_serialized_block(&parent_hash[..])? - .ok_or(BlockProcessingError::ParentBlockNotFound)?; - let parent_ssz_block = SszBeaconBlock::from_slice(&parent_block_ssz_bytes)?; - - /* - * Generate the context in which to validate this block. - */ - let validation_context = - self.block_validation_context(&ssz_block, &parent_ssz_block, present_slot)?; - - /* - * Validate the block against the context, checking signatures, parent_hashes, etc. - */ - let block = validation_context.validate_ssz_block(&ssz_block)?; - - let (new_act_state, new_cry_state_option) = { - /* - * Load the states from memory. - * - * Note: this is the second time we load these, the first was in - * `block_validation_context`. Theres an opportunity for some opimisation here. - * It was left out because it made the code more cumbersome. - */ - let act_state = self - .active_states - .get(&block.active_state_root) - .ok_or(BlockValidationContextError::UnknownActiveState)?; - let cry_state = self - .crystallized_states - .get(&block.crystallized_state_root) - .ok_or(BlockValidationContextError::UnknownCrystallizedState)?; - - self.transition_states(act_state, cry_state, &block, &block_hash)? - }; - - /* - * Calculate the new active state root and ensure the block state root matches. - */ - let new_act_state_root = new_act_state.canonical_root(); - if new_act_state_root != block.active_state_root { - return Err(BlockProcessingError::ActiveStateRootInvalid); - } - - /* - * Determine the crystallized state root and ensure the block state root matches. - * - * If a new crystallized state was created, store it in memory. - */ - let (new_cry_state_root, cry_state_transitioned) = match new_cry_state_option { - None => { - /* - * A new crystallized state was not created, therefore the - * `crystallized_state_root` of this block must match its parent. - */ - if Hash256::from(parent_ssz_block.cry_state_root()) != block.crystallized_state_root - { - return Err(BlockProcessingError::ActiveStateRootInvalid); - } - // Return the old root - (block.crystallized_state_root, false) - } - Some(new_cry_state) => { - /* - * A new crystallized state was created. Check to ensure the crystallized - * state root in the block is the same as the calculated on this node. - */ - let cry_state_root = new_cry_state.canonical_root(); - if cry_state_root != block.crystallized_state_root { - return Err(BlockProcessingError::ActiveStateRootInvalid); - } - /* - * Store the new crystallized state in memory. - */ - self.crystallized_states - .insert(cry_state_root, new_cry_state); - // Return the new root - (cry_state_root, true) - } - }; - - /* - * Store the new block as a leaf in the block tree. - */ - let mut new_head_block_hashes = self.head_block_hashes.clone(); - let new_parent_head_hash_index = match new_head_block_hashes - .iter() - .position(|x| *x == Hash256::from(parent_hash)) - { - Some(i) => { - new_head_block_hashes[i] = block_hash.clone(); - i - } - None => { - new_head_block_hashes.push(block_hash.clone()); - new_head_block_hashes.len() - 1 - } - }; - - /* - * Store the new block in the database. - */ - self.store - .block - .put_serialized_block(&block_hash[..], ssz_block.block_ssz())?; - - /* - * Store the active state in memory. - */ - self.active_states.insert(new_act_state_root, new_act_state); - - let new_canonical_head_block_hash_index = - match naive_fork_choice(&self.head_block_hashes, self.store.block.clone())? { - None => { - /* - * Fork choice failed, therefore the block, active state and crystallized state - * can be removed from storage (i.e., forgotten). - */ - if cry_state_transitioned { - // A new crystallized state was generated, so it should be deleted. - self.crystallized_states.remove(&new_cry_state_root); - } - self.active_states.remove(&new_act_state_root); - self.store.block.delete_block(&block_hash[..])?; - return Err(BlockProcessingError::NoHeadHashes); - } - Some(i) => i, - }; - - if new_canonical_head_block_hash_index != self.canonical_head_block_hash { - /* - * The block caused a re-org (switch of chains). - */ - Ok((BlockProcessingOutcome::NewReorgBlock, block_hash)) - } else { - /* - * The block did not cause a re-org. - */ - if new_parent_head_hash_index == self.canonical_head_block_hash { - Ok((BlockProcessingOutcome::NewCanonicalBlock, block_hash)) - } else { - Ok((BlockProcessingOutcome::NewForkBlock, block_hash)) - } - } - } -} - -impl From for BlockProcessingError { - fn from(e: BlockValidationContextError) -> Self { - BlockProcessingError::ContextGenerationFailed(e) - } -} - -impl From for BlockProcessingError { - fn from(e: SszBeaconBlockError) -> Self { - BlockProcessingError::DeserializationFailed(e) - } -} - -impl From for BlockProcessingError { - fn from(e: DBError) -> Self { - BlockProcessingError::DBError(e.message) - } -} - -impl From for BlockProcessingError { - fn from(e: ForkChoiceError) -> Self { - BlockProcessingError::ForkChoiceFailed(e) - } -} - -impl From for BlockProcessingError { - fn from(e: StateTransitionError) -> Self { - BlockProcessingError::StateTransitionFailed(e) + ) -> Result<(BlockProcessingOutcome, Hash256), Error> { + // TODO: block processing has been removed. + // https://github.com/sigp/lighthouse/issues/98 + Err(Error::NotImplemented) } } diff --git a/beacon_chain/chain/src/lib.rs b/beacon_chain/chain/src/lib.rs index 00ae5f325..17cda320f 100644 --- a/beacon_chain/chain/src/lib.rs +++ b/beacon_chain/chain/src/lib.rs @@ -7,7 +7,6 @@ extern crate types; extern crate validator_induction; extern crate validator_shuffling; -mod block_context; mod block_processing; mod genesis; mod maps;