2018-10-29 19:29:15 +00:00
|
|
|
use super::block_context::BlockValidationContextError;
|
|
|
|
use super::state_transition::StateTransitionError;
|
|
|
|
use super::BeaconChain;
|
|
|
|
use db::{ClientDB, DBError};
|
|
|
|
use naive_fork_choice::{naive_fork_choice, ForkChoiceError};
|
|
|
|
use ssz_helpers::ssz_beacon_block::{SszBeaconBlock, SszBeaconBlockError};
|
|
|
|
use types::Hash256;
|
|
|
|
use validation::block_validation::SszBeaconBlockValidationError;
|
2018-10-25 08:14:43 +00:00
|
|
|
|
|
|
|
pub enum BlockProcessingOutcome {
|
|
|
|
BlockAlreadyKnown,
|
|
|
|
NewCanonicalBlock,
|
2018-10-29 19:29:15 +00:00
|
|
|
NewReorgBlock,
|
2018-10-25 08:14:43 +00:00
|
|
|
NewForkBlock,
|
|
|
|
}
|
|
|
|
|
|
|
|
pub enum BlockProcessingError {
|
2018-10-29 19:29:15 +00:00
|
|
|
ParentBlockNotFound,
|
|
|
|
ActiveStateRootInvalid,
|
|
|
|
CrystallizedStateRootInvalid,
|
|
|
|
NoHeadHashes,
|
|
|
|
ForkChoiceFailed(ForkChoiceError),
|
|
|
|
ContextGenerationFailed(BlockValidationContextError),
|
2018-10-25 08:14:43 +00:00
|
|
|
DeserializationFailed(SszBeaconBlockError),
|
|
|
|
ValidationFailed(SszBeaconBlockValidationError),
|
2018-10-29 19:29:15 +00:00
|
|
|
StateTransitionFailed(StateTransitionError),
|
|
|
|
DBError(String),
|
2018-10-25 08:14:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl<T> BeaconChain<T>
|
2018-10-29 19:29:15 +00:00
|
|
|
where
|
|
|
|
T: ClientDB + Sized,
|
2018-10-25 08:14:43 +00:00
|
|
|
{
|
2018-10-29 19:29:15 +00:00
|
|
|
pub fn process_block(
|
|
|
|
&mut self,
|
|
|
|
ssz: &[u8],
|
|
|
|
present_slot: u64,
|
|
|
|
) -> Result<(BlockProcessingOutcome, Hash256), BlockProcessingError> {
|
2018-10-25 08:14:43 +00:00
|
|
|
/*
|
|
|
|
* Generate a SszBlock to read directly from the serialized SSZ.
|
|
|
|
*/
|
|
|
|
let ssz_block = SszBeaconBlock::from_slice(ssz)?;
|
|
|
|
let block_hash = Hash256::from(&ssz_block.block_hash()[..]);
|
2018-10-29 19:29:15 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* 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()
|
2018-10-25 08:14:43 +00:00
|
|
|
.ok_or(BlockProcessingError::ValidationFailed(
|
2018-10-29 19:29:15 +00:00
|
|
|
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)?;
|
2018-10-25 08:14:43 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Generate the context in which to validate this block.
|
|
|
|
*/
|
2018-10-29 19:29:15 +00:00
|
|
|
let validation_context =
|
|
|
|
self.block_validation_context(&ssz_block, &parent_ssz_block, present_slot)?;
|
2018-10-25 08:14:43 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Validate the block against the context, checking signatures, parent_hashes, etc.
|
|
|
|
*/
|
2018-10-29 19:29:15 +00:00
|
|
|
let block = validation_context.validate_ssz_block(&ssz_block)?;
|
2018-10-25 08:14:43 +00:00
|
|
|
|
2018-10-29 19:29:15 +00:00
|
|
|
let (new_act_state, new_cry_state_option) = {
|
2018-10-25 08:14:43 +00:00
|
|
|
/*
|
2018-10-29 19:29:15 +00:00
|
|
|
* Load the states from memory.
|
2018-10-25 08:14:43 +00:00
|
|
|
*
|
2018-10-29 19:29:15 +00:00
|
|
|
* 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.
|
2018-10-25 08:14:43 +00:00
|
|
|
*/
|
2018-10-29 19:29:15 +00:00
|
|
|
let act_state = self
|
|
|
|
.active_states
|
|
|
|
.get(&block.active_state_root)
|
|
|
|
.ok_or(BlockValidationContextError::UnknownActiveState)?;
|
|
|
|
let cry_state = self
|
|
|
|
.crystallized_states
|
|
|
|
.get(&block.crystallized_state_root)
|
|
|
|
.ok_or(BlockValidationContextError::UnknownCrystallizedState)?;
|
|
|
|
|
|
|
|
self.transition_states(act_state, cry_state, &block, &block_hash)?
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Calculate the new active state root and ensure the block state root matches.
|
|
|
|
*/
|
|
|
|
let new_act_state_root = new_act_state.canonical_root();
|
|
|
|
if new_act_state_root != block.active_state_root {
|
|
|
|
return Err(BlockProcessingError::ActiveStateRootInvalid);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Determine the crystallized state root and ensure the block state root matches.
|
|
|
|
*
|
|
|
|
* If a new crystallized state was created, store it in memory.
|
|
|
|
*/
|
|
|
|
let (new_cry_state_root, cry_state_transitioned) = match new_cry_state_option {
|
|
|
|
None => {
|
|
|
|
/*
|
|
|
|
* A new crystallized state was not created, therefore the
|
|
|
|
* `crystallized_state_root` of this block must match its parent.
|
|
|
|
*/
|
|
|
|
if Hash256::from(parent_ssz_block.cry_state_root()) != block.crystallized_state_root
|
|
|
|
{
|
|
|
|
return Err(BlockProcessingError::ActiveStateRootInvalid);
|
|
|
|
}
|
|
|
|
// Return the old root
|
|
|
|
(block.crystallized_state_root, false)
|
2018-10-25 08:14:43 +00:00
|
|
|
}
|
2018-10-29 19:29:15 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
};
|
2018-10-25 08:14:43 +00:00
|
|
|
|
2018-10-29 19:29:15 +00:00
|
|
|
/*
|
|
|
|
* 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);
|
2018-10-25 08:14:43 +00:00
|
|
|
}
|
2018-10-29 19:29:15 +00:00
|
|
|
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))
|
2018-10-25 08:14:43 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<BlockValidationContextError> for BlockProcessingError {
|
|
|
|
fn from(e: BlockValidationContextError) -> Self {
|
2018-10-29 19:29:15 +00:00
|
|
|
BlockProcessingError::ContextGenerationFailed(e)
|
2018-10-25 08:14:43 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<SszBeaconBlockError> for BlockProcessingError {
|
|
|
|
fn from(e: SszBeaconBlockError) -> Self {
|
|
|
|
BlockProcessingError::DeserializationFailed(e)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-29 19:29:15 +00:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-25 08:14:43 +00:00
|
|
|
impl From<SszBeaconBlockValidationError> for BlockProcessingError {
|
|
|
|
fn from(e: SszBeaconBlockValidationError) -> Self {
|
|
|
|
BlockProcessingError::ValidationFailed(e)
|
|
|
|
}
|
|
|
|
}
|
2018-10-29 19:29:15 +00:00
|
|
|
|
|
|
|
impl From<StateTransitionError> for BlockProcessingError {
|
|
|
|
fn from(e: StateTransitionError) -> Self {
|
|
|
|
BlockProcessingError::StateTransitionFailed(e)
|
|
|
|
}
|
|
|
|
}
|