mirror of
https://github.com/sigp/lighthouse.git
synced 2026-05-07 16:55:46 +00:00
Add untested minimum viable block processing
This commit is contained in:
@@ -1,22 +1,10 @@
|
||||
use db::{
|
||||
ClientDB,
|
||||
};
|
||||
use db::stores::{
|
||||
BeaconBlockAtSlotError,
|
||||
};
|
||||
use validation::block_validation::{
|
||||
BeaconBlockValidationContext,
|
||||
};
|
||||
use super::{
|
||||
BeaconChain,
|
||||
};
|
||||
use ssz_helpers::ssz_beacon_block::{
|
||||
SszBeaconBlock,
|
||||
};
|
||||
use super::BeaconChain;
|
||||
use db::stores::BeaconBlockAtSlotError;
|
||||
use db::ClientDB;
|
||||
use ssz_helpers::ssz_beacon_block::SszBeaconBlock;
|
||||
use std::sync::Arc;
|
||||
use types::{
|
||||
Hash256,
|
||||
};
|
||||
use types::Hash256;
|
||||
use validation::block_validation::BeaconBlockValidationContext;
|
||||
|
||||
pub enum BlockValidationContextError {
|
||||
UnknownCrystallizedState,
|
||||
@@ -35,18 +23,24 @@ impl From<BeaconBlockAtSlotError> for BlockValidationContextError {
|
||||
}
|
||||
|
||||
impl<T> BeaconChain<T>
|
||||
where T: ClientDB + Sized
|
||||
where
|
||||
T: ClientDB + Sized,
|
||||
{
|
||||
pub(crate) fn block_validation_context(&self, block: &SszBeaconBlock, present_slot: u64)
|
||||
-> Result<BeaconBlockValidationContext<T>, BlockValidationContextError>
|
||||
{
|
||||
pub(crate) fn block_validation_context(
|
||||
&self,
|
||||
block: &SszBeaconBlock,
|
||||
parent_block: &SszBeaconBlock,
|
||||
present_slot: u64,
|
||||
) -> Result<BeaconBlockValidationContext<T>, BlockValidationContextError> {
|
||||
/*
|
||||
* Load the crystallized state for this block from our caches.
|
||||
*
|
||||
* Fail if the crystallized state is unknown.
|
||||
*/
|
||||
let cry_state_root = Hash256::from(block.cry_state_root());
|
||||
let cry_state = self.crystallized_states.get(&cry_state_root)
|
||||
let cry_state_root = Hash256::from(parent_block.cry_state_root());
|
||||
let cry_state = self
|
||||
.crystallized_states
|
||||
.get(&cry_state_root)
|
||||
.ok_or(BlockValidationContextError::UnknownCrystallizedState)?;
|
||||
|
||||
/*
|
||||
@@ -54,8 +48,10 @@ impl<T> BeaconChain<T>
|
||||
*
|
||||
* Fail if the active state is unknown.
|
||||
*/
|
||||
let act_state_root = Hash256::from(block.act_state_root());
|
||||
let act_state = self.active_states.get(&act_state_root)
|
||||
let act_state_root = Hash256::from(parent_block.act_state_root());
|
||||
let act_state = self
|
||||
.active_states
|
||||
.get(&act_state_root)
|
||||
.ok_or(BlockValidationContextError::UnknownActiveState)?;
|
||||
|
||||
/*
|
||||
@@ -63,16 +59,21 @@ impl<T> BeaconChain<T>
|
||||
* the hash of this block from the database
|
||||
*/
|
||||
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)?;
|
||||
let (last_justified_block_hash, _) = self.store.block.block_at_slot(
|
||||
&parent_block_hash, last_justified_slot)?
|
||||
let (last_justified_block_hash, _) = self
|
||||
.store
|
||||
.block
|
||||
.block_at_slot(&parent_block_hash, last_justified_slot)?
|
||||
.ok_or(BlockValidationContextError::UnknownJustifiedBlock)?;
|
||||
|
||||
/*
|
||||
* Load the attester and proposer maps for the crystallized state.
|
||||
*/
|
||||
let (attester_map, proposer_map) = self.attester_proposer_maps.get(&cry_state_root)
|
||||
let (attester_map, proposer_map) = self
|
||||
.attester_proposer_maps
|
||||
.get(&cry_state_root)
|
||||
.ok_or(BlockValidationContextError::UnknownAttesterProposerMaps)?;
|
||||
|
||||
Ok(BeaconBlockValidationContext {
|
||||
|
||||
@@ -1,98 +1,219 @@
|
||||
use super::{
|
||||
BeaconChain,
|
||||
ClientDB,
|
||||
};
|
||||
use super::block_context::{
|
||||
BlockValidationContextError,
|
||||
};
|
||||
use ssz_helpers::ssz_beacon_block::{
|
||||
SszBeaconBlock,
|
||||
SszBeaconBlockError,
|
||||
};
|
||||
use types::{
|
||||
Hash256,
|
||||
};
|
||||
use validation::block_validation::{
|
||||
BeaconBlockStatus,
|
||||
SszBeaconBlockValidationError,
|
||||
};
|
||||
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;
|
||||
|
||||
pub enum BlockProcessingOutcome {
|
||||
BlockAlreadyKnown,
|
||||
NewCanonicalBlock,
|
||||
NewReorgBlock,
|
||||
NewForkBlock,
|
||||
}
|
||||
|
||||
pub enum BlockProcessingError {
|
||||
ContextGenerationError(BlockValidationContextError),
|
||||
ParentBlockNotFound,
|
||||
ActiveStateRootInvalid,
|
||||
CrystallizedStateRootInvalid,
|
||||
NoHeadHashes,
|
||||
ForkChoiceFailed(ForkChoiceError),
|
||||
ContextGenerationFailed(BlockValidationContextError),
|
||||
DeserializationFailed(SszBeaconBlockError),
|
||||
ValidationFailed(SszBeaconBlockValidationError),
|
||||
StateTransitionFailed(StateTransitionError),
|
||||
DBError(String),
|
||||
}
|
||||
|
||||
impl<T> BeaconChain<T>
|
||||
where T: ClientDB + Sized
|
||||
where
|
||||
T: ClientDB + Sized,
|
||||
{
|
||||
pub fn process_block(&mut self, ssz: &[u8], present_slot: u64)
|
||||
-> Result<(BlockProcessingOutcome, Hash256), BlockProcessingError>
|
||||
{
|
||||
pub fn process_block(
|
||||
&mut self,
|
||||
ssz: &[u8],
|
||||
present_slot: u64,
|
||||
) -> Result<(BlockProcessingOutcome, Hash256), BlockProcessingError> {
|
||||
/*
|
||||
* Generate a SszBlock to read directly from the serialized SSZ.
|
||||
*/
|
||||
let ssz_block = SszBeaconBlock::from_slice(ssz)?;
|
||||
let block_hash = Hash256::from(&ssz_block.block_hash()[..]);
|
||||
let parent_hash = ssz_block.parent_hash()
|
||||
|
||||
/*
|
||||
* 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(
|
||||
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.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
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 => {
|
||||
Ok((BlockProcessingOutcome::BlockAlreadyKnown, block_hash))
|
||||
}
|
||||
BeaconBlockStatus::NewBlock => {
|
||||
let head_hash_index = {
|
||||
match self.head_block_hashes.iter().position(|x| *x == Hash256::from(parent_hash)) {
|
||||
Some(i) => i,
|
||||
None => {
|
||||
self.head_block_hashes.push(block_hash);
|
||||
self.head_block_hashes.len() - 1
|
||||
}
|
||||
}
|
||||
};
|
||||
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)?;
|
||||
|
||||
if head_hash_index == self.canonical_head_block_hash {
|
||||
Ok((BlockProcessingOutcome::NewCanonicalBlock, block_hash))
|
||||
} else {
|
||||
Ok((BlockProcessingOutcome::NewForkBlock, block_hash))
|
||||
self.transition_states(act_state, cry_state, &block, &block_hash)?
|
||||
};
|
||||
|
||||
/*
|
||||
* Calculate the new active state root and ensure the block state root matches.
|
||||
*/
|
||||
let new_act_state_root = new_act_state.canonical_root();
|
||||
if new_act_state_root != block.active_state_root {
|
||||
return Err(BlockProcessingError::ActiveStateRootInvalid);
|
||||
}
|
||||
|
||||
/*
|
||||
* Determine the crystallized state root and ensure the block state root matches.
|
||||
*
|
||||
* If a new crystallized state was created, store it in memory.
|
||||
*/
|
||||
let (new_cry_state_root, cry_state_transitioned) = match new_cry_state_option {
|
||||
None => {
|
||||
/*
|
||||
* A new crystallized state was not created, therefore the
|
||||
* `crystallized_state_root` of this block must match its parent.
|
||||
*/
|
||||
if Hash256::from(parent_ssz_block.cry_state_root()) != block.crystallized_state_root
|
||||
{
|
||||
return Err(BlockProcessingError::ActiveStateRootInvalid);
|
||||
}
|
||||
// Return the old root
|
||||
(block.crystallized_state_root, false)
|
||||
}
|
||||
Some(new_cry_state) => {
|
||||
/*
|
||||
* A new crystallized state was created. Check to ensure the crystallized
|
||||
* state root in the block is the same as the calculated on this node.
|
||||
*/
|
||||
let cry_state_root = new_cry_state.canonical_root();
|
||||
if cry_state_root != block.crystallized_state_root {
|
||||
return Err(BlockProcessingError::ActiveStateRootInvalid);
|
||||
}
|
||||
/*
|
||||
* Store the new crystallized state in memory.
|
||||
*/
|
||||
self.crystallized_states
|
||||
.insert(cry_state_root, new_cry_state);
|
||||
// Return the new root
|
||||
(cry_state_root, true)
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Store the new block as a leaf in the block tree.
|
||||
*/
|
||||
let mut new_head_block_hashes = self.head_block_hashes.clone();
|
||||
let new_parent_head_hash_index = match new_head_block_hashes
|
||||
.iter()
|
||||
.position(|x| *x == Hash256::from(parent_hash))
|
||||
{
|
||||
Some(i) => {
|
||||
new_head_block_hashes[i] = block_hash.clone();
|
||||
i
|
||||
}
|
||||
None => {
|
||||
new_head_block_hashes.push(block_hash.clone());
|
||||
new_head_block_hashes.len() - 1
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Store the new block in the database.
|
||||
*/
|
||||
self.store
|
||||
.block
|
||||
.put_serialized_block(&block_hash[..], ssz_block.block_ssz())?;
|
||||
|
||||
/*
|
||||
* Store the active state in memory.
|
||||
*/
|
||||
self.active_states.insert(new_act_state_root, new_act_state);
|
||||
|
||||
let new_canonical_head_block_hash_index =
|
||||
match naive_fork_choice(&self.head_block_hashes, self.store.block.clone())? {
|
||||
None => {
|
||||
/*
|
||||
* Fork choice failed, therefore the block, active state and crystallized state
|
||||
* can be removed from storage (i.e., forgotten).
|
||||
*/
|
||||
if cry_state_transitioned {
|
||||
// A new crystallized state was generated, so it should be deleted.
|
||||
self.crystallized_states.remove(&new_cry_state_root);
|
||||
}
|
||||
self.active_states.remove(&new_act_state_root);
|
||||
self.store.block.delete_block(&block_hash[..])?;
|
||||
return Err(BlockProcessingError::NoHeadHashes);
|
||||
}
|
||||
Some(i) => i,
|
||||
};
|
||||
|
||||
if new_canonical_head_block_hash_index != self.canonical_head_block_hash {
|
||||
/*
|
||||
* The block caused a re-org (switch of chains).
|
||||
*/
|
||||
Ok((BlockProcessingOutcome::NewReorgBlock, block_hash))
|
||||
} else {
|
||||
/*
|
||||
* The block did not cause a re-org.
|
||||
*/
|
||||
if new_parent_head_hash_index == self.canonical_head_block_hash {
|
||||
Ok((BlockProcessingOutcome::NewCanonicalBlock, block_hash))
|
||||
} else {
|
||||
Ok((BlockProcessingOutcome::NewForkBlock, block_hash))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn extend_chain(
|
||||
&self,
|
||||
block: &Block,
|
||||
block_hash: &Hash256,
|
||||
head_hash_index: usize)
|
||||
-> Result<>
|
||||
}
|
||||
|
||||
|
||||
impl From<BlockValidationContextError> for BlockProcessingError {
|
||||
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 {
|
||||
fn from(e: SszBeaconBlockValidationError) -> Self {
|
||||
BlockProcessingError::ValidationFailed(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<StateTransitionError> for BlockProcessingError {
|
||||
fn from(e: StateTransitionError) -> Self {
|
||||
BlockProcessingError::StateTransitionFailed(e)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,20 +1,7 @@
|
||||
use types::{
|
||||
CrosslinkRecord,
|
||||
Hash256,
|
||||
ValidatorRegistration,
|
||||
ValidatorStatus,
|
||||
};
|
||||
use super::{
|
||||
ActiveState,
|
||||
CrystallizedState,
|
||||
BeaconChainError,
|
||||
ChainConfig,
|
||||
};
|
||||
use super::{ActiveState, BeaconChainError, ChainConfig, CrystallizedState};
|
||||
use types::{CrosslinkRecord, Hash256, ValidatorStatus};
|
||||
use validator_induction::ValidatorInductor;
|
||||
use validator_shuffling::{
|
||||
shard_and_committees_for_cycle,
|
||||
ValidatorAssignmentError,
|
||||
};
|
||||
use validator_shuffling::{shard_and_committees_for_cycle, ValidatorAssignmentError};
|
||||
|
||||
pub const INITIAL_FORK_VERSION: u32 = 0;
|
||||
|
||||
@@ -27,9 +14,9 @@ impl From<ValidatorAssignmentError> for BeaconChainError {
|
||||
/// Initialize a new ChainHead with genesis parameters.
|
||||
///
|
||||
/// Used when syncing a chain from scratch.
|
||||
pub fn genesis_states(config: &ChainConfig)
|
||||
-> Result<(ActiveState, CrystallizedState), ValidatorAssignmentError>
|
||||
{
|
||||
pub fn genesis_states(
|
||||
config: &ChainConfig,
|
||||
) -> Result<(ActiveState, CrystallizedState), ValidatorAssignmentError> {
|
||||
/*
|
||||
* 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![]);
|
||||
for registration in &config.initial_validators {
|
||||
let _ = inductor.induct(®istration, ValidatorStatus::Active);
|
||||
};
|
||||
}
|
||||
inductor.to_vec()
|
||||
};
|
||||
|
||||
@@ -107,21 +94,14 @@ pub fn genesis_states(config: &ChainConfig)
|
||||
Ok((active_state, crystallized_state))
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
extern crate validator_induction;
|
||||
extern crate bls;
|
||||
extern crate validator_induction;
|
||||
|
||||
use self::bls::{create_proof_of_possession, Keypair};
|
||||
use super::*;
|
||||
use self::bls::{
|
||||
create_proof_of_possession,
|
||||
Keypair,
|
||||
};
|
||||
use types::{
|
||||
Hash256,
|
||||
Address,
|
||||
};
|
||||
use types::{Address, Hash256, ValidatorRegistration};
|
||||
|
||||
#[test]
|
||||
fn test_genesis_no_validators() {
|
||||
@@ -140,7 +120,10 @@ mod tests {
|
||||
assert_eq!(cry.last_finalized_slot, 0);
|
||||
assert_eq!(cry.last_justified_slot, 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.validator_set_delta_hash_chain, Hash256::zero());
|
||||
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_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());
|
||||
}
|
||||
|
||||
@@ -160,7 +146,7 @@ mod tests {
|
||||
withdrawal_shard: 0,
|
||||
withdrawal_address: Address::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 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);
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,33 +1,27 @@
|
||||
extern crate db;
|
||||
extern crate types;
|
||||
extern crate naive_fork_choice;
|
||||
extern crate state_transition;
|
||||
extern crate ssz;
|
||||
extern crate ssz_helpers;
|
||||
extern crate types;
|
||||
extern crate validation;
|
||||
extern crate validator_induction;
|
||||
extern crate validator_shuffling;
|
||||
|
||||
mod stores;
|
||||
mod block_context;
|
||||
mod block_processing;
|
||||
mod maps;
|
||||
mod genesis;
|
||||
mod maps;
|
||||
mod transition;
|
||||
mod stores;
|
||||
|
||||
use db::ClientDB;
|
||||
use genesis::genesis_states;
|
||||
use maps::{
|
||||
generate_attester_and_proposer_maps,
|
||||
AttesterAndProposerMapError,
|
||||
};
|
||||
use maps::{generate_attester_and_proposer_maps, AttesterAndProposerMapError};
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
use stores::BeaconChainStore;
|
||||
use types::{
|
||||
ActiveState,
|
||||
AttesterMap,
|
||||
ChainConfig,
|
||||
CrystallizedState,
|
||||
Hash256,
|
||||
ProposerMap,
|
||||
};
|
||||
use types::{ActiveState, AttesterMap, ChainConfig, CrystallizedState, Hash256, ProposerMap};
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum BeaconChainError {
|
||||
@@ -37,12 +31,6 @@ pub enum BeaconChainError {
|
||||
DBError(String),
|
||||
}
|
||||
|
||||
impl From<AttesterAndProposerMapError> for BeaconChainError {
|
||||
fn from(e: AttesterAndProposerMapError) -> BeaconChainError {
|
||||
BeaconChainError::UnableToGenerateMaps(e)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct BeaconChain<T: ClientDB + Sized> {
|
||||
/// The last slot which has been finalized, this is common to all forks.
|
||||
pub last_finalized_slot: u64,
|
||||
@@ -63,11 +51,10 @@ pub struct BeaconChain<T: ClientDB + Sized> {
|
||||
}
|
||||
|
||||
impl<T> BeaconChain<T>
|
||||
where T: ClientDB + Sized
|
||||
where
|
||||
T: ClientDB + Sized,
|
||||
{
|
||||
pub fn new(store: BeaconChainStore<T>, config: ChainConfig)
|
||||
-> Result<Self, BeaconChainError>
|
||||
{
|
||||
pub fn new(store: BeaconChainStore<T>, config: ChainConfig) -> Result<Self, BeaconChainError> {
|
||||
if config.initial_validators.is_empty() {
|
||||
return Err(BeaconChainError::InsufficientValidators);
|
||||
}
|
||||
@@ -82,15 +69,18 @@ impl<T> BeaconChain<T>
|
||||
let mut attester_proposer_maps = HashMap::new();
|
||||
|
||||
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);
|
||||
crystallized_states.insert(canonical_latest_block_hash, crystallized_state);
|
||||
attester_proposer_maps.insert(
|
||||
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,
|
||||
head_block_hashes,
|
||||
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]
|
||||
}
|
||||
}
|
||||
|
||||
impl From<AttesterAndProposerMapError> for BeaconChainError {
|
||||
fn from(e: AttesterAndProposerMapError) -> BeaconChainError {
|
||||
BeaconChainError::UnableToGenerateMaps(e)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::sync::Arc;
|
||||
use super::*;
|
||||
use types::ValidatorRegistration;
|
||||
use db::MemoryDB;
|
||||
use db::stores::*;
|
||||
use db::MemoryDB;
|
||||
use std::sync::Arc;
|
||||
use types::ValidatorRegistration;
|
||||
|
||||
#[test]
|
||||
fn test_new_chain() {
|
||||
@@ -129,7 +124,9 @@ mod tests {
|
||||
};
|
||||
|
||||
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();
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
use types::{
|
||||
AttesterMap,
|
||||
ProposerMap,
|
||||
ShardAndCommittee,
|
||||
};
|
||||
use types::{AttesterMap, ProposerMap, ShardAndCommittee};
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum AttesterAndProposerMapError {
|
||||
@@ -15,9 +11,8 @@ pub enum AttesterAndProposerMapError {
|
||||
/// The attester map is used to optimise the lookup of a committee.
|
||||
pub fn generate_attester_and_proposer_maps(
|
||||
shard_and_committee_for_slots: &Vec<Vec<ShardAndCommittee>>,
|
||||
start_slot: u64)
|
||||
-> Result<(AttesterMap, ProposerMap), AttesterAndProposerMapError>
|
||||
{
|
||||
start_slot: u64,
|
||||
) -> Result<(AttesterMap, ProposerMap), AttesterAndProposerMapError> {
|
||||
let mut attester_map = AttesterMap::new();
|
||||
let mut proposer_map = ProposerMap::new();
|
||||
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.
|
||||
*/
|
||||
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)?
|
||||
.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)?;
|
||||
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();
|
||||
attester_map.insert((slot_number, shard_and_committee.shard), committee);
|
||||
}
|
||||
};
|
||||
}
|
||||
Ok((attester_map, proposer_map))
|
||||
}
|
||||
|
||||
@@ -47,12 +44,12 @@ pub fn generate_attester_and_proposer_maps(
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
fn sac_generator(shard_count: u16,
|
||||
slot_count: usize,
|
||||
sac_per_slot: usize,
|
||||
committee_size: usize)
|
||||
-> Vec<Vec<ShardAndCommittee>>
|
||||
{
|
||||
fn sac_generator(
|
||||
shard_count: u16,
|
||||
slot_count: usize,
|
||||
sac_per_slot: usize,
|
||||
committee_size: usize,
|
||||
) -> Vec<Vec<ShardAndCommittee>> {
|
||||
let mut shard = 0;
|
||||
let mut validator = 0;
|
||||
let mut cycle = vec![];
|
||||
@@ -80,14 +77,20 @@ mod tests {
|
||||
fn test_attester_proposer_maps_empty_slots() {
|
||||
let sac = sac_generator(4, 4, 0, 1);
|
||||
let result = generate_attester_and_proposer_maps(&sac, 0);
|
||||
assert_eq!(result, Err(AttesterAndProposerMapError::NoShardAndCommitteeForSlot));
|
||||
assert_eq!(
|
||||
result,
|
||||
Err(AttesterAndProposerMapError::NoShardAndCommitteeForSlot)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_attester_proposer_maps_empty_committees() {
|
||||
let sac = sac_generator(4, 4, 1, 0);
|
||||
let result = generate_attester_and_proposer_maps(&sac, 0);
|
||||
assert_eq!(result, Err(AttesterAndProposerMapError::NoAvailableProposer));
|
||||
assert_eq!(
|
||||
result,
|
||||
Err(AttesterAndProposerMapError::NoAvailableProposer)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -1,11 +1,5 @@
|
||||
use db::{
|
||||
ClientDB,
|
||||
};
|
||||
use db::stores::{
|
||||
BeaconBlockStore,
|
||||
PoWChainStore,
|
||||
ValidatorStore,
|
||||
};
|
||||
use db::stores::{BeaconBlockStore, PoWChainStore, ValidatorStore};
|
||||
use db::ClientDB;
|
||||
use std::sync::Arc;
|
||||
|
||||
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))
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user