diff --git a/beacon_chain/chain/Cargo.toml b/beacon_chain/chain/Cargo.toml index ab328dc26..fd20d8100 100644 --- a/beacon_chain/chain/Cargo.toml +++ b/beacon_chain/chain/Cargo.toml @@ -4,6 +4,7 @@ version = "0.1.0" authors = ["Paul Hauner "] [dependencies] +bls = { path = "../utils/bls" } types = { path = "../types" } validator_induction = { path = "../validator_induction" } validator_shuffling = { path = "../validator_shuffling" } diff --git a/beacon_chain/chain/src/blocks.rs b/beacon_chain/chain/src/blocks.rs new file mode 100644 index 000000000..ec43099b6 --- /dev/null +++ b/beacon_chain/chain/src/blocks.rs @@ -0,0 +1,12 @@ +use super::{ + BeaconChain, + BeaconChainError, +}; + +impl BeaconChain { + pub fn validate_serialized_block(&self, ssz: &[u8]) + -> Result<(), BeaconChainError> + { + Ok(()) + } +} diff --git a/beacon_chain/chain/src/genesis.rs b/beacon_chain/chain/src/genesis.rs new file mode 100644 index 000000000..c503ec6ca --- /dev/null +++ b/beacon_chain/chain/src/genesis.rs @@ -0,0 +1,210 @@ +use types::{ + CrosslinkRecord, + Hash256, +}; +use super::{ + ActiveState, + CrystallizedState, + BeaconChain, + BeaconChainError, + ChainConfig, +}; +use validator_induction::{ + ValidatorInductor, + ValidatorRegistration, +}; +use validator_shuffling::{ + shard_and_committees_for_cycle, + ValidatorAssignmentError, +}; + +pub const INITIAL_FORK_VERSION: u32 = 0; + +impl From for BeaconChainError { + fn from(_: ValidatorAssignmentError) -> BeaconChainError { + BeaconChainError::InvalidGenesis + } +} + +impl BeaconChain { + /// Initialize a new ChainHead with genesis parameters. + /// + /// Used when syncing a chain from scratch. + pub fn genesis_states( + initial_validator_entries: &[ValidatorRegistration], + 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 initial_validator_entries { + let _ = inductor.induct(®istration); + }; + 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 + }; + + /* + * 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, + }; + + /* + * 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(), + }; + + Ok((active_state, crystallized_state)) + } +} + + +#[cfg(test)] +mod tests { + extern crate validator_induction; + extern crate bls; + + use super::*; + use self::bls::Keypair; + use types::{ + Hash256, + Address, + }; + use validator_induction::create_proof_of_possession; + + #[test] + fn test_genesis_no_validators() { + let config = ChainConfig::standard(); + let (act, cry) = BeaconChain::genesis_states(&vec![], &config).unwrap(); + + assert_eq!(cry.validator_set_change_slot, 0); + assert_eq!(cry.validators.len(), 0); + assert_eq!(cry.crosslinks.len(), config.shard_count as usize); + for cl in cry.crosslinks { + assert_eq!(cl.recently_changed, false); + assert_eq!(cl.slot, 0); + assert_eq!(cl.hash, Hash256::zero()); + } + assert_eq!(cry.last_state_recalculation_slot, 0); + 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.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); + assert_eq!(cry.post_fork_version, INITIAL_FORK_VERSION); + assert_eq!(cry.fork_slot_number, 0); + + 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.randao_mix, Hash256::zero()); + } + + fn random_registration() -> ValidatorRegistration { + let keypair = Keypair::random(); + ValidatorRegistration { + pubkey: keypair.pk.clone(), + withdrawal_shard: 0, + withdrawal_address: Address::random(), + randao_commitment: Hash256::random(), + proof_of_possession: create_proof_of_possession(&keypair) + } + } + + #[test] + fn test_genesis_valid_validators() { + let config = ChainConfig::standard(); + let validator_count = 5; + + let mut validators = vec![]; + for _ in 0..validator_count { + validators.push(random_registration()); + } + + let (_, cry) = BeaconChain::genesis_states(&validators, &config).unwrap(); + + assert_eq!(cry.validators.len(), validator_count); + } + + #[test] + fn test_genesis_invalid_validators() { + let config = ChainConfig::standard(); + let good_validator_count = 5; + + let mut all_validators = vec![]; + for _ in 0..good_validator_count { + all_validators.push(random_registration()); + } + + let mut bad_v = random_registration(); + let bad_kp = Keypair::random(); + bad_v.proof_of_possession = create_proof_of_possession(&bad_kp); + all_validators.push(bad_v); + + let mut bad_v = random_registration(); + bad_v.withdrawal_shard = config.shard_count + 1; + all_validators.push(bad_v); + + let (_, cry) = BeaconChain::genesis_states(&all_validators, &config).unwrap(); + + assert!(all_validators.len() != good_validator_count, "test is invalid"); + assert_eq!(cry.validators.len(), good_validator_count); + } +} diff --git a/beacon_chain/chain/src/lib.rs b/beacon_chain/chain/src/lib.rs index e4ec8ec8d..d5e7e2e86 100644 --- a/beacon_chain/chain/src/lib.rs +++ b/beacon_chain/chain/src/lib.rs @@ -2,141 +2,63 @@ extern crate types; extern crate validator_induction; extern crate validator_shuffling; +mod blocks; +mod genesis; + +use std::collections::HashMap; use types::{ ActiveState, ChainConfig, - CrosslinkRecord, CrystallizedState, Hash256, }; -use validator_induction::{ - ValidatorInductor, - ValidatorRegistration, -}; -use validator_shuffling::{ - shard_and_committees_for_cycle, - ValidatorAssignmentError, -}; -pub const INITIAL_FORK_VERSION: u32 = 0; - -/// A ChainHead structure represents the "head" or "tip" of a beacon chain blockchain. -/// -/// Initially, a "gensis" chainhead will be created and then new blocks will be built upon it. -pub struct ChainHead { - /// The hash of the block that is the head of the chain. - pub head_hash: Hash256, - /// The active state at this head block. - pub active_state: ActiveState, - /// The crystallized state at this head block. - pub crystallized_state: CrystallizedState, - /// The configuration of the underlying chain. - pub config: ChainConfig, +pub enum BeaconChainError { + InvalidGenesis, + DBError(String), } -impl ChainHead { - /// Initialize a new ChainHead with genesis parameters. - /// - /// Used when syncing a chain from scratch. - pub fn genesis( - initial_validator_entries: &[ValidatorRegistration], - config: ChainConfig) - -> Result +pub struct BeaconChain { + pub last_finalized_slot: Option, + pub canonical_latest_block_hash: Hash256, + pub fork_latest_block_hashes: Vec, + pub active_states: HashMap, + pub crystallized_states: HashMap, +} + +impl BeaconChain { + pub fn new(config: ChainConfig) + -> Result { - /* - * 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 initial_validator_entries { - let _ = inductor.induct(®istration); - }; - inductor.to_vec() - }; + let initial_validators = vec![]; + let (active_state, crystallized_state) = BeaconChain::genesis_states( + &initial_validators, &config)?; - /* - * 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 - }; + 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(); - /* - * 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 - }; + active_states.insert(canonical_latest_block_hash, active_state); + crystallized_states.insert(canonical_latest_block_hash, crystallized_state); - /* - * 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 = { - let mut x = vec![]; - for _ in 0..config.cycle_length { - x.push(Hash256::zero()); - } - x - }; - - /* - * Create an active state. - */ - let active_state = ActiveState { - pending_attestations: vec![], - pending_specials: vec![], - recent_block_hashes, - randao_mix: Hash256::zero(), - }; - - - Ok(Self { - head_hash: Hash256::zero(), - active_state, - crystallized_state, - config, + Ok(Self{ + last_finalized_slot: None, + canonical_latest_block_hash, + fork_latest_block_hashes, + active_states, + crystallized_states, }) } } + #[cfg(test)] mod tests { + use super::*; + #[test] - fn it_works() { + fn test_new_chain() { assert_eq!(2 + 2, 4); } }