Add unfinished progress on BeaconChain struct

This commit is contained in:
Paul Hauner 2018-10-23 13:16:26 +02:00
parent a40b49d586
commit a34266de0a
No known key found for this signature in database
GPG Key ID: 303E4494BB28068C
6 changed files with 343 additions and 90 deletions

View File

@ -5,6 +5,9 @@ authors = ["Paul Hauner <paul@paulhauner.com>"]
[dependencies]
bls = { path = "../utils/bls" }
db = { path = "../../lighthouse/db" }
ssz_helpers = { path = "../utils/ssz_helpers" }
types = { path = "../types" }
validation = { path = "../validation" }
validator_induction = { path = "../validator_induction" }
validator_shuffling = { path = "../validator_shuffling" }

View File

@ -0,0 +1,135 @@
extern crate ssz_helpers;
extern crate validation;
use db::{
ClientDB,
};
use db::stores::{
BeaconBlockAtSlotError,
};
use self::validation::block_validation::{
BeaconBlockValidationContext,
SszBeaconBlockValidationError,
};
use super::{
BeaconChain,
BeaconChainError,
};
use self::ssz_helpers::ssz_beacon_block::{
SszBeaconBlock,
SszBeaconBlockError,
};
use std::sync::Arc;
use types::{
BeaconBlock,
Hash256,
};
pub use self::validation::block_validation::BeaconBlockStatus;
pub enum BeaconChainBlockError {
UnknownCrystallizedState,
UnknownActiveState,
UnknownAttesterProposerMaps,
NoParentHash,
UnknownJustifiedBlock,
BlockAlreadyKnown,
BlockSlotLookupError(BeaconBlockAtSlotError),
BadSsz(SszBeaconBlockError),
BlockValidationError(SszBeaconBlockValidationError),
DBError(String),
}
impl From<BeaconBlockAtSlotError> for BeaconChainBlockError {
fn from(e: BeaconBlockAtSlotError) -> BeaconChainBlockError {
BeaconChainBlockError::BlockSlotLookupError(e)
}
}
impl From<SszBeaconBlockValidationError> for BeaconChainBlockError {
fn from(e: SszBeaconBlockValidationError) -> BeaconChainBlockError {
BeaconChainBlockError::BlockValidationError(e)
}
}
pub type BlockStatusTriple = (BeaconBlockStatus, Hash256, BeaconBlock);
impl<T> BeaconChain<T>
where T: ClientDB + Sized
{
pub fn process_incoming_block(&self, ssz: &[u8], rx_time: u64)
-> Result<BlockStatusTriple, BeaconChainBlockError>
{
/*
* Generate a SszBlock to read directly from the serialized SSZ.
*/
let block = SszBeaconBlock::from_slice(ssz)?;
let block_hash = Hash256::from(&block.block_hash()[..]);
/*
* 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)
.ok_or(BeaconChainBlockError::UnknownCrystallizedState)?;
/*
* Load the active state for this block from our caches.
*
* 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)
.ok_or(BeaconChainBlockError::UnknownActiveState)?;
/*
* Learn the last justified slot from the crystallized state and load
* the hash of this block from the database
*/
let last_justified_slot = cry_state.last_justified_slot;
let parent_block_hash = block.parent_hash()
.ok_or(BeaconChainBlockError::NoParentHash)?;
let (last_justified_block_hash, _) = self.store.block.block_at_slot(
&parent_block_hash, last_justified_slot)?
.ok_or(BeaconChainBlockError::UnknownJustifiedBlock)?;
/*
* Load the attester and proposer maps for the crystallized state.
*/
let (attester_map, proposer_map) = self.attester_proposer_maps.get(&cry_state_root)
.ok_or(BeaconChainBlockError::UnknownAttesterProposerMaps)?;
let present_slot = 100; // TODO: fix this
/*
* Build a block validation context to test the block against.
*/
let validation_context = BeaconBlockValidationContext {
present_slot,
cycle_length: self.config.cycle_length,
last_justified_slot: cry_state.last_justified_slot,
last_justified_block_hash: Hash256::from(&last_justified_block_hash[..]),
last_finalized_slot: self.last_finalized_slot,
recent_block_hashes: Arc::new(act_state.recent_block_hashes.clone()),
proposer_map: proposer_map.clone(),
attester_map: attester_map.clone(),
block_store: self.store.block.clone(),
validator_store: self.store.validator.clone(),
pow_store: self.store.pow_chain.clone(),
};
let (block_status, deserialized_block) = validation_context.validate_ssz_block(&block_hash, &block)?;
match deserialized_block {
Some(b) => Ok((block_status, block_hash, b)),
None => Err(BeaconChainBlockError::BlockAlreadyKnown)
}
}
}
impl From<SszBeaconBlockError> for BeaconChainBlockError {
fn from(e: SszBeaconBlockError) -> BeaconChainBlockError {
BeaconChainBlockError::BadSsz(e)
}
}

View File

@ -6,7 +6,6 @@ use types::{
use super::{
ActiveState,
CrystallizedState,
BeaconChain,
BeaconChainError,
ChainConfig,
};
@ -24,89 +23,87 @@ impl From<ValidatorAssignmentError> for BeaconChainError {
}
}
impl BeaconChain {
/// Initialize a new ChainHead with genesis parameters.
///
/// Used when syncing a chain from scratch.
pub fn genesis_states(config: &ChainConfig)
-> Result<(ActiveState, CrystallizedState), ValidatorAssignmentError>
{
/*
* Parse the ValidatorRegistrations into ValidatorRecords and induct them.
*
* Ignore any records which fail proof-of-possession or are invalid.
*/
let validators = {
let mut inductor = ValidatorInductor::new(0, config.shard_count, vec![]);
for registration in &config.initial_validators {
let _ = inductor.induct(&registration);
};
inductor.to_vec()
/// Initialize a new ChainHead with genesis parameters.
///
/// Used when syncing a chain from scratch.
pub fn genesis_states(config: &ChainConfig)
-> Result<(ActiveState, CrystallizedState), ValidatorAssignmentError>
{
/*
* Parse the ValidatorRegistrations into ValidatorRecords and induct them.
*
* Ignore any records which fail proof-of-possession or are invalid.
*/
let validators = {
let mut inductor = ValidatorInductor::new(0, config.shard_count, vec![]);
for registration in &config.initial_validators {
let _ = inductor.induct(&registration);
};
inductor.to_vec()
};
/*
* Assign the validators to shards, using all zeros as the seed.
*
* Crystallizedstate stores two cycles, so we simply repeat the same assignment twice.
*/
let shard_and_committee_for_slots = {
let mut a = shard_and_committees_for_cycle(&vec![0; 32], &validators, 0, &config)?;
let mut b = a.clone();
a.append(&mut b);
a
};
/*
* Assign the validators to shards, using all zeros as the seed.
*
* Crystallizedstate stores two cycles, so we simply repeat the same assignment twice.
*/
let shard_and_committee_for_slots = {
let mut a = shard_and_committees_for_cycle(&vec![0; 32], &validators, 0, &config)?;
let mut b = a.clone();
a.append(&mut b);
a
};
/*
* Set all the crosslink records to reference zero hashes.
*/
let crosslinks = {
let mut c = vec![];
for _ in 0..config.shard_count {
c.push(CrosslinkRecord {
recently_changed: false,
slot: 0,
hash: Hash256::zero(),
});
}
c
};
/*
* Set all the crosslink records to reference zero hashes.
*/
let crosslinks = {
let mut c = vec![];
for _ in 0..config.shard_count {
c.push(CrosslinkRecord {
recently_changed: false,
slot: 0,
hash: Hash256::zero(),
});
}
c
};
/*
* Initialize a genesis `Crystallizedstate`
*/
let crystallized_state = CrystallizedState {
validator_set_change_slot: 0,
validators: validators.to_vec(),
crosslinks,
last_state_recalculation_slot: 0,
last_finalized_slot: 0,
last_justified_slot: 0,
justified_streak: 0,
shard_and_committee_for_slots,
deposits_penalized_in_period: vec![],
validator_set_delta_hash_chain: Hash256::zero(),
pre_fork_version: INITIAL_FORK_VERSION,
post_fork_version: INITIAL_FORK_VERSION,
fork_slot_number: 0,
};
/*
* Initialize a genesis `Crystallizedstate`
*/
let crystallized_state = CrystallizedState {
validator_set_change_slot: 0,
validators: validators.to_vec(),
crosslinks,
last_state_recalculation_slot: 0,
last_finalized_slot: 0,
last_justified_slot: 0,
justified_streak: 0,
shard_and_committee_for_slots,
deposits_penalized_in_period: vec![],
validator_set_delta_hash_chain: Hash256::zero(),
pre_fork_version: INITIAL_FORK_VERSION,
post_fork_version: INITIAL_FORK_VERSION,
fork_slot_number: 0,
};
/*
* Set all recent block hashes to zero.
*/
let recent_block_hashes = vec![Hash256::zero(); config.cycle_length as usize];
/*
* Set all recent block hashes to zero.
*/
let recent_block_hashes = vec![Hash256::zero(); config.cycle_length as usize];
/*
* Create an active state.
*/
let active_state = ActiveState {
pending_attestations: vec![],
pending_specials: vec![],
recent_block_hashes,
randao_mix: Hash256::zero(),
};
/*
* Create an active state.
*/
let active_state = ActiveState {
pending_attestations: vec![],
pending_specials: vec![],
recent_block_hashes,
randao_mix: Hash256::zero(),
};
Ok((active_state, crystallized_state))
}
Ok((active_state, crystallized_state))
}
@ -128,7 +125,7 @@ mod tests {
#[test]
fn test_genesis_no_validators() {
let config = ChainConfig::standard();
let (act, cry) = BeaconChain::genesis_states(&config).unwrap();
let (act, cry) = genesis_states(&config).unwrap();
assert_eq!(cry.validator_set_change_slot, 0);
assert_eq!(cry.validators.len(), 0);
@ -175,7 +172,7 @@ mod tests {
config.initial_validators.push(random_registration());
}
let (_, cry) = BeaconChain::genesis_states(&config).unwrap();
let (_, cry) = genesis_states(&config).unwrap();
assert_eq!(cry.validators.len(), validator_count);
}
@ -198,7 +195,7 @@ mod tests {
bad_v.withdrawal_shard = config.shard_count + 1;
config.initial_validators.push(bad_v);
let (_, cry) = BeaconChain::genesis_states(&config).unwrap();
let (_, cry) = genesis_states(&config).unwrap();
assert!(config.initial_validators.len() != good_validator_count, "test is invalid");
assert_eq!(cry.validators.len(), good_validator_count);

View File

@ -1,52 +1,99 @@
extern crate db;
extern crate types;
extern crate validator_induction;
extern crate validator_shuffling;
mod stores;
mod blocks;
mod maps;
mod genesis;
use db::ClientDB;
use genesis::genesis_states;
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,
};
#[derive(Debug, PartialEq)]
pub enum BeaconChainError {
InvalidGenesis,
InsufficientValidators,
UnableToGenerateMaps(AttesterAndProposerMapError),
DBError(String),
}
pub struct BeaconChain {
pub last_finalized_slot: Option<u64>,
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,
/// The hash of the head of the canonical chain.
pub canonical_latest_block_hash: Hash256,
/// A vec of hashes of heads of fork (non-canonical) chains.
pub fork_latest_block_hashes: Vec<Hash256>,
/// A map where the value is an active state the the key is its hash.
pub active_states: HashMap<Hash256, ActiveState>,
/// A map where the value is crystallized state the the key is its hash.
pub crystallized_states: HashMap<Hash256, CrystallizedState>,
/// A map of crystallized state to a proposer and attester map.
pub attester_proposer_maps: HashMap<Hash256, (Arc<AttesterMap>, Arc<ProposerMap>)>,
/// A collection of database stores used by the chain.
pub store: BeaconChainStore<T>,
/// The chain configuration.
pub config: ChainConfig,
}
impl BeaconChain {
pub fn new(config: ChainConfig)
impl<T> BeaconChain<T>
where T: ClientDB + Sized
{
pub fn new(store: BeaconChainStore<T>, config: ChainConfig)
-> Result<Self, BeaconChainError>
{
let (active_state, crystallized_state) = BeaconChain::genesis_states(&config)?;
if config.initial_validators.is_empty() {
return Err(BeaconChainError::InsufficientValidators);
}
let (active_state, crystallized_state) = genesis_states(&config)?;
let canonical_latest_block_hash = Hash256::zero();
let fork_latest_block_hashes = vec![];
let mut active_states = HashMap::new();
let mut crystallized_states = HashMap::new();
let mut attester_proposer_maps = HashMap::new();
let (attester_map, proposer_map) = generate_attester_and_proposer_maps(
&crystallized_state, 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)));
Ok(Self{
last_finalized_slot: None,
last_finalized_slot: 0,
canonical_latest_block_hash,
fork_latest_block_hashes,
active_states,
crystallized_states,
attester_proposer_maps,
store,
config,
})
}
@ -55,21 +102,32 @@ impl BeaconChain {
#[cfg(test)]
mod tests {
use std::sync::Arc;
use super::*;
use types::ValidatorRegistration;
use db::MemoryDB;
use db::stores::*;
#[test]
fn test_new_chain() {
let mut config = ChainConfig::standard();
config.cycle_length = 4;
config.shard_count = 4;
let db = Arc::new(MemoryDB::open());
let store = BeaconChainStore {
block: Arc::new(BeaconBlockStore::new(db.clone())),
pow_chain: Arc::new(PoWChainStore::new(db.clone())),
validator: Arc::new(ValidatorStore::new(db.clone())),
};
for _ in 0..4 {
for _ in 0..config.cycle_length * 2 {
config.initial_validators.push(ValidatorRegistration::random())
}
let chain = BeaconChain::new(config.clone()).unwrap();
let (act, cry) = BeaconChain::genesis_states(&config).unwrap();
let chain = BeaconChain::new(store, config.clone()).unwrap();
let (act, cry) = genesis_states(&config).unwrap();
assert_eq!(chain.last_finalized_slot, None);
assert_eq!(chain.last_finalized_slot, 0);
assert_eq!(chain.canonical_latest_block_hash, Hash256::zero());
let stored_act = chain.active_states.get(&Hash256::zero()).unwrap();

View File

@ -0,0 +1,45 @@
use types::{
AttesterMap,
CrystallizedState,
ProposerMap,
};
#[derive(Debug, PartialEq)]
pub enum AttesterAndProposerMapError {
NoShardAndCommitteeForSlot,
NoAvailableProposer,
}
/// Generate a map of `(slot, shard) |--> committee`.
///
/// The attester map is used to optimise the lookup of a committee.
pub fn generate_attester_and_proposer_maps(cry_state: &CrystallizedState, start_slot: u64)
-> Result<(AttesterMap, ProposerMap), AttesterAndProposerMapError>
{
let mut attester_map = AttesterMap::new();
let mut proposer_map = ProposerMap::new();
for (i, slot) in cry_state.shard_and_committee_for_slots.iter().enumerate() {
/*
* Store the proposer for the block.
*/
let slot_number = (i as u64).saturating_add(start_slot);
let first_committee = {
let first_shard_and_committee = slot.get(0)
.ok_or(AttesterAndProposerMapError::NoShardAndCommitteeForSlot)?;
first_shard_and_committee.committee.clone()
};
println!("{:?}", slot);
let proposer_index = (slot_number as usize).checked_rem(first_committee.len())
.ok_or(AttesterAndProposerMapError::NoAvailableProposer)?;
proposer_map.insert(slot_number, proposer_index);
/*
* Loop through the shards and extend the attester map.
*/
for shard_and_committee in slot {
let committee = shard_and_committee.committee.clone();
attester_map.insert((slot_number, shard_and_committee.shard), committee);
}
};
Ok((attester_map, proposer_map))
}

View File

@ -0,0 +1,15 @@
use db::{
ClientDB,
};
use db::stores::{
BeaconBlockStore,
PoWChainStore,
ValidatorStore,
};
use std::sync::Arc;
pub struct BeaconChainStore<T: ClientDB + Sized> {
pub block: Arc<BeaconBlockStore<T>>,
pub pow_chain: Arc<PoWChainStore<T>>,
pub validator: Arc<ValidatorStore<T>>,
}