Add untested minimum viable block processing

This commit is contained in:
Paul Hauner 2018-10-29 20:29:15 +01:00
parent e27c4106e9
commit 46da9b670f
No known key found for this signature in database
GPG Key ID: 303E4494BB28068C
21 changed files with 891 additions and 465 deletions

View File

@ -33,6 +33,8 @@ name = "lighthouse"
[workspace] [workspace]
members = [ members = [
"beacon_chain/chain", "beacon_chain/chain",
"beacon_chain/naive_fork_choice",
"beacon_chain/state-transition",
"beacon_chain/types", "beacon_chain/types",
"beacon_chain/utils/active-validators", "beacon_chain/utils/active-validators",
"beacon_chain/utils/bls", "beacon_chain/utils/bls",

View File

@ -6,7 +6,10 @@ authors = ["Paul Hauner <paul@paulhauner.com>"]
[dependencies] [dependencies]
bls = { path = "../utils/bls" } bls = { path = "../utils/bls" }
db = { path = "../../lighthouse/db" } db = { path = "../../lighthouse/db" }
naive_fork_choice = { path = "../naive_fork_choice" }
ssz = { path = "../utils/ssz" }
ssz_helpers = { path = "../utils/ssz_helpers" } ssz_helpers = { path = "../utils/ssz_helpers" }
state-transition = { path = "../state-transition" }
types = { path = "../types" } types = { path = "../types" }
validation = { path = "../validation" } validation = { path = "../validation" }
validator_induction = { path = "../validator_induction" } validator_induction = { path = "../validator_induction" }

View File

@ -1,22 +1,10 @@
use db::{ use super::BeaconChain;
ClientDB, use db::stores::BeaconBlockAtSlotError;
}; use db::ClientDB;
use db::stores::{ use ssz_helpers::ssz_beacon_block::SszBeaconBlock;
BeaconBlockAtSlotError,
};
use validation::block_validation::{
BeaconBlockValidationContext,
};
use super::{
BeaconChain,
};
use ssz_helpers::ssz_beacon_block::{
SszBeaconBlock,
};
use std::sync::Arc; use std::sync::Arc;
use types::{ use types::Hash256;
Hash256, use validation::block_validation::BeaconBlockValidationContext;
};
pub enum BlockValidationContextError { pub enum BlockValidationContextError {
UnknownCrystallizedState, UnknownCrystallizedState,
@ -35,18 +23,24 @@ impl From<BeaconBlockAtSlotError> for BlockValidationContextError {
} }
impl<T> BeaconChain<T> impl<T> BeaconChain<T>
where T: ClientDB + Sized where
T: ClientDB + Sized,
{ {
pub(crate) fn block_validation_context(&self, block: &SszBeaconBlock, present_slot: u64) pub(crate) fn block_validation_context(
-> Result<BeaconBlockValidationContext<T>, BlockValidationContextError> &self,
{ block: &SszBeaconBlock,
parent_block: &SszBeaconBlock,
present_slot: u64,
) -> Result<BeaconBlockValidationContext<T>, BlockValidationContextError> {
/* /*
* Load the crystallized state for this block from our caches. * Load the crystallized state for this block from our caches.
* *
* Fail if the crystallized state is unknown. * Fail if the crystallized state is unknown.
*/ */
let cry_state_root = Hash256::from(block.cry_state_root()); let cry_state_root = Hash256::from(parent_block.cry_state_root());
let cry_state = self.crystallized_states.get(&cry_state_root) let cry_state = self
.crystallized_states
.get(&cry_state_root)
.ok_or(BlockValidationContextError::UnknownCrystallizedState)?; .ok_or(BlockValidationContextError::UnknownCrystallizedState)?;
/* /*
@ -54,8 +48,10 @@ impl<T> BeaconChain<T>
* *
* Fail if the active state is unknown. * Fail if the active state is unknown.
*/ */
let act_state_root = Hash256::from(block.act_state_root()); let act_state_root = Hash256::from(parent_block.act_state_root());
let act_state = self.active_states.get(&act_state_root) let act_state = self
.active_states
.get(&act_state_root)
.ok_or(BlockValidationContextError::UnknownActiveState)?; .ok_or(BlockValidationContextError::UnknownActiveState)?;
/* /*
@ -63,16 +59,21 @@ impl<T> BeaconChain<T>
* the hash of this block from the database * the hash of this block from the database
*/ */
let last_justified_slot = cry_state.last_justified_slot; let last_justified_slot = cry_state.last_justified_slot;
let parent_block_hash = block.parent_hash() let parent_block_hash = block
.parent_hash()
.ok_or(BlockValidationContextError::NoParentHash)?; .ok_or(BlockValidationContextError::NoParentHash)?;
let (last_justified_block_hash, _) = self.store.block.block_at_slot( let (last_justified_block_hash, _) = self
&parent_block_hash, last_justified_slot)? .store
.block
.block_at_slot(&parent_block_hash, last_justified_slot)?
.ok_or(BlockValidationContextError::UnknownJustifiedBlock)?; .ok_or(BlockValidationContextError::UnknownJustifiedBlock)?;
/* /*
* Load the attester and proposer maps for the crystallized state. * Load the attester and proposer maps for the crystallized state.
*/ */
let (attester_map, proposer_map) = self.attester_proposer_maps.get(&cry_state_root) let (attester_map, proposer_map) = self
.attester_proposer_maps
.get(&cry_state_root)
.ok_or(BlockValidationContextError::UnknownAttesterProposerMaps)?; .ok_or(BlockValidationContextError::UnknownAttesterProposerMaps)?;
Ok(BeaconBlockValidationContext { Ok(BeaconBlockValidationContext {

View File

@ -1,98 +1,219 @@
use super::{ use super::block_context::BlockValidationContextError;
BeaconChain, use super::state_transition::StateTransitionError;
ClientDB, use super::BeaconChain;
}; use db::{ClientDB, DBError};
use super::block_context::{ use naive_fork_choice::{naive_fork_choice, ForkChoiceError};
BlockValidationContextError, use ssz_helpers::ssz_beacon_block::{SszBeaconBlock, SszBeaconBlockError};
}; use types::Hash256;
use ssz_helpers::ssz_beacon_block::{ use validation::block_validation::SszBeaconBlockValidationError;
SszBeaconBlock,
SszBeaconBlockError,
};
use types::{
Hash256,
};
use validation::block_validation::{
BeaconBlockStatus,
SszBeaconBlockValidationError,
};
pub enum BlockProcessingOutcome { pub enum BlockProcessingOutcome {
BlockAlreadyKnown, BlockAlreadyKnown,
NewCanonicalBlock, NewCanonicalBlock,
NewReorgBlock,
NewForkBlock, NewForkBlock,
} }
pub enum BlockProcessingError { pub enum BlockProcessingError {
ContextGenerationError(BlockValidationContextError), ParentBlockNotFound,
ActiveStateRootInvalid,
CrystallizedStateRootInvalid,
NoHeadHashes,
ForkChoiceFailed(ForkChoiceError),
ContextGenerationFailed(BlockValidationContextError),
DeserializationFailed(SszBeaconBlockError), DeserializationFailed(SszBeaconBlockError),
ValidationFailed(SszBeaconBlockValidationError), ValidationFailed(SszBeaconBlockValidationError),
StateTransitionFailed(StateTransitionError),
DBError(String),
} }
impl<T> BeaconChain<T> impl<T> BeaconChain<T>
where T: ClientDB + Sized where
T: ClientDB + Sized,
{ {
pub fn process_block(&mut self, ssz: &[u8], present_slot: u64) pub fn process_block(
-> Result<(BlockProcessingOutcome, Hash256), BlockProcessingError> &mut self,
{ ssz: &[u8],
present_slot: u64,
) -> Result<(BlockProcessingOutcome, Hash256), BlockProcessingError> {
/* /*
* Generate a SszBlock to read directly from the serialized SSZ. * Generate a SszBlock to read directly from the serialized SSZ.
*/ */
let ssz_block = SszBeaconBlock::from_slice(ssz)?; let ssz_block = SszBeaconBlock::from_slice(ssz)?;
let block_hash = Hash256::from(&ssz_block.block_hash()[..]); let block_hash = Hash256::from(&ssz_block.block_hash()[..]);
let parent_hash = ssz_block.parent_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::ValidationFailed( .ok_or(BlockProcessingError::ValidationFailed(
SszBeaconBlockValidationError::UnknownParentHash))?; SszBeaconBlockValidationError::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. * Generate the context in which to validate this block.
*/ */
let validation_context = self.block_validation_context(&ssz_block, present_slot)?; 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. * Validate the block against the context, checking signatures, parent_hashes, etc.
*/ */
let (block_status, block) = validation_context.validate_ssz_block(&block_hash, &block)?; let block = validation_context.validate_ssz_block(&ssz_block)?;
match block_status { 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.
*/ */
BeaconBlockStatus::KnownBlock => { let act_state = self
Ok((BlockProcessingOutcome::BlockAlreadyKnown, block_hash)) .active_states
} .get(&block.active_state_root)
BeaconBlockStatus::NewBlock => { .ok_or(BlockValidationContextError::UnknownActiveState)?;
let head_hash_index = { let cry_state = self
match self.head_block_hashes.iter().position(|x| *x == Hash256::from(parent_hash)) { .crystallized_states
Some(i) => i, .get(&block.crystallized_state_root)
None => { .ok_or(BlockValidationContextError::UnknownCrystallizedState)?;
self.head_block_hashes.push(block_hash);
self.head_block_hashes.len() - 1
}
}
};
if head_hash_index == self.canonical_head_block_hash { self.transition_states(act_state, cry_state, &block, &block_hash)?
Ok((BlockProcessingOutcome::NewCanonicalBlock, block_hash)) };
} else {
Ok((BlockProcessingOutcome::NewForkBlock, 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))
} }
} }
} }
pub fn extend_chain(
&self,
block: &Block,
block_hash: &Hash256,
head_hash_index: usize)
-> Result<>
} }
impl From<BlockValidationContextError> for BlockProcessingError { impl From<BlockValidationContextError> for BlockProcessingError {
fn from(e: BlockValidationContextError) -> Self { fn from(e: BlockValidationContextError) -> Self {
BlockProcessingError::ContextGenerationError(e) BlockProcessingError::ContextGenerationFailed(e)
} }
} }
@ -102,8 +223,26 @@ impl From<SszBeaconBlockError> for BlockProcessingError {
} }
} }
impl From<DBError> for BlockProcessingError {
fn from(e: DBError) -> Self {
BlockProcessingError::DBError(e.message)
}
}
impl From<ForkChoiceError> for BlockProcessingError {
fn from(e: ForkChoiceError) -> Self {
BlockProcessingError::ForkChoiceFailed(e)
}
}
impl From<SszBeaconBlockValidationError> for BlockProcessingError { impl From<SszBeaconBlockValidationError> for BlockProcessingError {
fn from(e: SszBeaconBlockValidationError) -> Self { fn from(e: SszBeaconBlockValidationError) -> Self {
BlockProcessingError::ValidationFailed(e) BlockProcessingError::ValidationFailed(e)
} }
} }
impl From<StateTransitionError> for BlockProcessingError {
fn from(e: StateTransitionError) -> Self {
BlockProcessingError::StateTransitionFailed(e)
}
}

View File

@ -1,20 +1,7 @@
use types::{ use super::{ActiveState, BeaconChainError, ChainConfig, CrystallizedState};
CrosslinkRecord, use types::{CrosslinkRecord, Hash256, ValidatorStatus};
Hash256,
ValidatorRegistration,
ValidatorStatus,
};
use super::{
ActiveState,
CrystallizedState,
BeaconChainError,
ChainConfig,
};
use validator_induction::ValidatorInductor; use validator_induction::ValidatorInductor;
use validator_shuffling::{ use validator_shuffling::{shard_and_committees_for_cycle, ValidatorAssignmentError};
shard_and_committees_for_cycle,
ValidatorAssignmentError,
};
pub const INITIAL_FORK_VERSION: u32 = 0; pub const INITIAL_FORK_VERSION: u32 = 0;
@ -27,9 +14,9 @@ impl From<ValidatorAssignmentError> for BeaconChainError {
/// Initialize a new ChainHead with genesis parameters. /// Initialize a new ChainHead with genesis parameters.
/// ///
/// Used when syncing a chain from scratch. /// Used when syncing a chain from scratch.
pub fn genesis_states(config: &ChainConfig) pub fn genesis_states(
-> Result<(ActiveState, CrystallizedState), ValidatorAssignmentError> config: &ChainConfig,
{ ) -> Result<(ActiveState, CrystallizedState), ValidatorAssignmentError> {
/* /*
* Parse the ValidatorRegistrations into ValidatorRecords and induct them. * Parse the ValidatorRegistrations into ValidatorRecords and induct them.
* *
@ -39,7 +26,7 @@ pub fn genesis_states(config: &ChainConfig)
let mut inductor = ValidatorInductor::new(0, config.shard_count, vec![]); let mut inductor = ValidatorInductor::new(0, config.shard_count, vec![]);
for registration in &config.initial_validators { for registration in &config.initial_validators {
let _ = inductor.induct(&registration, ValidatorStatus::Active); let _ = inductor.induct(&registration, ValidatorStatus::Active);
}; }
inductor.to_vec() inductor.to_vec()
}; };
@ -107,21 +94,14 @@ pub fn genesis_states(config: &ChainConfig)
Ok((active_state, crystallized_state)) Ok((active_state, crystallized_state))
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
extern crate validator_induction;
extern crate bls; extern crate bls;
extern crate validator_induction;
use self::bls::{create_proof_of_possession, Keypair};
use super::*; use super::*;
use self::bls::{ use types::{Address, Hash256, ValidatorRegistration};
create_proof_of_possession,
Keypair,
};
use types::{
Hash256,
Address,
};
#[test] #[test]
fn test_genesis_no_validators() { fn test_genesis_no_validators() {
@ -140,7 +120,10 @@ mod tests {
assert_eq!(cry.last_finalized_slot, 0); assert_eq!(cry.last_finalized_slot, 0);
assert_eq!(cry.last_justified_slot, 0); assert_eq!(cry.last_justified_slot, 0);
assert_eq!(cry.justified_streak, 0); assert_eq!(cry.justified_streak, 0);
assert_eq!(cry.shard_and_committee_for_slots.len(), (config.cycle_length as usize) * 2); assert_eq!(
cry.shard_and_committee_for_slots.len(),
(config.cycle_length as usize) * 2
);
assert_eq!(cry.deposits_penalized_in_period.len(), 0); assert_eq!(cry.deposits_penalized_in_period.len(), 0);
assert_eq!(cry.validator_set_delta_hash_chain, Hash256::zero()); assert_eq!(cry.validator_set_delta_hash_chain, Hash256::zero());
assert_eq!(cry.pre_fork_version, INITIAL_FORK_VERSION); assert_eq!(cry.pre_fork_version, INITIAL_FORK_VERSION);
@ -149,7 +132,10 @@ mod tests {
assert_eq!(act.pending_attestations.len(), 0); assert_eq!(act.pending_attestations.len(), 0);
assert_eq!(act.pending_specials.len(), 0); assert_eq!(act.pending_specials.len(), 0);
assert_eq!(act.recent_block_hashes, vec![Hash256::zero(); config.cycle_length as usize]); assert_eq!(
act.recent_block_hashes,
vec![Hash256::zero(); config.cycle_length as usize]
);
assert_eq!(act.randao_mix, Hash256::zero()); assert_eq!(act.randao_mix, Hash256::zero());
} }
@ -160,7 +146,7 @@ mod tests {
withdrawal_shard: 0, withdrawal_shard: 0,
withdrawal_address: Address::random(), withdrawal_address: Address::random(),
randao_commitment: Hash256::random(), randao_commitment: Hash256::random(),
proof_of_possession: create_proof_of_possession(&keypair) proof_of_possession: create_proof_of_possession(&keypair),
} }
} }
@ -189,16 +175,19 @@ mod tests {
let mut bad_v = random_registration(); let mut bad_v = random_registration();
let bad_kp = Keypair::random(); let bad_kp = Keypair::random();
bad_v.proof_of_possession = create_proof_of_possession(&bad_kp); bad_v.proof_of_possession = create_proof_of_possession(&bad_kp);
config.initial_validators.push(bad_v); config.initial_validators.push(bad_v);
let mut bad_v = random_registration(); let mut bad_v = random_registration();
bad_v.withdrawal_shard = config.shard_count + 1; bad_v.withdrawal_shard = config.shard_count + 1;
config.initial_validators.push(bad_v); config.initial_validators.push(bad_v);
let (_, cry) = genesis_states(&config).unwrap(); let (_, cry) = genesis_states(&config).unwrap();
assert!(config.initial_validators.len() != good_validator_count, "test is invalid"); assert!(
config.initial_validators.len() != good_validator_count,
"test is invalid"
);
assert_eq!(cry.validators.len(), good_validator_count); assert_eq!(cry.validators.len(), good_validator_count);
} }
} }

View File

@ -1,33 +1,27 @@
extern crate db; extern crate db;
extern crate types; extern crate naive_fork_choice;
extern crate state_transition;
extern crate ssz;
extern crate ssz_helpers; extern crate ssz_helpers;
extern crate types;
extern crate validation; extern crate validation;
extern crate validator_induction; extern crate validator_induction;
extern crate validator_shuffling; extern crate validator_shuffling;
mod stores;
mod block_context; mod block_context;
mod block_processing; mod block_processing;
mod maps;
mod genesis; mod genesis;
mod maps;
mod transition;
mod stores;
use db::ClientDB; use db::ClientDB;
use genesis::genesis_states; use genesis::genesis_states;
use maps::{ use maps::{generate_attester_and_proposer_maps, AttesterAndProposerMapError};
generate_attester_and_proposer_maps,
AttesterAndProposerMapError,
};
use std::collections::HashMap; use std::collections::HashMap;
use std::sync::Arc; use std::sync::Arc;
use stores::BeaconChainStore; use stores::BeaconChainStore;
use types::{ use types::{ActiveState, AttesterMap, ChainConfig, CrystallizedState, Hash256, ProposerMap};
ActiveState,
AttesterMap,
ChainConfig,
CrystallizedState,
Hash256,
ProposerMap,
};
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub enum BeaconChainError { pub enum BeaconChainError {
@ -37,12 +31,6 @@ pub enum BeaconChainError {
DBError(String), DBError(String),
} }
impl From<AttesterAndProposerMapError> for BeaconChainError {
fn from(e: AttesterAndProposerMapError) -> BeaconChainError {
BeaconChainError::UnableToGenerateMaps(e)
}
}
pub struct BeaconChain<T: ClientDB + Sized> { pub struct BeaconChain<T: ClientDB + Sized> {
/// The last slot which has been finalized, this is common to all forks. /// The last slot which has been finalized, this is common to all forks.
pub last_finalized_slot: u64, pub last_finalized_slot: u64,
@ -63,11 +51,10 @@ pub struct BeaconChain<T: ClientDB + Sized> {
} }
impl<T> BeaconChain<T> impl<T> BeaconChain<T>
where T: ClientDB + Sized where
T: ClientDB + Sized,
{ {
pub fn new(store: BeaconChainStore<T>, config: ChainConfig) pub fn new(store: BeaconChainStore<T>, config: ChainConfig) -> Result<Self, BeaconChainError> {
-> Result<Self, BeaconChainError>
{
if config.initial_validators.is_empty() { if config.initial_validators.is_empty() {
return Err(BeaconChainError::InsufficientValidators); return Err(BeaconChainError::InsufficientValidators);
} }
@ -82,15 +69,18 @@ impl<T> BeaconChain<T>
let mut attester_proposer_maps = HashMap::new(); let mut attester_proposer_maps = HashMap::new();
let (attester_map, proposer_map) = generate_attester_and_proposer_maps( let (attester_map, proposer_map) = generate_attester_and_proposer_maps(
&crystallized_state.shard_and_committee_for_slots, 0)?; &crystallized_state.shard_and_committee_for_slots,
0,
)?;
active_states.insert(canonical_latest_block_hash, active_state); active_states.insert(canonical_latest_block_hash, active_state);
crystallized_states.insert(canonical_latest_block_hash, crystallized_state); crystallized_states.insert(canonical_latest_block_hash, crystallized_state);
attester_proposer_maps.insert( attester_proposer_maps.insert(
canonical_latest_block_hash, canonical_latest_block_hash,
(Arc::new(attester_map), Arc::new(proposer_map))); (Arc::new(attester_map), Arc::new(proposer_map)),
);
Ok(Self{ Ok(Self {
last_finalized_slot: 0, last_finalized_slot: 0,
head_block_hashes, head_block_hashes,
canonical_head_block_hash, canonical_head_block_hash,
@ -102,19 +92,24 @@ impl<T> BeaconChain<T>
}) })
} }
pub fn canonical_block_hash(self) -> Hash256 { pub fn canonical_block_hash(&self) -> Hash256 {
self.head_block_hashes[self.canonical_head_block_hash] self.head_block_hashes[self.canonical_head_block_hash]
} }
} }
impl From<AttesterAndProposerMapError> for BeaconChainError {
fn from(e: AttesterAndProposerMapError) -> BeaconChainError {
BeaconChainError::UnableToGenerateMaps(e)
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::sync::Arc;
use super::*; use super::*;
use types::ValidatorRegistration;
use db::MemoryDB;
use db::stores::*; use db::stores::*;
use db::MemoryDB;
use std::sync::Arc;
use types::ValidatorRegistration;
#[test] #[test]
fn test_new_chain() { fn test_new_chain() {
@ -129,7 +124,9 @@ mod tests {
}; };
for _ in 0..config.cycle_length * 2 { for _ in 0..config.cycle_length * 2 {
config.initial_validators.push(ValidatorRegistration::random()) config
.initial_validators
.push(ValidatorRegistration::random())
} }
let chain = BeaconChain::new(store, config.clone()).unwrap(); let chain = BeaconChain::new(store, config.clone()).unwrap();

View File

@ -1,8 +1,4 @@
use types::{ use types::{AttesterMap, ProposerMap, ShardAndCommittee};
AttesterMap,
ProposerMap,
ShardAndCommittee,
};
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub enum AttesterAndProposerMapError { pub enum AttesterAndProposerMapError {
@ -15,9 +11,8 @@ pub enum AttesterAndProposerMapError {
/// The attester map is used to optimise the lookup of a committee. /// The attester map is used to optimise the lookup of a committee.
pub fn generate_attester_and_proposer_maps( pub fn generate_attester_and_proposer_maps(
shard_and_committee_for_slots: &Vec<Vec<ShardAndCommittee>>, shard_and_committee_for_slots: &Vec<Vec<ShardAndCommittee>>,
start_slot: u64) start_slot: u64,
-> Result<(AttesterMap, ProposerMap), AttesterAndProposerMapError> ) -> Result<(AttesterMap, ProposerMap), AttesterAndProposerMapError> {
{
let mut attester_map = AttesterMap::new(); let mut attester_map = AttesterMap::new();
let mut proposer_map = ProposerMap::new(); let mut proposer_map = ProposerMap::new();
for (i, slot) in shard_and_committee_for_slots.iter().enumerate() { for (i, slot) in shard_and_committee_for_slots.iter().enumerate() {
@ -25,10 +20,12 @@ pub fn generate_attester_and_proposer_maps(
* Store the proposer for the block. * Store the proposer for the block.
*/ */
let slot_number = (i as u64).saturating_add(start_slot); let slot_number = (i as u64).saturating_add(start_slot);
let first_committee = &slot.get(0) let first_committee = &slot
.get(0)
.ok_or(AttesterAndProposerMapError::NoShardAndCommitteeForSlot)? .ok_or(AttesterAndProposerMapError::NoShardAndCommitteeForSlot)?
.committee; .committee;
let proposer_index = (slot_number as usize).checked_rem(first_committee.len()) let proposer_index = (slot_number as usize)
.checked_rem(first_committee.len())
.ok_or(AttesterAndProposerMapError::NoAvailableProposer)?; .ok_or(AttesterAndProposerMapError::NoAvailableProposer)?;
proposer_map.insert(slot_number, first_committee[proposer_index]); proposer_map.insert(slot_number, first_committee[proposer_index]);
@ -39,7 +36,7 @@ pub fn generate_attester_and_proposer_maps(
let committee = shard_and_committee.committee.clone(); let committee = shard_and_committee.committee.clone();
attester_map.insert((slot_number, shard_and_committee.shard), committee); attester_map.insert((slot_number, shard_and_committee.shard), committee);
} }
}; }
Ok((attester_map, proposer_map)) Ok((attester_map, proposer_map))
} }
@ -47,12 +44,12 @@ pub fn generate_attester_and_proposer_maps(
mod tests { mod tests {
use super::*; use super::*;
fn sac_generator(shard_count: u16, fn sac_generator(
slot_count: usize, shard_count: u16,
sac_per_slot: usize, slot_count: usize,
committee_size: usize) sac_per_slot: usize,
-> Vec<Vec<ShardAndCommittee>> committee_size: usize,
{ ) -> Vec<Vec<ShardAndCommittee>> {
let mut shard = 0; let mut shard = 0;
let mut validator = 0; let mut validator = 0;
let mut cycle = vec![]; let mut cycle = vec![];
@ -80,14 +77,20 @@ mod tests {
fn test_attester_proposer_maps_empty_slots() { fn test_attester_proposer_maps_empty_slots() {
let sac = sac_generator(4, 4, 0, 1); let sac = sac_generator(4, 4, 0, 1);
let result = generate_attester_and_proposer_maps(&sac, 0); let result = generate_attester_and_proposer_maps(&sac, 0);
assert_eq!(result, Err(AttesterAndProposerMapError::NoShardAndCommitteeForSlot)); assert_eq!(
result,
Err(AttesterAndProposerMapError::NoShardAndCommitteeForSlot)
);
} }
#[test] #[test]
fn test_attester_proposer_maps_empty_committees() { fn test_attester_proposer_maps_empty_committees() {
let sac = sac_generator(4, 4, 1, 0); let sac = sac_generator(4, 4, 1, 0);
let result = generate_attester_and_proposer_maps(&sac, 0); let result = generate_attester_and_proposer_maps(&sac, 0);
assert_eq!(result, Err(AttesterAndProposerMapError::NoAvailableProposer)); assert_eq!(
result,
Err(AttesterAndProposerMapError::NoAvailableProposer)
);
} }
#[test] #[test]

View File

@ -1,11 +1,5 @@
use db::{ use db::stores::{BeaconBlockStore, PoWChainStore, ValidatorStore};
ClientDB, use db::ClientDB;
};
use db::stores::{
BeaconBlockStore,
PoWChainStore,
ValidatorStore,
};
use std::sync::Arc; use std::sync::Arc;
pub struct BeaconChainStore<T: ClientDB + Sized> { pub struct BeaconChainStore<T: ClientDB + Sized> {

View File

@ -0,0 +1,29 @@
use super::BeaconChain;
use db::ClientDB;
use state_transition::{extend_active_state, StateTransitionError};
use types::{ActiveState, BeaconBlock, CrystallizedState, Hash256};
impl<T> BeaconChain<T>
where
T: ClientDB + Sized,
{
pub(crate) fn transition_states(
&self,
act_state: &ActiveState,
cry_state: &CrystallizedState,
block: &BeaconBlock,
block_hash: &Hash256,
) -> Result<(ActiveState, Option<CrystallizedState>), StateTransitionError> {
let state_recalc_distance = block
.slot
.checked_sub(cry_state.last_state_recalculation_slot)
.ok_or(StateTransitionError::BlockSlotBeforeRecalcSlot)?;
if state_recalc_distance >= u64::from(self.config.cycle_length) {
panic!("Not implemented!")
} else {
let new_act_state = extend_active_state(act_state, block, block_hash)?;
Ok((new_act_state, None))
}
}
}

View File

@ -0,0 +1,7 @@
extern crate chain;
#[cfg(test)]
mod tests {
use chain::{BeaconChain, BeaconChainError};
}

View File

@ -0,0 +1,9 @@
[package]
name = "naive_fork_choice"
version = "0.1.0"
authors = ["Paul Hauner <paul@paulhauner.com>"]
[dependencies]
db = { path = "../../lighthouse/db" }
ssz = { path = "../utils/ssz" }
types = { path = "../types" }

View File

@ -0,0 +1,97 @@
extern crate db;
extern crate ssz;
extern crate types;
use db::stores::BeaconBlockStore;
use db::{ClientDB, DBError};
use ssz::{Decodable, DecodeError};
use std::sync::Arc;
use types::{BeaconBlock, Hash256};
pub enum ForkChoiceError {
BadSszInDatabase,
MissingBlock,
DBError(String),
}
pub fn naive_fork_choice<T>(
head_block_hashes: &Vec<Hash256>,
block_store: Arc<BeaconBlockStore<T>>,
) -> Result<Option<usize>, ForkChoiceError>
where
T: ClientDB + Sized,
{
let mut head_blocks: Vec<(usize, BeaconBlock)> = vec![];
/*
* Load all the head_block hashes from the DB as SszBeaconBlocks.
*/
for (index, block_hash) in head_block_hashes.iter().enumerate() {
let ssz = block_store
.get_serialized_block(&block_hash.to_vec()[..])?
.ok_or(ForkChoiceError::MissingBlock)?;
let (block, _) = BeaconBlock::ssz_decode(&ssz, 0)?;
head_blocks.push((index, block));
}
/*
* Loop through all the head blocks and find the highest slot.
*/
let highest_slot: Option<u64> = None;
for (_, block) in &head_blocks {
let slot = block.slot;
match highest_slot {
None => Some(slot),
Some(winning_slot) => {
if slot > winning_slot {
Some(slot)
} else {
Some(winning_slot)
}
}
};
}
/*
* Loop through all the highest blocks and sort them by highest hash.
*
* Ultimately, the index of the head_block hash with the highest slot and highest block
* hash will be the winner.
*/
match highest_slot {
None => Ok(None),
Some(highest_slot) => {
let mut highest_blocks = vec![];
for (index, block) in head_blocks {
if block.slot == highest_slot {
highest_blocks.push((index, block))
}
}
highest_blocks.sort_by(|a, b| head_block_hashes[a.0].cmp(&head_block_hashes[b.0]));
let (index, _) = highest_blocks[0];
Ok(Some(index))
}
}
}
impl From<DecodeError> for ForkChoiceError {
fn from(_: DecodeError) -> Self {
ForkChoiceError::BadSszInDatabase
}
}
impl From<DBError> for ForkChoiceError {
fn from(e: DBError) -> Self {
ForkChoiceError::DBError(e.message)
}
}
#[cfg(test)]
mod tests {
#[test]
fn test_naive_fork_choice() {
assert_eq!(2 + 2, 4);
}
}

View File

@ -0,0 +1,7 @@
[package]
name = "state-transition"
version = "0.1.0"
authors = ["Paul Hauner <paul@paulhauner.com>"]
[dependencies]
types = { path = "../types" }

View File

@ -0,0 +1,194 @@
extern crate types;
use types::{ActiveState, BeaconBlock, Hash256};
#[derive(Debug, PartialEq)]
pub enum StateTransitionError {
BlockSlotBeforeRecalcSlot,
InvalidParentHashes,
DBError(String),
}
pub fn extend_active_state(
act_state: &ActiveState,
block: &BeaconBlock,
block_hash: &Hash256,
) -> Result<ActiveState, StateTransitionError> {
/*
* Extend the pending attestations in the active state with the new attestations included
* in the block.
*
* Using the concat method to avoid reallocations.
*/
let pending_attestations =
[&act_state.pending_attestations[..], &block.attestations[..]].concat();
/*
* Extend the pending specials in the active state with the new specials included in the
* block.
*
* Using the concat method to avoid reallocations.
*/
let pending_specials = [&act_state.pending_specials[..], &block.specials[..]].concat();
/*
* Update the active state recent_block_hashes:
*
* - Drop the hash from the earliest position.
* - Push the block_hash into the latest position.
*
* Using the concat method to avoid reallocations.
*/
let (_first_hash, last_hashes) = act_state
.recent_block_hashes
.split_first()
.ok_or(StateTransitionError::InvalidParentHashes)?;
let new_hash = &[block_hash.clone()];
let recent_block_hashes = [&last_hashes, &new_hash[..]].concat();
/*
* The new `randao_mix` is set to the XOR of the previous active state randao mix and the
* randao reveal in this block.
*/
let randao_mix = act_state.randao_mix ^ block.randao_reveal;
Ok(ActiveState {
pending_attestations,
pending_specials,
recent_block_hashes,
randao_mix,
})
}
#[cfg(test)]
mod tests {
use super::*;
use types::SpecialRecord;
fn empty_active_state() -> ActiveState {
ActiveState {
pending_attestations: vec![],
pending_specials: vec![],
recent_block_hashes: vec![],
randao_mix: Hash256::zero(),
}
}
#[test]
fn test_extend_active_state_minimal() {
let mut act_state = empty_active_state();
let parent_hash = Hash256::from("parent_hash".as_bytes());
act_state.recent_block_hashes = vec![parent_hash];
let block = BeaconBlock::zero();
let block_hash = Hash256::from("block_hash".as_bytes());
let new_act_state = extend_active_state(&act_state, &block, &block_hash).unwrap();
assert_eq!(new_act_state.pending_attestations, vec![]);
assert_eq!(new_act_state.pending_specials, vec![]);
assert_eq!(new_act_state.recent_block_hashes, vec![block_hash]);
assert_eq!(new_act_state.randao_mix, Hash256::zero());
}
#[test]
fn test_extend_active_state_specials() {
let mut act_state = empty_active_state();
let parent_hash = Hash256::from("parent_hash".as_bytes());
act_state.recent_block_hashes = vec![parent_hash];
let mut block = BeaconBlock::zero();
let special = SpecialRecord {
kind: 0,
data: vec![42, 42],
};
block.specials.push(special.clone());
let block_hash = Hash256::from("block_hash".as_bytes());
let new_act_state = extend_active_state(&act_state, &block, &block_hash).unwrap();
assert_eq!(new_act_state.pending_attestations, vec![]);
assert_eq!(new_act_state.pending_specials, vec![special.clone()]);
assert_eq!(new_act_state.recent_block_hashes, vec![block_hash]);
assert_eq!(new_act_state.randao_mix, Hash256::zero());
let new_new_act_state = extend_active_state(&new_act_state, &block, &block_hash).unwrap();
assert_eq!(new_new_act_state.pending_attestations, vec![]);
assert_eq!(
new_new_act_state.pending_specials,
vec![special.clone(), special.clone()]
);
assert_eq!(new_new_act_state.recent_block_hashes, vec![block_hash]);
assert_eq!(new_new_act_state.randao_mix, Hash256::zero());
}
#[test]
fn test_extend_active_state_empty_recent_block_hashes() {
let act_state = empty_active_state();
let block = BeaconBlock::zero();
let block_hash = Hash256::from("block_hash".as_bytes());
let result = extend_active_state(&act_state, &block, &block_hash);
assert_eq!(result, Err(StateTransitionError::InvalidParentHashes));
}
#[test]
fn test_extend_active_recent_block_hashes() {
let mut act_state = empty_active_state();
let parent_hashes = vec![
Hash256::from("one".as_bytes()),
Hash256::from("two".as_bytes()),
Hash256::from("three".as_bytes()),
];
act_state.recent_block_hashes = parent_hashes.clone();
let block = BeaconBlock::zero();
let block_hash = Hash256::from("four".as_bytes());
let new_act_state = extend_active_state(&act_state, &block, &block_hash).unwrap();
assert_eq!(new_act_state.pending_attestations, vec![]);
assert_eq!(new_act_state.pending_specials, vec![]);
assert_eq!(
new_act_state.recent_block_hashes,
vec![
Hash256::from("two".as_bytes()),
Hash256::from("three".as_bytes()),
Hash256::from("four".as_bytes()),
]
);
assert_eq!(new_act_state.randao_mix, Hash256::zero());
}
#[test]
fn test_extend_active_state_randao() {
let mut act_state = empty_active_state();
let parent_hash = Hash256::from("parent_hash".as_bytes());
act_state.recent_block_hashes = vec![parent_hash];
act_state.randao_mix = Hash256::from(0b00000000);
let mut block = BeaconBlock::zero();
block.randao_reveal = Hash256::from(0b00000001);
let block_hash = Hash256::from("block_hash".as_bytes());
let new_act_state = extend_active_state(&act_state, &block, &block_hash).unwrap();
assert_eq!(new_act_state.pending_attestations, vec![]);
assert_eq!(new_act_state.pending_specials, vec![]);
assert_eq!(new_act_state.recent_block_hashes, vec![block_hash]);
assert_eq!(new_act_state.randao_mix, Hash256::from(0b00000001));
}
}

View File

@ -1,8 +1,5 @@
use super::Hash256; use super::Hash256;
use super::{ use super::{AttestationRecord, SpecialRecord};
AttestationRecord,
SpecialRecord,
};
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub struct ActiveState { pub struct ActiveState {
@ -11,3 +8,10 @@ pub struct ActiveState {
pub recent_block_hashes: Vec<Hash256>, pub recent_block_hashes: Vec<Hash256>,
pub randao_mix: Hash256, pub randao_mix: Hash256,
} }
impl ActiveState {
// TODO: implement this.
pub fn canonical_root(&self) -> Hash256 {
Hash256::zero()
}
}

View File

@ -1,9 +1,8 @@
use super::validator_record::ValidatorRecord;
use super::crosslink_record::CrosslinkRecord; use super::crosslink_record::CrosslinkRecord;
use super::shard_and_committee::ShardAndCommittee; use super::shard_and_committee::ShardAndCommittee;
use super::validator_record::ValidatorRecord;
use super::Hash256; use super::Hash256;
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub struct CrystallizedState { pub struct CrystallizedState {
pub validator_set_change_slot: u64, pub validator_set_change_slot: u64,
@ -20,3 +19,10 @@ pub struct CrystallizedState {
pub post_fork_version: u32, pub post_fork_version: u32,
pub fork_slot_number: u32, pub fork_slot_number: u32,
} }
impl CrystallizedState {
// TODO: implement this.
pub fn canonical_root(&self) -> Hash256 {
Hash256::zero()
}
}

View File

@ -40,6 +40,7 @@ const CRYSTALLIZED_STATE_BYTES: usize = HASH_SIZE;
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub struct SszBeaconBlock<'a> { pub struct SszBeaconBlock<'a> {
ssz: &'a [u8], ssz: &'a [u8],
block_ssz_len: usize,
// Ancestors // Ancestors
ancestors_position: usize, ancestors_position: usize,
ancestors_len: usize, ancestors_len: usize,
@ -117,6 +118,7 @@ impl<'a> SszBeaconBlock<'a> {
Ok(Self{ Ok(Self{
ssz: &untrimmed_ssz[0..block_ssz_len], ssz: &untrimmed_ssz[0..block_ssz_len],
block_ssz_len,
ancestors_position, ancestors_position,
ancestors_len, ancestors_len,
attestations_position, attestations_position,
@ -129,9 +131,16 @@ impl<'a> SszBeaconBlock<'a> {
pub fn len(&self) -> usize { self.ssz.len() } pub fn len(&self) -> usize { self.ssz.len() }
pub fn is_empty(&self) -> bool { self.ssz.is_empty() } pub fn is_empty(&self) -> bool { self.ssz.is_empty() }
/// Returns this block as ssz.
///
/// Does not include any excess ssz bytes that were supplied to this struct.
pub fn block_ssz(&self) -> &'a [u8] {
&self.ssz[0..self.block_ssz_len]
}
/// Return the canonical hash for this block. /// Return the canonical hash for this block.
pub fn block_hash(&self) -> Vec<u8> { pub fn block_hash(&self) -> Vec<u8> {
canonical_hash(self.ssz) canonical_hash(&self.ssz)
} }
/// Return the bytes representing `ancestor_hashes[0]`. /// Return the bytes representing `ancestor_hashes[0]`.

View File

@ -2,49 +2,17 @@ extern crate rayon;
use self::rayon::prelude::*; use self::rayon::prelude::*;
use std::sync::{ use super::attestation_validation::{AttestationValidationContext, AttestationValidationError};
Arc, use super::db::stores::{BeaconBlockStore, PoWChainStore, ValidatorStore};
RwLock, use super::db::{ClientDB, DBError};
}; use super::ssz::{Decodable, DecodeError};
use super::attestation_validation::{
AttestationValidationContext,
AttestationValidationError,
};
use super::types::{
AttestationRecord,
AttesterMap,
BeaconBlock,
ProposerMap,
};
use super::ssz_helpers::attestation_ssz_splitter::{ use super::ssz_helpers::attestation_ssz_splitter::{
split_one_attestation, split_all_attestations, split_one_attestation, AttestationSplitError,
split_all_attestations,
AttestationSplitError,
};
use super::ssz_helpers::ssz_beacon_block::{
SszBeaconBlock,
SszBeaconBlockError,
};
use super::db::{
ClientDB,
DBError,
};
use super::db::stores::{
BeaconBlockStore,
PoWChainStore,
ValidatorStore,
};
use super::ssz::{
Decodable,
DecodeError,
}; };
use super::ssz_helpers::ssz_beacon_block::{SszBeaconBlock, SszBeaconBlockError};
use super::types::Hash256; use super::types::Hash256;
use super::types::{AttestationRecord, AttesterMap, BeaconBlock, ProposerMap};
#[derive(Debug, PartialEq)] use std::sync::{Arc, RwLock};
pub enum BeaconBlockStatus {
NewBlock,
KnownBlock,
}
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub enum SszBeaconBlockValidationError { pub enum SszBeaconBlockValidationError {
@ -67,7 +35,8 @@ pub enum SszBeaconBlockValidationError {
/// The context against which a block should be validated. /// The context against which a block should be validated.
pub struct BeaconBlockValidationContext<T> pub struct BeaconBlockValidationContext<T>
where T: ClientDB + Sized where
T: ClientDB + Sized,
{ {
/// The slot as determined by the system time. /// The slot as determined by the system time.
pub present_slot: u64, pub present_slot: u64,
@ -94,7 +63,8 @@ pub struct BeaconBlockValidationContext<T>
} }
impl<T> BeaconBlockValidationContext<T> impl<T> BeaconBlockValidationContext<T>
where T: ClientDB where
T: ClientDB,
{ {
/// Validate some SszBeaconBlock against a block validation context. An SszBeaconBlock varies from a BeaconBlock in /// 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. /// that is a read-only structure that reads directly from encoded SSZ.
@ -109,19 +79,13 @@ impl<T> BeaconBlockValidationContext<T>
/// Note: this function does not implement randao_reveal checking as it is not in the /// Note: this function does not implement randao_reveal checking as it is not in the
/// specification. /// specification.
#[allow(dead_code)] #[allow(dead_code)]
pub fn validate_ssz_block(&self, block_hash: &Hash256, b: &SszBeaconBlock) pub fn validate_ssz_block(
-> Result<(BeaconBlockStatus, Option<BeaconBlock>), SszBeaconBlockValidationError> &self,
where T: ClientDB + Sized b: &SszBeaconBlock,
) -> Result<BeaconBlock, SszBeaconBlockValidationError>
where
T: ClientDB + Sized,
{ {
/*
* If this block is already known, return immediately and indicate the the block is
* known. Don't attempt to deserialize the block.
*/
if self.block_store.block_exists(&block_hash)? {
return Ok((BeaconBlockStatus::KnownBlock, None));
}
/* /*
* If the block slot corresponds to a slot in the future, return immediately with an error. * If the block slot corresponds to a slot in the future, return immediately with an error.
* *
@ -173,11 +137,8 @@ impl<T> BeaconBlockValidationContext<T>
* The first attestation must be validated separately as it must contain a signature of the * 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). * proposer of the previous block (this is checked later in this function).
*/ */
let (first_attestation_ssz, next_index) = split_one_attestation( let (first_attestation_ssz, next_index) = split_one_attestation(&attestations_ssz, 0)?;
&attestations_ssz, let (first_attestation, _) = AttestationRecord::ssz_decode(&first_attestation_ssz, 0)?;
0)?;
let (first_attestation, _) = AttestationRecord::ssz_decode(
&first_attestation_ssz, 0)?;
/* /*
* The first attestation may not have oblique hashes. * The first attestation may not have oblique hashes.
@ -197,7 +158,8 @@ impl<T> BeaconBlockValidationContext<T>
* *
* Also, read the slot from the parent block for later use. * Also, read the slot from the parent block for later use.
*/ */
let parent_hash = b.parent_hash() let parent_hash = b
.parent_hash()
.ok_or(SszBeaconBlockValidationError::BadAncestorHashesSsz)?; .ok_or(SszBeaconBlockValidationError::BadAncestorHashesSsz)?;
let parent_block_slot = match self.block_store.get_serialized_block(&parent_hash)? { let parent_block_slot = match self.block_store.get_serialized_block(&parent_hash)? {
None => return Err(SszBeaconBlockValidationError::UnknownParentHash), None => return Err(SszBeaconBlockValidationError::UnknownParentHash),
@ -233,8 +195,8 @@ impl<T> BeaconBlockValidationContext<T>
/* /*
* Validate this first attestation. * Validate this first attestation.
*/ */
let attestation_voters = attestation_validation_context let attestation_voters =
.validate_attestation(&first_attestation)?; attestation_validation_context.validate_attestation(&first_attestation)?;
/* /*
* Attempt to read load the parent block proposer from the proposer map. Return with an * Attempt to read load the parent block proposer from the proposer map. Return with an
@ -243,7 +205,9 @@ impl<T> BeaconBlockValidationContext<T>
* If the signature of proposer for the parent slot was not present in the first (0'th) * If the signature of proposer for the parent slot was not present in the first (0'th)
* attestation of this block, reject the block. * attestation of this block, reject the block.
*/ */
let parent_block_proposer = self.proposer_map.get(&parent_block_slot) let parent_block_proposer = self
.proposer_map
.get(&parent_block_slot)
.ok_or(SszBeaconBlockValidationError::BadProposerMap)?; .ok_or(SszBeaconBlockValidationError::BadProposerMap)?;
if !attestation_voters.contains(&parent_block_proposer) { if !attestation_voters.contains(&parent_block_proposer) {
return Err(SszBeaconBlockValidationError::NoProposerSignature); return Err(SszBeaconBlockValidationError::NoProposerSignature);
@ -253,8 +217,7 @@ impl<T> BeaconBlockValidationContext<T>
* Split the remaining attestations into a vector of slices, each containing * Split the remaining attestations into a vector of slices, each containing
* a single serialized attestation record. * a single serialized attestation record.
*/ */
let other_attestations = split_all_attestations(attestations_ssz, let other_attestations = split_all_attestations(attestations_ssz, next_index)?;
next_index)?;
/* /*
* Verify each other AttestationRecord. * Verify each other AttestationRecord.
@ -278,7 +241,7 @@ impl<T> BeaconBlockValidationContext<T>
*/ */
match failure.read() { match failure.read() {
Ok(ref option) if option.is_none() => (), Ok(ref option) if option.is_none() => (),
_ => return None _ => return None,
} }
/* /*
* If there has not been a failure yet, attempt to serialize and validate the * If there has not been a failure yet, attempt to serialize and validate the
@ -317,22 +280,18 @@ impl<T> BeaconBlockValidationContext<T>
/* /*
* Attestation validation succeded. * Attestation validation succeded.
*/ */
Ok(_) => Some(attestation) Ok(_) => Some(attestation),
} }
} }
} }
}) }).collect();
.collect();
match failure.into_inner() { match failure.into_inner() {
Err(_) => return Err(SszBeaconBlockValidationError::RwLockPoisoned), Err(_) => return Err(SszBeaconBlockValidationError::RwLockPoisoned),
Ok(failure) => { Ok(failure) => match failure {
match failure { Some(error) => return Err(error),
Some(error) => return Err(error), _ => (),
_ => () },
}
}
} }
/* /*
@ -360,7 +319,7 @@ impl<T> BeaconBlockValidationContext<T>
attestations: deserialized_attestations, attestations: deserialized_attestations,
specials, specials,
}; };
Ok((BeaconBlockStatus::NewBlock, Some(block))) Ok(block)
} }
} }
@ -373,8 +332,7 @@ impl From<DBError> for SszBeaconBlockValidationError {
impl From<AttestationSplitError> for SszBeaconBlockValidationError { impl From<AttestationSplitError> for SszBeaconBlockValidationError {
fn from(e: AttestationSplitError) -> Self { fn from(e: AttestationSplitError) -> Self {
match e { match e {
AttestationSplitError::TooShort => AttestationSplitError::TooShort => SszBeaconBlockValidationError::BadAttestationSsz,
SszBeaconBlockValidationError::BadAttestationSsz
} }
} }
} }
@ -382,10 +340,12 @@ impl From<AttestationSplitError> for SszBeaconBlockValidationError {
impl From<SszBeaconBlockError> for SszBeaconBlockValidationError { impl From<SszBeaconBlockError> for SszBeaconBlockValidationError {
fn from(e: SszBeaconBlockError) -> Self { fn from(e: SszBeaconBlockError) -> Self {
match e { match e {
SszBeaconBlockError::TooShort => SszBeaconBlockError::TooShort => {
SszBeaconBlockValidationError::DBError("Bad parent block in db.".to_string()), SszBeaconBlockValidationError::DBError("Bad parent block in db.".to_string())
SszBeaconBlockError::TooLong => }
SszBeaconBlockValidationError::DBError("Bad parent block in db.".to_string()), SszBeaconBlockError::TooLong => {
SszBeaconBlockValidationError::DBError("Bad parent block in db.".to_string())
}
} }
} }
} }
@ -393,10 +353,8 @@ impl From<SszBeaconBlockError> for SszBeaconBlockValidationError {
impl From<DecodeError> for SszBeaconBlockValidationError { impl From<DecodeError> for SszBeaconBlockValidationError {
fn from(e: DecodeError) -> Self { fn from(e: DecodeError) -> Self {
match e { match e {
DecodeError::TooShort => DecodeError::TooShort => SszBeaconBlockValidationError::BadAttestationSsz,
SszBeaconBlockValidationError::BadAttestationSsz, DecodeError::TooLong => SszBeaconBlockValidationError::BadAttestationSsz,
DecodeError::TooLong =>
SszBeaconBlockValidationError::BadAttestationSsz,
} }
} }
} }

View File

@ -1,35 +1,14 @@
use std::sync::Arc; use std::sync::Arc;
use super::attestation_validation::helpers::{ use super::attestation_validation::helpers::{generate_attestation, insert_justified_block_hash};
generate_attestation, use super::bls::Keypair;
insert_justified_block_hash, use super::db::stores::{BeaconBlockStore, PoWChainStore, ValidatorStore};
}; use super::db::MemoryDB;
use super::bls::{ use super::ssz::SszStream;
Keypair,
};
use super::db::{
MemoryDB,
};
use super::db::stores::{
BeaconBlockStore,
PoWChainStore,
ValidatorStore,
};
use super::types::{
AttestationRecord,
AttesterMap,
BeaconBlock,
Hash256,
ProposerMap,
};
use super::ssz_helpers::ssz_beacon_block::SszBeaconBlock; use super::ssz_helpers::ssz_beacon_block::SszBeaconBlock;
use super::types::{AttestationRecord, AttesterMap, BeaconBlock, Hash256, ProposerMap};
use super::validation::block_validation::{ use super::validation::block_validation::{
BeaconBlockValidationContext, BeaconBlockValidationContext, SszBeaconBlockValidationError,
SszBeaconBlockValidationError,
BeaconBlockStatus,
};
use super::ssz::{
SszStream,
}; };
#[derive(Debug)] #[derive(Debug)]
@ -74,9 +53,15 @@ type ParentHashes = Vec<Hash256>;
/// Setup for a block validation function, without actually executing the /// Setup for a block validation function, without actually executing the
/// block validation function. /// block validation function.
pub fn setup_block_validation_scenario(params: &BeaconBlockTestParams) pub fn setup_block_validation_scenario(
-> (BeaconBlock, ParentHashes, AttesterMap, ProposerMap, TestStore) params: &BeaconBlockTestParams,
{ ) -> (
BeaconBlock,
ParentHashes,
AttesterMap,
ProposerMap,
TestStore,
) {
let stores = TestStore::new(); let stores = TestStore::new();
let cycle_length = params.cycle_length; let cycle_length = params.cycle_length;
@ -100,7 +85,10 @@ pub fn setup_block_validation_scenario(params: &BeaconBlockTestParams)
/* /*
* Store a valid PoW chain ref * Store a valid PoW chain ref
*/ */
stores.pow_chain.put_block_hash(pow_chain_ref.as_ref()).unwrap(); stores
.pow_chain
.put_block_hash(pow_chain_ref.as_ref())
.unwrap();
/* /*
* Generate a minimum viable parent block and store it in the database. * Generate a minimum viable parent block and store it in the database.
@ -110,7 +98,10 @@ pub fn setup_block_validation_scenario(params: &BeaconBlockTestParams)
parent_block.slot = block_slot - 1; parent_block.slot = block_slot - 1;
parent_block.attestations.push(parent_attestation); parent_block.attestations.push(parent_attestation);
let parent_block_ssz = serialize_block(&parent_block); let parent_block_ssz = serialize_block(&parent_block);
stores.block.put_serialized_block(parent_hash.as_ref(), &parent_block_ssz).unwrap(); stores
.block
.put_serialized_block(parent_hash.as_ref(), &parent_block_ssz)
.unwrap();
let proposer_map = { let proposer_map = {
let mut proposer_map = ProposerMap::new(); let mut proposer_map = ProposerMap::new();
@ -132,24 +123,28 @@ pub fn setup_block_validation_scenario(params: &BeaconBlockTestParams)
&mut parent_hashes, &mut parent_hashes,
&justified_block_hash, &justified_block_hash,
block_slot, block_slot,
attestation_slot); attestation_slot,
);
/* /*
* For each shard in this slot, generate an attestation. * For each shard in this slot, generate an attestation.
*/ */
for shard in 0..shards_per_slot { for shard in 0..shards_per_slot {
let mut signing_keys = vec![]; let mut signing_keys = vec![];
let mut attesters = vec![]; let mut attesters = vec![];
/* /*
* Generate a random keypair for each validator and clone it into the * Generate a random keypair for each validator and clone it into the
* list of keypairs. Store it in the database. * list of keypairs. Store it in the database.
*/ */
for _ in 0..validators_per_shard { for _ in 0..validators_per_shard {
let keypair = Keypair::random(); let keypair = Keypair::random();
keypairs.push(keypair.clone()); keypairs.push(keypair.clone());
stores.validator.put_public_key_by_index(i, &keypair.pk).unwrap(); stores
signing_keys.push(Some(keypair.sk.clone())); .validator
attesters.push(i); .put_public_key_by_index(i, &keypair.pk)
i += 1; .unwrap();
signing_keys.push(Some(keypair.sk.clone()));
attesters.push(i);
i += 1;
} }
attester_map.insert((attestation_slot, shard), attesters); attester_map.insert((attestation_slot, shard), attesters);
@ -163,7 +158,8 @@ pub fn setup_block_validation_scenario(params: &BeaconBlockTestParams)
cycle_length, cycle_length,
&parent_hashes, &parent_hashes,
&signing_keys[..], &signing_keys[..],
&stores.block); &stores.block,
);
attestations.push(attestation); attestations.push(attestation);
} }
(attester_map, attestations, keypairs) (attester_map, attestations, keypairs)
@ -180,11 +176,7 @@ pub fn setup_block_validation_scenario(params: &BeaconBlockTestParams)
specials: vec![], specials: vec![],
}; };
(block, (block, parent_hashes, attester_map, proposer_map, stores)
parent_hashes,
attester_map,
proposer_map,
stores)
} }
/// Helper function to take some BeaconBlock and SSZ serialize it. /// Helper function to take some BeaconBlock and SSZ serialize it.
@ -199,25 +191,20 @@ pub fn serialize_block(b: &BeaconBlock) -> Vec<u8> {
/// Returns the Result returned from the block validation function. /// Returns the Result returned from the block validation function.
pub fn run_block_validation_scenario<F>( pub fn run_block_validation_scenario<F>(
params: &BeaconBlockTestParams, params: &BeaconBlockTestParams,
mutator_func: F) mutator_func: F,
-> Result<(BeaconBlockStatus, Option<BeaconBlock>), SszBeaconBlockValidationError> ) -> Result<BeaconBlock, SszBeaconBlockValidationError>
where F: FnOnce(BeaconBlock, AttesterMap, ProposerMap, TestStore) where
-> (BeaconBlock, AttesterMap, ProposerMap, TestStore) F: FnOnce(BeaconBlock, AttesterMap, ProposerMap, TestStore)
-> (BeaconBlock, AttesterMap, ProposerMap, TestStore),
{ {
let (block, let (block, parent_hashes, attester_map, proposer_map, stores) =
parent_hashes, setup_block_validation_scenario(&params);
attester_map,
proposer_map,
stores) = setup_block_validation_scenario(&params);
let (block, let (block, attester_map, proposer_map, stores) =
attester_map, mutator_func(block, attester_map, proposer_map, stores);
proposer_map,
stores) = mutator_func(block, attester_map, proposer_map, stores);
let ssz_bytes = serialize_block(&block); let ssz_bytes = serialize_block(&block);
let ssz_block = SszBeaconBlock::from_slice(&ssz_bytes[..]) let ssz_block = SszBeaconBlock::from_slice(&ssz_bytes[..]).unwrap();
.unwrap();
let context = BeaconBlockValidationContext { let context = BeaconBlockValidationContext {
present_slot: params.validation_context_slot, present_slot: params.validation_context_slot,
@ -230,17 +217,17 @@ pub fn run_block_validation_scenario<F>(
attester_map: Arc::new(attester_map), attester_map: Arc::new(attester_map),
block_store: stores.block.clone(), block_store: stores.block.clone(),
validator_store: stores.validator.clone(), validator_store: stores.validator.clone(),
pow_store: stores.pow_chain.clone() pow_store: stores.pow_chain.clone(),
}; };
let block_hash = Hash256::from(&ssz_block.block_hash()[..]); let block_hash = Hash256::from(&ssz_block.block_hash()[..]);
let validation_status = context.validate_ssz_block(&block_hash, &ssz_block); let validation_result = context.validate_ssz_block(&ssz_block);
/* /*
* If validation returned a block, make sure it's the same block we supplied to it. * If validation returned a block, make sure it's the same block we supplied to it.
* *
* I.e., there were no errors during the serialization -> deserialization process. * I.e., there were no errors during the serialization -> deserialization process.
*/ */
if let Ok((_, Some(returned_block))) = &validation_status { if let Ok(returned_block) = &validation_result {
assert_eq!(*returned_block, block); assert_eq!(*returned_block, block);
}; };
validation_status validation_result
} }

View File

@ -1,26 +1,12 @@
use super::bls::{ use super::bls::AggregateSignature;
AggregateSignature, use super::hashing::canonical_hash;
};
use super::helpers::{ use super::helpers::{
BeaconBlockTestParams, run_block_validation_scenario, serialize_block, BeaconBlockTestParams, TestStore,
TestStore,
run_block_validation_scenario,
serialize_block,
};
use super::types::{
BeaconBlock,
Hash256,
ProposerMap,
}; };
use super::ssz_helpers::ssz_beacon_block::SszBeaconBlock; use super::ssz_helpers::ssz_beacon_block::SszBeaconBlock;
use super::validation::block_validation::{ use super::types::{BeaconBlock, Hash256, ProposerMap};
SszBeaconBlockValidationError, use super::validation::attestation_validation::AttestationValidationError;
BeaconBlockStatus, use super::validation::block_validation::SszBeaconBlockValidationError;
};
use super::validation::attestation_validation::{
AttestationValidationError,
};
use super::hashing::canonical_hash;
fn get_simple_params() -> BeaconBlockTestParams { fn get_simple_params() -> BeaconBlockTestParams {
let validators_per_shard: usize = 5; let validators_per_shard: usize = 5;
@ -66,11 +52,9 @@ fn test_block_validation_valid() {
(block, attester_map, proposer_map, stores) (block, attester_map, proposer_map, stores)
}; };
let status = run_block_validation_scenario( let status = run_block_validation_scenario(&params, mutator);
&params,
mutator);
assert_eq!(status.unwrap().0, BeaconBlockStatus::NewBlock); assert!(status.is_ok())
} }
#[test] #[test]
@ -83,15 +67,21 @@ fn test_block_validation_valid_known_block() {
*/ */
let block_ssz = serialize_block(&block); let block_ssz = serialize_block(&block);
let block_hash = canonical_hash(&block_ssz); let block_hash = canonical_hash(&block_ssz);
stores.block.put_serialized_block(&block_hash, &block_ssz).unwrap(); stores
.block
.put_serialized_block(&block_hash, &block_ssz)
.unwrap();
(block, attester_map, proposer_map, stores) (block, attester_map, proposer_map, stores)
}; };
let status = run_block_validation_scenario( let status = run_block_validation_scenario(&params, mutator);
&params,
mutator);
assert_eq!(status.unwrap(), (BeaconBlockStatus::KnownBlock, None)); /*
* This function does _not_ check if a block is already known.
*
* Known blocks will appear as valid blocks.
*/
assert!(status.is_ok())
} }
#[test] #[test]
@ -103,11 +93,12 @@ fn test_block_validation_parent_slot_too_high() {
(block, attester_map, proposer_map, stores) (block, attester_map, proposer_map, stores)
}; };
let status = run_block_validation_scenario( let status = run_block_validation_scenario(&params, mutator);
&params,
mutator);
assert_eq!(status, Err(SszBeaconBlockValidationError::ParentSlotHigherThanBlockSlot)); assert_eq!(
status,
Err(SszBeaconBlockValidationError::ParentSlotHigherThanBlockSlot)
);
} }
#[test] #[test]
@ -119,9 +110,7 @@ fn test_block_validation_invalid_future_slot() {
(block, attester_map, proposer_map, stores) (block, attester_map, proposer_map, stores)
}; };
let status = run_block_validation_scenario( let status = run_block_validation_scenario(&params, mutator);
&params,
mutator);
assert_eq!(status, Err(SszBeaconBlockValidationError::FutureSlot)); assert_eq!(status, Err(SszBeaconBlockValidationError::FutureSlot));
} }
@ -131,8 +120,8 @@ fn test_block_validation_invalid_slot_already_finalized() {
let mut params = get_simple_params(); let mut params = get_simple_params();
params.validation_context_finalized_slot = params.block_slot; params.validation_context_finalized_slot = params.block_slot;
params.validation_context_justified_slot = params.validation_context_finalized_slot + params.validation_context_justified_slot =
u64::from(params.cycle_length); params.validation_context_finalized_slot + u64::from(params.cycle_length);
let mutator = |block, attester_map, proposer_map, stores| { let mutator = |block, attester_map, proposer_map, stores| {
/* /*
@ -141,11 +130,12 @@ fn test_block_validation_invalid_slot_already_finalized() {
(block, attester_map, proposer_map, stores) (block, attester_map, proposer_map, stores)
}; };
let status = run_block_validation_scenario( let status = run_block_validation_scenario(&params, mutator);
&params,
mutator);
assert_eq!(status, Err(SszBeaconBlockValidationError::SlotAlreadyFinalized)); assert_eq!(
status,
Err(SszBeaconBlockValidationError::SlotAlreadyFinalized)
);
} }
#[test] #[test]
@ -157,11 +147,12 @@ fn test_block_validation_invalid_unknown_pow_hash() {
(block, attester_map, proposer_map, stores) (block, attester_map, proposer_map, stores)
}; };
let status = run_block_validation_scenario( let status = run_block_validation_scenario(&params, mutator);
&params,
mutator);
assert_eq!(status, Err(SszBeaconBlockValidationError::UnknownPoWChainRef)); assert_eq!(
status,
Err(SszBeaconBlockValidationError::UnknownPoWChainRef)
);
} }
#[test] #[test]
@ -173,11 +164,12 @@ fn test_block_validation_invalid_unknown_parent_hash() {
(block, attester_map, proposer_map, stores) (block, attester_map, proposer_map, stores)
}; };
let status = run_block_validation_scenario( let status = run_block_validation_scenario(&params, mutator);
&params,
mutator);
assert_eq!(status, Err(SszBeaconBlockValidationError::UnknownParentHash)); assert_eq!(
status,
Err(SszBeaconBlockValidationError::UnknownParentHash)
);
} }
#[test] #[test]
@ -192,36 +184,44 @@ fn test_block_validation_invalid_1st_attestation_signature() {
(block, attester_map, proposer_map, stores) (block, attester_map, proposer_map, stores)
}; };
let status = run_block_validation_scenario( let status = run_block_validation_scenario(&params, mutator);
&params,
mutator);
assert_eq!(status, Err(SszBeaconBlockValidationError::AttestationValidationError( assert_eq!(
AttestationValidationError::BadAggregateSignature))); status,
Err(SszBeaconBlockValidationError::AttestationValidationError(
AttestationValidationError::BadAggregateSignature
))
);
} }
#[test] #[test]
fn test_block_validation_invalid_no_parent_proposer_signature() { fn test_block_validation_invalid_no_parent_proposer_signature() {
let params = get_simple_params(); let params = get_simple_params();
let mutator = |block: BeaconBlock, attester_map, mut proposer_map: ProposerMap, stores: TestStore| { let mutator =
/* |block: BeaconBlock, attester_map, mut proposer_map: ProposerMap, stores: TestStore| {
* Set the proposer for this slot to be a validator that does not exist. /*
*/ * Set the proposer for this slot to be a validator that does not exist.
let ssz = { */
let parent_hash = block.parent_hash().unwrap().as_ref(); let ssz = {
stores.block.get_serialized_block(parent_hash).unwrap().unwrap() let parent_hash = block.parent_hash().unwrap().as_ref();
stores
.block
.get_serialized_block(parent_hash)
.unwrap()
.unwrap()
};
let parent_block_slot = SszBeaconBlock::from_slice(&ssz[..]).unwrap().slot();
proposer_map.insert(parent_block_slot, params.total_validators + 1);
(block, attester_map, proposer_map, stores)
}; };
let parent_block_slot = SszBeaconBlock::from_slice(&ssz[..]).unwrap().slot();
proposer_map.insert(parent_block_slot, params.total_validators + 1);
(block, attester_map, proposer_map, stores)
};
let status = run_block_validation_scenario( let status = run_block_validation_scenario(&params, mutator);
&params,
mutator);
assert_eq!(status, Err(SszBeaconBlockValidationError::NoProposerSignature)); assert_eq!(
status,
Err(SszBeaconBlockValidationError::NoProposerSignature)
);
} }
#[test] #[test]
@ -236,9 +236,7 @@ fn test_block_validation_invalid_bad_proposer_map() {
(block, attester_map, proposer_map, stores) (block, attester_map, proposer_map, stores)
}; };
let status = run_block_validation_scenario( let status = run_block_validation_scenario(&params, mutator);
&params,
mutator);
assert_eq!(status, Err(SszBeaconBlockValidationError::BadProposerMap)); assert_eq!(status, Err(SszBeaconBlockValidationError::BadProposerMap));
} }
@ -255,10 +253,12 @@ fn test_block_validation_invalid_2nd_attestation_signature() {
(block, attester_map, proposer_map, stores) (block, attester_map, proposer_map, stores)
}; };
let status = run_block_validation_scenario( let status = run_block_validation_scenario(&params, mutator);
&params,
mutator);
assert_eq!(status, Err(SszBeaconBlockValidationError::AttestationValidationError( assert_eq!(
AttestationValidationError::BadAggregateSignature))); status,
Err(SszBeaconBlockValidationError::AttestationValidationError(
AttestationValidationError::BadAggregateSignature
))
);
} }

View File

@ -1,14 +1,9 @@
extern crate ssz_helpers; extern crate ssz_helpers;
use self::ssz_helpers::ssz_beacon_block::{ use self::ssz_helpers::ssz_beacon_block::SszBeaconBlock;
SszBeaconBlock,
};
use std::sync::Arc;
use super::{
ClientDB,
DBError,
};
use super::BLOCKS_DB_COLUMN as DB_COLUMN; use super::BLOCKS_DB_COLUMN as DB_COLUMN;
use super::{ClientDB, DBError};
use std::sync::Arc;
type BeaconBlockHash = Vec<u8>; type BeaconBlockHash = Vec<u8>;
type BeaconBlockSsz = Vec<u8>; type BeaconBlockSsz = Vec<u8>;
@ -21,36 +16,33 @@ pub enum BeaconBlockAtSlotError {
} }
pub struct BeaconBlockStore<T> pub struct BeaconBlockStore<T>
where T: ClientDB where
T: ClientDB,
{ {
db: Arc<T>, db: Arc<T>,
} }
impl<T: ClientDB> BeaconBlockStore<T> { impl<T: ClientDB> BeaconBlockStore<T> {
pub fn new(db: Arc<T>) -> Self { pub fn new(db: Arc<T>) -> Self {
Self { Self { db }
db,
}
} }
pub fn put_serialized_block(&self, hash: &[u8], ssz: &[u8]) pub fn put_serialized_block(&self, hash: &[u8], ssz: &[u8]) -> Result<(), DBError> {
-> Result<(), DBError>
{
self.db.put(DB_COLUMN, hash, ssz) self.db.put(DB_COLUMN, hash, ssz)
} }
pub fn get_serialized_block(&self, hash: &[u8]) pub fn get_serialized_block(&self, hash: &[u8]) -> Result<Option<Vec<u8>>, DBError> {
-> Result<Option<Vec<u8>>, DBError>
{
self.db.get(DB_COLUMN, hash) self.db.get(DB_COLUMN, hash)
} }
pub fn block_exists(&self, hash: &[u8]) pub fn block_exists(&self, hash: &[u8]) -> Result<bool, DBError> {
-> Result<bool, DBError>
{
self.db.exists(DB_COLUMN, hash) self.db.exists(DB_COLUMN, hash)
} }
pub fn delete_block(&self, hash: &[u8]) -> Result<(), DBError> {
self.db.delete(DB_COLUMN, hash)
}
/// Retrieve the block at a slot given a "head_hash" and a slot. /// 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 /// A "head_hash" must be a block hash with a slot number greater than or equal to the desired
@ -60,9 +52,11 @@ impl<T: ClientDB> BeaconBlockStore<T> {
/// slot number. If the slot is skipped, the function will return None. /// slot number. If the slot is skipped, the function will return None.
/// ///
/// If a block is found, a tuple of (block_hash, serialized_block) is returned. /// If a block is found, a tuple of (block_hash, serialized_block) is returned.
pub fn block_at_slot(&self, head_hash: &[u8], slot: u64) pub fn block_at_slot(
-> Result<Option<(BeaconBlockHash, BeaconBlockSsz)>, BeaconBlockAtSlotError> &self,
{ head_hash: &[u8],
slot: u64,
) -> Result<Option<(BeaconBlockHash, BeaconBlockSsz)>, BeaconBlockAtSlotError> {
match self.get_serialized_block(head_hash)? { match self.get_serialized_block(head_hash)? {
None => Err(BeaconBlockAtSlotError::UnknownBeaconBlock), None => Err(BeaconBlockAtSlotError::UnknownBeaconBlock),
Some(ssz) => { Some(ssz) => {
@ -71,12 +65,10 @@ impl<T: ClientDB> BeaconBlockStore<T> {
match block.slot() { match block.slot() {
s if s == slot => Ok(Some((head_hash.to_vec(), ssz.to_vec()))), s if s == slot => Ok(Some((head_hash.to_vec(), ssz.to_vec()))),
s if s < slot => Ok(None), s if s < slot => Ok(None),
_ => { _ => match block.parent_hash() {
match block.parent_hash() { Some(parent_hash) => self.block_at_slot(parent_hash, slot),
Some(parent_hash) => self.block_at_slot(parent_hash, slot), None => Err(BeaconBlockAtSlotError::UnknownBeaconBlock),
None => Err(BeaconBlockAtSlotError::UnknownBeaconBlock) },
}
}
} }
} }
} }
@ -94,15 +86,15 @@ mod tests {
extern crate ssz; extern crate ssz;
extern crate types; extern crate types;
use self::types::beacon_block::BeaconBlock;
use self::types::attestation_record::AttestationRecord;
use self::types::Hash256;
use self::ssz::SszStream; use self::ssz::SszStream;
use self::types::attestation_record::AttestationRecord;
use self::types::beacon_block::BeaconBlock;
use self::types::Hash256;
use super::*;
use super::super::super::MemoryDB; use super::super::super::MemoryDB;
use std::thread; use super::*;
use std::sync::Arc; use std::sync::Arc;
use std::thread;
#[test] #[test]
fn test_block_store_on_memory_db() { fn test_block_store_on_memory_db() {
@ -148,13 +140,12 @@ mod tests {
let db = Arc::new(MemoryDB::open()); let db = Arc::new(MemoryDB::open());
let bs = Arc::new(BeaconBlockStore::new(db.clone())); let bs = Arc::new(BeaconBlockStore::new(db.clone()));
let blocks = (0..5).into_iter() let blocks = (0..5).into_iter().map(|_| {
.map(|_| { let mut block = BeaconBlock::zero();
let mut block = BeaconBlock::zero(); let ar = AttestationRecord::zero();
let ar = AttestationRecord::zero(); block.attestations.push(ar);
block.attestations.push(ar); block
block });
});
let hashes = [ let hashes = [
Hash256::from("zero".as_bytes()), Hash256::from("zero".as_bytes()),