Add untested minimum viable block processing
This commit is contained in:
parent
e27c4106e9
commit
46da9b670f
@ -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",
|
||||||
|
@ -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" }
|
||||||
|
@ -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 {
|
||||||
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -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(®istration, ValidatorStatus::Active);
|
let _ = inductor.induct(®istration, 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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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();
|
||||||
|
@ -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]
|
||||||
|
@ -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> {
|
||||||
|
29
beacon_chain/chain/src/transition.rs
Normal file
29
beacon_chain/chain/src/transition.rs
Normal 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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
7
beacon_chain/chain/tests/main.rs
Normal file
7
beacon_chain/chain/tests/main.rs
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
extern crate chain;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use chain::{BeaconChain, BeaconChainError};
|
||||||
|
|
||||||
|
}
|
9
beacon_chain/naive_fork_choice/Cargo.toml
Normal file
9
beacon_chain/naive_fork_choice/Cargo.toml
Normal 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" }
|
97
beacon_chain/naive_fork_choice/src/lib.rs
Normal file
97
beacon_chain/naive_fork_choice/src/lib.rs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
7
beacon_chain/state-transition/Cargo.toml
Normal file
7
beacon_chain/state-transition/Cargo.toml
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
[package]
|
||||||
|
name = "state-transition"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Paul Hauner <paul@paulhauner.com>"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
types = { path = "../types" }
|
194
beacon_chain/state-transition/src/lib.rs
Normal file
194
beacon_chain/state-transition/src/lib.rs
Normal 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));
|
||||||
|
}
|
||||||
|
}
|
@ -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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -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]`.
|
||||||
|
@ -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,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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(¶ms);
|
||||||
attester_map,
|
|
||||||
proposer_map,
|
|
||||||
stores) = setup_block_validation_scenario(¶ms);
|
|
||||||
|
|
||||||
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
|
||||||
}
|
}
|
||||||
|
@ -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(¶ms, mutator);
|
||||||
¶ms,
|
|
||||||
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(¶ms, mutator);
|
||||||
¶ms,
|
|
||||||
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(¶ms, mutator);
|
||||||
¶ms,
|
|
||||||
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(¶ms, mutator);
|
||||||
¶ms,
|
|
||||||
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(¶ms, mutator);
|
||||||
¶ms,
|
|
||||||
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(¶ms, mutator);
|
||||||
¶ms,
|
|
||||||
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(¶ms, mutator);
|
||||||
¶ms,
|
|
||||||
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(¶ms, mutator);
|
||||||
¶ms,
|
|
||||||
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(¶ms, mutator);
|
||||||
¶ms,
|
|
||||||
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(¶ms, mutator);
|
||||||
¶ms,
|
|
||||||
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(¶ms, mutator);
|
||||||
¶ms,
|
|
||||||
mutator);
|
|
||||||
|
|
||||||
assert_eq!(status, Err(SszBeaconBlockValidationError::AttestationValidationError(
|
assert_eq!(
|
||||||
AttestationValidationError::BadAggregateSignature)));
|
status,
|
||||||
|
Err(SszBeaconBlockValidationError::AttestationValidationError(
|
||||||
|
AttestationValidationError::BadAggregateSignature
|
||||||
|
))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
@ -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()),
|
||||||
|
Loading…
Reference in New Issue
Block a user