From 606c32950d5ae1166bcf5900a39dcdef0ad17ede Mon Sep 17 00:00:00 2001 From: Paul Hauner Date: Sat, 20 Oct 2018 13:08:43 +1100 Subject: [PATCH 01/24] Move shuffling functions around - Move `delegation` from the `transition` dir into its own dir: `beacon_chain/validator_shuffling` - Rename `beacon_chain/utils/shuffling` -> `vec_shuffle` --- Cargo.toml | 4 +-- beacon_chain/transition/src/delegation/mod.rs | 6 ---- beacon_chain/transition/src/lib.rs | 10 ------ .../{shuffling => vec_shuffle}/Cargo.toml | 2 +- .../{shuffling => vec_shuffle}/src/lib.rs | 0 .../{shuffling => vec_shuffle}/src/rng.rs | 0 beacon_chain/validator_induction/src/lib.rs | 4 +++ .../Cargo.toml | 6 ++-- .../src/active_validator_indices.rs | 19 +++++++++++ beacon_chain/validator_shuffling/src/lib.rs | 8 +++++ .../src/shuffle.rs} | 33 +++++++------------ 11 files changed, 49 insertions(+), 43 deletions(-) delete mode 100644 beacon_chain/transition/src/delegation/mod.rs delete mode 100644 beacon_chain/transition/src/lib.rs rename beacon_chain/utils/{shuffling => vec_shuffle}/Cargo.toml (85%) rename beacon_chain/utils/{shuffling => vec_shuffle}/src/lib.rs (100%) rename beacon_chain/utils/{shuffling => vec_shuffle}/src/rng.rs (100%) rename beacon_chain/{transition => validator_shuffling}/Cargo.toml (52%) create mode 100644 beacon_chain/validator_shuffling/src/active_validator_indices.rs create mode 100644 beacon_chain/validator_shuffling/src/lib.rs rename beacon_chain/{transition/src/delegation/validator.rs => validator_shuffling/src/shuffle.rs} (92%) diff --git a/Cargo.toml b/Cargo.toml index 55bd4de53..0ac729929 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,15 +33,15 @@ name = "lighthouse" [workspace] members = [ "beacon_chain/types", - "beacon_chain/transition", "beacon_chain/utils/bls", "beacon_chain/utils/boolean-bitfield", "beacon_chain/utils/hashing", "beacon_chain/utils/honey-badger-split", - "beacon_chain/utils/shuffling", "beacon_chain/utils/ssz", "beacon_chain/utils/ssz_helpers", + "beacon_chain/utils/vec_shuffle", "beacon_chain/validation", "beacon_chain/validator_induction", + "beacon_chain/validator_shuffling", "lighthouse/db", ] diff --git a/beacon_chain/transition/src/delegation/mod.rs b/beacon_chain/transition/src/delegation/mod.rs deleted file mode 100644 index 66f3304f3..000000000 --- a/beacon_chain/transition/src/delegation/mod.rs +++ /dev/null @@ -1,6 +0,0 @@ -use super::honey_badger_split; -use super::types; -use super::TransitionError; -use super::shuffling::shuffle; - -pub mod validator; diff --git a/beacon_chain/transition/src/lib.rs b/beacon_chain/transition/src/lib.rs deleted file mode 100644 index ccac52529..000000000 --- a/beacon_chain/transition/src/lib.rs +++ /dev/null @@ -1,10 +0,0 @@ -extern crate honey_badger_split; -extern crate types; -extern crate shuffling; - -pub mod delegation; - -#[derive(Debug)] -pub enum TransitionError { - InvalidInput(String), -} diff --git a/beacon_chain/utils/shuffling/Cargo.toml b/beacon_chain/utils/vec_shuffle/Cargo.toml similarity index 85% rename from beacon_chain/utils/shuffling/Cargo.toml rename to beacon_chain/utils/vec_shuffle/Cargo.toml index df0a108ee..4fa29288a 100644 --- a/beacon_chain/utils/shuffling/Cargo.toml +++ b/beacon_chain/utils/vec_shuffle/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "shuffling" +name = "vec_shuffle" version = "0.1.0" authors = ["Paul Hauner "] diff --git a/beacon_chain/utils/shuffling/src/lib.rs b/beacon_chain/utils/vec_shuffle/src/lib.rs similarity index 100% rename from beacon_chain/utils/shuffling/src/lib.rs rename to beacon_chain/utils/vec_shuffle/src/lib.rs diff --git a/beacon_chain/utils/shuffling/src/rng.rs b/beacon_chain/utils/vec_shuffle/src/rng.rs similarity index 100% rename from beacon_chain/utils/shuffling/src/rng.rs rename to beacon_chain/utils/vec_shuffle/src/rng.rs diff --git a/beacon_chain/validator_induction/src/lib.rs b/beacon_chain/validator_induction/src/lib.rs index 6c5bf11a9..4a3d9bb0a 100644 --- a/beacon_chain/validator_induction/src/lib.rs +++ b/beacon_chain/validator_induction/src/lib.rs @@ -10,3 +10,7 @@ pub use inductor::{ ValidatorInductor, ValidatorInductionError, }; + +pub use registration::{ + ValidatorRegistration, +}; diff --git a/beacon_chain/transition/Cargo.toml b/beacon_chain/validator_shuffling/Cargo.toml similarity index 52% rename from beacon_chain/transition/Cargo.toml rename to beacon_chain/validator_shuffling/Cargo.toml index c17d6994f..ba99841b8 100644 --- a/beacon_chain/transition/Cargo.toml +++ b/beacon_chain/validator_shuffling/Cargo.toml @@ -1,9 +1,9 @@ [package] -name = "transition" +name = "validator_shuffling" version = "0.1.0" -authors = ["Age Manning "] +authors = ["Paul Hauner "] [dependencies] honey-badger-split = { path = "../utils/honey-badger-split" } types = { path = "../types" } -shuffling = { path = "../utils/shuffling" } +vec_shuffle = { path = "../utils/vec_shuffle" } diff --git a/beacon_chain/validator_shuffling/src/active_validator_indices.rs b/beacon_chain/validator_shuffling/src/active_validator_indices.rs new file mode 100644 index 000000000..1a6b7b99a --- /dev/null +++ b/beacon_chain/validator_shuffling/src/active_validator_indices.rs @@ -0,0 +1,19 @@ +use types::{ + ValidatorRecord, + ValidatorStatus, +}; + +/// Returns the indicies of each active validator in a given vec of validators. +pub fn active_validator_indices(validators: &[ValidatorRecord]) + -> Vec +{ + validators.iter() + .enumerate() + .filter_map(|(i, validator)| { + match validator.status { + x if x == ValidatorStatus::Active as u8 => Some(i), + _ => None + } + }) + .collect() +} diff --git a/beacon_chain/validator_shuffling/src/lib.rs b/beacon_chain/validator_shuffling/src/lib.rs new file mode 100644 index 000000000..a11483e56 --- /dev/null +++ b/beacon_chain/validator_shuffling/src/lib.rs @@ -0,0 +1,8 @@ +extern crate honey_badger_split; +extern crate vec_shuffle; +extern crate types; + +mod active_validator_indices; +mod shuffle; + +pub use shuffle::shard_and_committees_for_cycle; diff --git a/beacon_chain/transition/src/delegation/validator.rs b/beacon_chain/validator_shuffling/src/shuffle.rs similarity index 92% rename from beacon_chain/transition/src/delegation/validator.rs rename to beacon_chain/validator_shuffling/src/shuffle.rs index ab7c06b64..1ce778a6b 100644 --- a/beacon_chain/transition/src/delegation/validator.rs +++ b/beacon_chain/validator_shuffling/src/shuffle.rs @@ -1,29 +1,20 @@ -use super::honey_badger_split::SplitExt; -use super::types::{ +use std::cmp::min; + +use honey_badger_split::SplitExt; +use vec_shuffle::shuffle; +use types::{ ShardAndCommittee, ValidatorRecord, - ValidatorStatus, ChainConfig, }; -use super::TransitionError; -use super::shuffle; -use std::cmp::min; + +use super::active_validator_indices::active_validator_indices; type DelegatedCycle = Vec>; -/// Returns the indicies of each active validator in a given vec of validators. -fn active_validator_indicies(validators: &[ValidatorRecord]) - -> Vec -{ - validators.iter() - .enumerate() - .filter_map(|(i, validator)| { - match validator.status { - x if x == ValidatorStatus::Active as u8 => Some(i), - _ => None - } - }) - .collect() +#[derive(Debug)] +pub enum TransitionError { + InvalidInput(String), } @@ -31,7 +22,7 @@ fn active_validator_indicies(validators: &[ValidatorRecord]) /// Returns a vector or ShardAndComitte vectors representing the shards and committiees for /// each slot. /// References get_new_shuffling (ethereum 2.1 specification) -pub fn delegate_validators( +pub fn shard_and_committees_for_cycle( seed: &[u8], validators: &[ValidatorRecord], crosslinking_shard_start: u16, @@ -39,7 +30,7 @@ pub fn delegate_validators( -> Result { let shuffled_validator_indices = { - let mut validator_indices = active_validator_indicies(validators); + let mut validator_indices = active_validator_indices(validators); match shuffle(seed, validator_indices) { Ok(shuffled) => shuffled, _ => return Err(TransitionError::InvalidInput( From 073e3529e9f7066f6aede86aa49f0905ee395916 Mon Sep 17 00:00:00 2001 From: Paul Hauner Date: Sat, 20 Oct 2018 16:34:08 +1100 Subject: [PATCH 02/24] Add half-finished chain code --- Cargo.toml | 1 + beacon_chain/chain/Cargo.toml | 9 +++++ beacon_chain/chain/src/lib.rs | 67 +++++++++++++++++++++++++++++++++++ 3 files changed, 77 insertions(+) create mode 100644 beacon_chain/chain/Cargo.toml create mode 100644 beacon_chain/chain/src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index 0ac729929..57f0feb47 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,6 +32,7 @@ name = "lighthouse" [workspace] members = [ + "beacon_chain/chain", "beacon_chain/types", "beacon_chain/utils/bls", "beacon_chain/utils/boolean-bitfield", diff --git a/beacon_chain/chain/Cargo.toml b/beacon_chain/chain/Cargo.toml new file mode 100644 index 000000000..ab328dc26 --- /dev/null +++ b/beacon_chain/chain/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "chain" +version = "0.1.0" +authors = ["Paul Hauner "] + +[dependencies] +types = { path = "../types" } +validator_induction = { path = "../validator_induction" } +validator_shuffling = { path = "../validator_shuffling" } diff --git a/beacon_chain/chain/src/lib.rs b/beacon_chain/chain/src/lib.rs new file mode 100644 index 000000000..cff48c986 --- /dev/null +++ b/beacon_chain/chain/src/lib.rs @@ -0,0 +1,67 @@ +extern crate types; +extern crate validator_induction; +extern crate validator_shuffling; + +use types::{ + ActiveState, + ChainConfig, + CrystallizedState, + ValidatorRecord, +}; +use validator_induction::{ + ValidatorInductor, + ValidatorRegistration, +}; +use validator_shuffling::shard_and_committees_for_cycle; + +pub struct ChainHead<'a> { + act_state: ActiveState, + cry_state: &'a CrystallizedState, + config: ChainConfig, +} + +impl<'a> ChainHead<'a> { + pub fn genesis( + initial_validator_entries: &[ValidatorRegistration], + config: ChainConfig) + -> Self + { + /* + * Parse the ValidatorRegistrations into ValidatorRecords and induct them. + * + * Ignore any records which fail proof-of-possession or are invalid. + */ + let validators = { + let mut validators = vec![]; + let inductor = ValidatorInductor { + current_slot: 0, + shard_count: config.shard_count, + validators: &mut validators, + empty_validator_start: 0, + }; + for registration in initial_validator_entries { + let _ = inductor.induct(®istration); + }; + validators + }; + + /* + * Delegate the validators to shards. + */ + let shard_and_committees = shard_and_committees_for_cycle( + &vec![0; 32], + &validators, + 0, + &config); + + //TODO: complete this + } +} + +#[cfg(test)] +mod tests { + #[test] + fn it_works() { + assert_eq!(2 + 2, 4); + } +} From de8b84f9cd6a4fadbfc03270544b32a345f8b5b7 Mon Sep 17 00:00:00 2001 From: Paul Hauner Date: Sat, 20 Oct 2018 17:15:36 +1100 Subject: [PATCH 03/24] Update `ActiveState` as per new spec --- beacon_chain/types/src/active_state.rs | 30 ++++++-------------------- 1 file changed, 6 insertions(+), 24 deletions(-) diff --git a/beacon_chain/types/src/active_state.rs b/beacon_chain/types/src/active_state.rs index 6c71cadb8..4b72910fa 100644 --- a/beacon_chain/types/src/active_state.rs +++ b/beacon_chain/types/src/active_state.rs @@ -1,30 +1,12 @@ use super::Hash256; -use super::attestation_record::AttestationRecord; +use super::{ + AttestationRecord, + SpecialRecord, +}; pub struct ActiveState { pub pending_attestations: Vec, + pub pending_specials: Vec, pub recent_block_hashes: Vec, -} - -impl ActiveState { - /// Returns a new instance where all fields are empty vectors. - pub fn zero() -> Self { - Self { - pending_attestations: vec![], - recent_block_hashes: vec![], - } - } -} - - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_act_state_zero() { - let a = ActiveState::zero(); - assert_eq!(a.pending_attestations.len(), 0); - assert_eq!(a.recent_block_hashes.len(), 0); - } + pub randao_mix: Hash256, } From a8d2b205705fc4f1c5983539c9f185c513c6d695 Mon Sep 17 00:00:00 2001 From: Paul Hauner Date: Sat, 20 Oct 2018 17:15:53 +1100 Subject: [PATCH 04/24] Update `CrystallizedState` as per new spec. --- beacon_chain/types/src/crosslink_record.rs | 9 ++- beacon_chain/types/src/crystallized_state.rs | 66 ++++---------------- 2 files changed, 17 insertions(+), 58 deletions(-) diff --git a/beacon_chain/types/src/crosslink_record.rs b/beacon_chain/types/src/crosslink_record.rs index a28284681..2c7525fad 100644 --- a/beacon_chain/types/src/crosslink_record.rs +++ b/beacon_chain/types/src/crosslink_record.rs @@ -2,7 +2,8 @@ use super::Hash256; #[derive(Clone)] pub struct CrosslinkRecord { - pub dynasty: u64, + pub recently_changed: bool, + pub slot: u64, pub hash: Hash256, } @@ -10,7 +11,8 @@ impl CrosslinkRecord { /// Generates a new instance where `dynasty` and `hash` are both zero. pub fn zero() -> Self { Self { - dynasty: 0, + recently_changed: false, + slot: 0, hash: Hash256::zero(), } } @@ -23,7 +25,8 @@ mod tests { #[test] fn test_crosslink_record_zero() { let c = CrosslinkRecord::zero(); - assert_eq!(c.dynasty, 0); + assert_eq!(c.recently_changed, false); + assert_eq!(c.slot, 0); assert!(c.hash.is_zero()); } } diff --git a/beacon_chain/types/src/crystallized_state.rs b/beacon_chain/types/src/crystallized_state.rs index e6d901495..580fc04b0 100644 --- a/beacon_chain/types/src/crystallized_state.rs +++ b/beacon_chain/types/src/crystallized_state.rs @@ -1,65 +1,21 @@ use super::validator_record::ValidatorRecord; use super::crosslink_record::CrosslinkRecord; use super::shard_and_committee::ShardAndCommittee; -use super::ethereum_types::U256; use super::Hash256; pub struct CrystallizedState { + pub validator_set_change_slot: u64, pub validators: Vec, - pub epoch_number: u64, - pub indicies_for_heights: Vec, - pub last_justified_slot: u64, - pub justified_streak: u16, + pub crosslinks: Vec, + pub last_state_recalculation_slot: u64, pub last_finalized_slot: u64, - pub current_dynasty: u64, - pub crosslinking_shard_start: u16, - pub crosslink_records: Vec, - pub total_deposits: U256, - pub dynasty_seed: Hash256, - pub dynasty_seed_last_reset: u64, -} - -impl CrystallizedState { - /// Returns a new instance where all fields are either zero or an - /// empty vector. - pub fn zero() -> Self { - Self { - validators: vec![], - epoch_number: 0, - indicies_for_heights: vec![], - last_justified_slot: 0, - justified_streak: 0, - last_finalized_slot: 0, - current_dynasty: 0, - crosslinking_shard_start: 0, - crosslink_records: vec![], - total_deposits: U256::zero(), - dynasty_seed: Hash256::zero(), - dynasty_seed_last_reset: 0, - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_cry_state_zero() { - let c = CrystallizedState::zero(); - assert_eq!(c.validators.len(), 0); - assert_eq!(c.epoch_number, 0); - assert_eq!(c.indicies_for_heights.len(), 0); - assert_eq!(c.last_justified_slot, 0); - assert_eq!(c.justified_streak, 0); - assert_eq!(c.last_finalized_slot, 0); - assert_eq!(c.current_dynasty, 0); - assert_eq!(c.crosslinking_shard_start, 0); - assert_eq!(c.crosslink_records.len(), 0); - assert!(c.total_deposits.is_zero()); - assert!(c.dynasty_seed.is_zero()); - assert_eq!(c.dynasty_seed_last_reset, 0); - } - + pub last_justified_slot: u64, + pub justified_streak: u64, + pub shard_and_committee_for_slots: Vec>, + pub deposits_penalized_in_period: u32, + pub validator_set_delta_hash_chain: Hash256, + pub pre_fork_version: u32, + pub post_fork_version: u32, + pub fork_slot_number: u32, } From 7508bfca6a541b31146a30a3ed2d808943eb5608 Mon Sep 17 00:00:00 2001 From: Paul Hauner Date: Sat, 20 Oct 2018 18:10:41 +1100 Subject: [PATCH 05/24] Change error handining in validator_shuffling --- beacon_chain/validator_shuffling/src/lib.rs | 5 ++- .../validator_shuffling/src/shuffle.rs | 38 ++++++++++--------- 2 files changed, 25 insertions(+), 18 deletions(-) diff --git a/beacon_chain/validator_shuffling/src/lib.rs b/beacon_chain/validator_shuffling/src/lib.rs index a11483e56..ee6f5e8fb 100644 --- a/beacon_chain/validator_shuffling/src/lib.rs +++ b/beacon_chain/validator_shuffling/src/lib.rs @@ -5,4 +5,7 @@ extern crate types; mod active_validator_indices; mod shuffle; -pub use shuffle::shard_and_committees_for_cycle; +pub use shuffle::{ + shard_and_committees_for_cycle, + ValidatorAssignmentError, +}; diff --git a/beacon_chain/validator_shuffling/src/shuffle.rs b/beacon_chain/validator_shuffling/src/shuffle.rs index 1ce778a6b..4768c1cb1 100644 --- a/beacon_chain/validator_shuffling/src/shuffle.rs +++ b/beacon_chain/validator_shuffling/src/shuffle.rs @@ -1,7 +1,10 @@ use std::cmp::min; use honey_badger_split::SplitExt; -use vec_shuffle::shuffle; +use vec_shuffle::{ + shuffle, + ShuffleErr, +}; use types::{ ShardAndCommittee, ValidatorRecord, @@ -12,12 +15,12 @@ use super::active_validator_indices::active_validator_indices; type DelegatedCycle = Vec>; -#[derive(Debug)] -pub enum TransitionError { - InvalidInput(String), +#[derive(Debug, PartialEq)] +pub enum ValidatorAssignmentError { + TooManyValidators, + TooFewShards, } - /// Delegates active validators into slots for a given cycle, given a random seed. /// Returns a vector or ShardAndComitte vectors representing the shards and committiees for /// each slot. @@ -27,15 +30,11 @@ pub fn shard_and_committees_for_cycle( validators: &[ValidatorRecord], crosslinking_shard_start: u16, config: &ChainConfig) - -> Result + -> Result { let shuffled_validator_indices = { let mut validator_indices = active_validator_indices(validators); - match shuffle(seed, validator_indices) { - Ok(shuffled) => shuffled, - _ => return Err(TransitionError::InvalidInput( - String::from("Shuffle list length exceed."))) - } + shuffle(seed, validator_indices)? }; let shard_indices: Vec = (0_usize..config.shard_count as usize).into_iter().collect(); let crosslinking_shard_start = crosslinking_shard_start as usize; @@ -56,17 +55,14 @@ fn generate_cycle( crosslinking_shard_start: usize, cycle_length: usize, min_committee_size: usize) - -> Result + -> Result { let validator_count = validator_indices.len(); let shard_count = shard_indices.len(); if shard_count / cycle_length == 0 { - return Err(TransitionError::InvalidInput(String::from("Number of - shards needs to be greater than - cycle length"))); - + return Err(ValidatorAssignmentError::TooFewShards) } let (committees_per_slot, slots_per_committee) = { @@ -105,6 +101,14 @@ fn generate_cycle( Ok(cycle) } +impl From for ValidatorAssignmentError { + fn from(e: ShuffleErr) -> ValidatorAssignmentError { + match e { + ShuffleErr::ExceedsListLength => ValidatorAssignmentError::TooManyValidators, + } + } +} + #[cfg(test)] mod tests { use super::*; @@ -115,7 +119,7 @@ mod tests { crosslinking_shard_start: usize, cycle_length: usize, min_committee_size: usize) - -> (Vec, Vec, Result) + -> (Vec, Vec, Result) { let validator_indices: Vec = (0_usize..*validator_count).into_iter().collect(); let shard_indices: Vec = (0_usize..*shard_count).into_iter().collect(); From bb75e30ff2e9adc3f58a0a9bcd5e36d6aceecc0d Mon Sep 17 00:00:00 2001 From: Paul Hauner Date: Sat, 20 Oct 2018 19:02:02 +1100 Subject: [PATCH 06/24] Fix error in CrystallizedState definition --- beacon_chain/types/src/crystallized_state.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/beacon_chain/types/src/crystallized_state.rs b/beacon_chain/types/src/crystallized_state.rs index 580fc04b0..21b4eb64e 100644 --- a/beacon_chain/types/src/crystallized_state.rs +++ b/beacon_chain/types/src/crystallized_state.rs @@ -13,7 +13,7 @@ pub struct CrystallizedState { pub last_justified_slot: u64, pub justified_streak: u64, pub shard_and_committee_for_slots: Vec>, - pub deposits_penalized_in_period: u32, + pub deposits_penalized_in_period: Vec, pub validator_set_delta_hash_chain: Hash256, pub pre_fork_version: u32, pub post_fork_version: u32, From eaf1198ec4f57ef25b4c1266e6a9e4c1faee3a9e Mon Sep 17 00:00:00 2001 From: Paul Hauner Date: Sat, 20 Oct 2018 19:02:15 +1100 Subject: [PATCH 07/24] Add more half-finished genesis code --- beacon_chain/chain/src/lib.rs | 89 ++++++++++++++++++++++++++++++----- 1 file changed, 76 insertions(+), 13 deletions(-) diff --git a/beacon_chain/chain/src/lib.rs b/beacon_chain/chain/src/lib.rs index cff48c986..27d605403 100644 --- a/beacon_chain/chain/src/lib.rs +++ b/beacon_chain/chain/src/lib.rs @@ -5,26 +5,33 @@ extern crate validator_shuffling; use types::{ ActiveState, ChainConfig, + CrosslinkRecord, CrystallizedState, + Hash256, ValidatorRecord, }; use validator_induction::{ ValidatorInductor, ValidatorRegistration, }; -use validator_shuffling::shard_and_committees_for_cycle; +use validator_shuffling::{ + shard_and_committees_for_cycle, + ValidatorAssignmentError, +}; -pub struct ChainHead<'a> { - act_state: ActiveState, - cry_state: &'a CrystallizedState, +pub const INITIAL_FORK_VERSION: u32 = 0; + +pub struct ChainHead { + active_state: ActiveState, + crystallized_state: CrystallizedState, config: ChainConfig, } -impl<'a> ChainHead<'a> { +impl ChainHead { pub fn genesis( initial_validator_entries: &[ValidatorRegistration], config: ChainConfig) - -> Self + -> Result { /* * Parse the ValidatorRegistrations into ValidatorRecords and induct them. @@ -46,15 +53,71 @@ impl<'a> ChainHead<'a> { }; /* - * Delegate the validators to shards. + * 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_committees = shard_and_committees_for_cycle( - &vec![0; 32], - &validators, - 0, - &config); + let shard_and_committee_for_slots = { + let x = shard_and_committees_for_cycle(&vec![0; 32], &validators, 0, &config)?; + x.append(&mut x.clone()); + x + }; - //TODO: complete this + /* + * 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, + 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, + }; + + let recent_block_hashes = { + let mut x = vec![]; + for _ in 0..config.cycle_length { + x.push(Hash256::zero()); + } + x + }; + + let active_state = ActiveState { + pending_attestations: vec![], + pending_specials: vec![], + recent_block_hashes, + randao_mix: Hash256::zero(), + }; + + + Ok(Self { + active_state, + crystallized_state, + config, + }) } } From 5d85c62f6e5fbf519b5a9a61f0b28bcf67b2ee27 Mon Sep 17 00:00:00 2001 From: Paul Hauner Date: Sat, 20 Oct 2018 21:42:07 +1100 Subject: [PATCH 08/24] Finish implementing genesis (without tests) --- beacon_chain/chain/src/lib.rs | 44 ++++++++++++------- beacon_chain/types/src/shard_and_committee.rs | 2 +- 2 files changed, 29 insertions(+), 17 deletions(-) diff --git a/beacon_chain/chain/src/lib.rs b/beacon_chain/chain/src/lib.rs index 27d605403..e4ec8ec8d 100644 --- a/beacon_chain/chain/src/lib.rs +++ b/beacon_chain/chain/src/lib.rs @@ -8,7 +8,6 @@ use types::{ CrosslinkRecord, CrystallizedState, Hash256, - ValidatorRecord, }; use validator_induction::{ ValidatorInductor, @@ -21,13 +20,24 @@ use validator_shuffling::{ 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 { - active_state: ActiveState, - crystallized_state: CrystallizedState, - config: ChainConfig, + /// 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, } 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) @@ -39,17 +49,11 @@ impl ChainHead { * Ignore any records which fail proof-of-possession or are invalid. */ let validators = { - let mut validators = vec![]; - let inductor = ValidatorInductor { - current_slot: 0, - shard_count: config.shard_count, - validators: &mut validators, - empty_validator_start: 0, - }; + let mut inductor = ValidatorInductor::new(0, config.shard_count, vec![]); for registration in initial_validator_entries { let _ = inductor.induct(®istration); }; - validators + inductor.to_vec() }; /* @@ -58,9 +62,10 @@ impl ChainHead { * Crystallizedstate stores two cycles, so we simply repeat the same assignment twice. */ let shard_and_committee_for_slots = { - let x = shard_and_committees_for_cycle(&vec![0; 32], &validators, 0, &config)?; - x.append(&mut x.clone()); - x + let mut a = shard_and_committees_for_cycle(&vec![0; 32], &validators, 0, &config)?; + let mut b = a.clone(); + a.append(&mut b); + a }; /* @@ -83,7 +88,7 @@ impl ChainHead { */ let crystallized_state = CrystallizedState { validator_set_change_slot: 0, - validators, + validators: validators.to_vec(), crosslinks, last_state_recalculation_slot: 0, last_finalized_slot: 0, @@ -97,6 +102,9 @@ impl ChainHead { 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 { @@ -105,6 +113,9 @@ impl ChainHead { x }; + /* + * Create an active state. + */ let active_state = ActiveState { pending_attestations: vec![], pending_specials: vec![], @@ -114,6 +125,7 @@ impl ChainHead { Ok(Self { + head_hash: Hash256::zero(), active_state, crystallized_state, config, diff --git a/beacon_chain/types/src/shard_and_committee.rs b/beacon_chain/types/src/shard_and_committee.rs index 3b2bcd0fd..6cc74429a 100644 --- a/beacon_chain/types/src/shard_and_committee.rs +++ b/beacon_chain/types/src/shard_and_committee.rs @@ -1,4 +1,4 @@ -#[derive(Clone,Debug)] +#[derive(Clone, Debug)] pub struct ShardAndCommittee { pub shard_id: u16, pub committee: Vec From d2c41977cc626b94412ce86673a323b1f616d244 Mon Sep 17 00:00:00 2001 From: Paul Hauner Date: Mon, 22 Oct 2018 02:18:35 +1100 Subject: [PATCH 09/24] Sketch out BeaconChain struct --- beacon_chain/chain/Cargo.toml | 1 + beacon_chain/chain/src/blocks.rs | 12 ++ beacon_chain/chain/src/genesis.rs | 210 ++++++++++++++++++++++++++++++ beacon_chain/chain/src/lib.rs | 152 ++++++--------------- 4 files changed, 260 insertions(+), 115 deletions(-) create mode 100644 beacon_chain/chain/src/blocks.rs create mode 100644 beacon_chain/chain/src/genesis.rs 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); } } From 42e774cb48a31b1aaddb8a97de9f4720a3e2a8a3 Mon Sep 17 00:00:00 2001 From: Paul Hauner Date: Mon, 22 Oct 2018 06:48:44 +1100 Subject: [PATCH 10/24] Finish genesis for BeaconChain --- beacon_chain/chain/src/blocks.rs | 12 ------ beacon_chain/chain/src/genesis.rs | 40 +++++++++---------- beacon_chain/chain/src/lib.rs | 27 ++++++++++--- beacon_chain/types/src/active_state.rs | 1 + beacon_chain/types/src/chain_config.rs | 6 +++ beacon_chain/types/src/crosslink_record.rs | 2 +- beacon_chain/types/src/crystallized_state.rs | 1 + beacon_chain/types/src/shard_and_committee.rs | 2 +- .../types/src/validator_registration.rs | 15 +++++++ 9 files changed, 65 insertions(+), 41 deletions(-) delete mode 100644 beacon_chain/chain/src/blocks.rs diff --git a/beacon_chain/chain/src/blocks.rs b/beacon_chain/chain/src/blocks.rs deleted file mode 100644 index ec43099b6..000000000 --- a/beacon_chain/chain/src/blocks.rs +++ /dev/null @@ -1,12 +0,0 @@ -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 index c503ec6ca..54ff7969f 100644 --- a/beacon_chain/chain/src/genesis.rs +++ b/beacon_chain/chain/src/genesis.rs @@ -1,6 +1,7 @@ use types::{ CrosslinkRecord, Hash256, + ValidatorRegistration, }; use super::{ ActiveState, @@ -9,10 +10,7 @@ use super::{ BeaconChainError, ChainConfig, }; -use validator_induction::{ - ValidatorInductor, - ValidatorRegistration, -}; +use validator_induction::ValidatorInductor; use validator_shuffling::{ shard_and_committees_for_cycle, ValidatorAssignmentError, @@ -30,9 +28,7 @@ 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) + pub fn genesis_states(config: &ChainConfig) -> Result<(ActiveState, CrystallizedState), ValidatorAssignmentError> { /* @@ -42,7 +38,7 @@ impl BeaconChain { */ let validators = { let mut inductor = ValidatorInductor::new(0, config.shard_count, vec![]); - for registration in initial_validator_entries { + for registration in &config.initial_validators { let _ = inductor.induct(®istration); }; inductor.to_vec() @@ -120,17 +116,19 @@ mod tests { extern crate bls; use super::*; - use self::bls::Keypair; + use self::bls::{ + create_proof_of_possession, + 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(); + let (act, cry) = BeaconChain::genesis_states(&config).unwrap(); assert_eq!(cry.validator_set_change_slot, 0); assert_eq!(cry.validators.len(), 0); @@ -170,41 +168,39 @@ mod tests { #[test] fn test_genesis_valid_validators() { - let config = ChainConfig::standard(); + let mut config = ChainConfig::standard(); let validator_count = 5; - let mut validators = vec![]; for _ in 0..validator_count { - validators.push(random_registration()); + config.initial_validators.push(random_registration()); } - let (_, cry) = BeaconChain::genesis_states(&validators, &config).unwrap(); + let (_, cry) = BeaconChain::genesis_states(&config).unwrap(); assert_eq!(cry.validators.len(), validator_count); } #[test] fn test_genesis_invalid_validators() { - let config = ChainConfig::standard(); + let mut config = ChainConfig::standard(); let good_validator_count = 5; - let mut all_validators = vec![]; for _ in 0..good_validator_count { - all_validators.push(random_registration()); + config.initial_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); + config.initial_validators.push(bad_v); let mut bad_v = random_registration(); bad_v.withdrawal_shard = config.shard_count + 1; - all_validators.push(bad_v); + config.initial_validators.push(bad_v); - let (_, cry) = BeaconChain::genesis_states(&all_validators, &config).unwrap(); + let (_, cry) = BeaconChain::genesis_states(&config).unwrap(); - assert!(all_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); } } diff --git a/beacon_chain/chain/src/lib.rs b/beacon_chain/chain/src/lib.rs index d5e7e2e86..808a9c641 100644 --- a/beacon_chain/chain/src/lib.rs +++ b/beacon_chain/chain/src/lib.rs @@ -2,7 +2,6 @@ extern crate types; extern crate validator_induction; extern crate validator_shuffling; -mod blocks; mod genesis; use std::collections::HashMap; @@ -13,6 +12,7 @@ use types::{ Hash256, }; +#[derive(Debug, PartialEq)] pub enum BeaconChainError { InvalidGenesis, DBError(String), @@ -24,15 +24,14 @@ pub struct BeaconChain { pub fork_latest_block_hashes: Vec, pub active_states: HashMap, pub crystallized_states: HashMap, + pub config: ChainConfig, } impl BeaconChain { pub fn new(config: ChainConfig) -> Result { - let initial_validators = vec![]; - let (active_state, crystallized_state) = BeaconChain::genesis_states( - &initial_validators, &config)?; + let (active_state, crystallized_state) = BeaconChain::genesis_states(&config)?; let canonical_latest_block_hash = Hash256::zero(); let fork_latest_block_hashes = vec![]; @@ -48,6 +47,7 @@ impl BeaconChain { fork_latest_block_hashes, active_states, crystallized_states, + config, }) } } @@ -56,9 +56,26 @@ impl BeaconChain { #[cfg(test)] mod tests { use super::*; + use types::ValidatorRegistration; #[test] fn test_new_chain() { - assert_eq!(2 + 2, 4); + let mut config = ChainConfig::standard(); + + for _ in 0..4 { + config.initial_validators.push(ValidatorRegistration::random()) + } + + let chain = BeaconChain::new(config.clone()).unwrap(); + let (act, cry) = BeaconChain::genesis_states(&config).unwrap(); + + assert_eq!(chain.last_finalized_slot, None); + assert_eq!(chain.canonical_latest_block_hash, Hash256::zero()); + + let stored_act = chain.active_states.get(&Hash256::zero()).unwrap(); + assert_eq!(act, *stored_act); + + let stored_cry = chain.crystallized_states.get(&Hash256::zero()).unwrap(); + assert_eq!(cry, *stored_cry); } } diff --git a/beacon_chain/types/src/active_state.rs b/beacon_chain/types/src/active_state.rs index 4b72910fa..b31f8447f 100644 --- a/beacon_chain/types/src/active_state.rs +++ b/beacon_chain/types/src/active_state.rs @@ -4,6 +4,7 @@ use super::{ SpecialRecord, }; +#[derive(Debug, PartialEq)] pub struct ActiveState { pub pending_attestations: Vec, pub pending_specials: Vec, diff --git a/beacon_chain/types/src/chain_config.rs b/beacon_chain/types/src/chain_config.rs index 4cdc91a6d..27382ee82 100644 --- a/beacon_chain/types/src/chain_config.rs +++ b/beacon_chain/types/src/chain_config.rs @@ -1,8 +1,12 @@ +use super::ValidatorRegistration; + +#[derive(Debug, Clone, PartialEq)] pub struct ChainConfig { pub cycle_length: u8, pub shard_count: u16, pub min_committee_size: u64, pub genesis_time: u64, + pub initial_validators: Vec, } /* @@ -17,6 +21,7 @@ impl ChainConfig { shard_count: 1024, min_committee_size: 128, genesis_time: GENESIS_TIME, // arbitrary + initial_validators: vec![], } } @@ -41,6 +46,7 @@ impl ChainConfig { shard_count: 2, min_committee_size: 2, genesis_time: GENESIS_TIME, // arbitrary + initial_validators: vec![], } } } diff --git a/beacon_chain/types/src/crosslink_record.rs b/beacon_chain/types/src/crosslink_record.rs index 2c7525fad..d28df4350 100644 --- a/beacon_chain/types/src/crosslink_record.rs +++ b/beacon_chain/types/src/crosslink_record.rs @@ -1,6 +1,6 @@ use super::Hash256; -#[derive(Clone)] +#[derive(Clone, Debug, PartialEq)] pub struct CrosslinkRecord { pub recently_changed: bool, pub slot: u64, diff --git a/beacon_chain/types/src/crystallized_state.rs b/beacon_chain/types/src/crystallized_state.rs index 21b4eb64e..71d31fe20 100644 --- a/beacon_chain/types/src/crystallized_state.rs +++ b/beacon_chain/types/src/crystallized_state.rs @@ -4,6 +4,7 @@ use super::shard_and_committee::ShardAndCommittee; use super::Hash256; +#[derive(Debug, PartialEq)] pub struct CrystallizedState { pub validator_set_change_slot: u64, pub validators: Vec, diff --git a/beacon_chain/types/src/shard_and_committee.rs b/beacon_chain/types/src/shard_and_committee.rs index 6cc74429a..10b9004f4 100644 --- a/beacon_chain/types/src/shard_and_committee.rs +++ b/beacon_chain/types/src/shard_and_committee.rs @@ -1,4 +1,4 @@ -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] pub struct ShardAndCommittee { pub shard_id: u16, pub committee: Vec diff --git a/beacon_chain/types/src/validator_registration.rs b/beacon_chain/types/src/validator_registration.rs index 384f2dc72..994d4d82d 100644 --- a/beacon_chain/types/src/validator_registration.rs +++ b/beacon_chain/types/src/validator_registration.rs @@ -1,4 +1,5 @@ use bls::{ + create_proof_of_possession, Keypair, PublicKey, Signature, @@ -18,3 +19,17 @@ pub struct ValidatorRegistration { pub randao_commitment: Hash256, pub proof_of_possession: Signature, } + +impl ValidatorRegistration { + pub fn random() -> Self { + let keypair = Keypair::random(); + + Self { + pubkey: keypair.pk.clone(), + withdrawal_shard: 0, + withdrawal_address: Address::random(), + randao_commitment: Hash256::random(), + proof_of_possession: create_proof_of_possession(&keypair), + } + } +} From 0536fb4a91ad79ffb1e8e93a4771e366a03cc9de Mon Sep 17 00:00:00 2001 From: Paul Hauner Date: Tue, 23 Oct 2018 12:28:07 +0200 Subject: [PATCH 11/24] Update ShardAndCommittee per new spec - `shard_id` -> `shard` --- beacon_chain/types/src/shard_and_committee.rs | 6 +++--- beacon_chain/validator_shuffling/src/shuffle.rs | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/beacon_chain/types/src/shard_and_committee.rs b/beacon_chain/types/src/shard_and_committee.rs index 10b9004f4..0f3483fe8 100644 --- a/beacon_chain/types/src/shard_and_committee.rs +++ b/beacon_chain/types/src/shard_and_committee.rs @@ -1,6 +1,6 @@ #[derive(Clone, Debug, PartialEq)] pub struct ShardAndCommittee { - pub shard_id: u16, + pub shard: u16, pub committee: Vec } @@ -9,7 +9,7 @@ impl ShardAndCommittee { /// committee is an empty vector. pub fn zero() -> Self { Self { - shard_id: 0, + shard: 0, committee: vec![], } } @@ -22,7 +22,7 @@ mod tests { #[test] fn test_shard_and_committee_zero() { let s = ShardAndCommittee::zero(); - assert_eq!(s.shard_id, 0); + assert_eq!(s.shard, 0); assert_eq!(s.committee.len(), 0); } } diff --git a/beacon_chain/validator_shuffling/src/shuffle.rs b/beacon_chain/validator_shuffling/src/shuffle.rs index 4768c1cb1..6e0e89c33 100644 --- a/beacon_chain/validator_shuffling/src/shuffle.rs +++ b/beacon_chain/validator_shuffling/src/shuffle.rs @@ -86,12 +86,12 @@ fn generate_cycle( let cycle = validator_indices.honey_badger_split(cycle_length) .enumerate() .map(|(i, slot_indices)| { - let shard_id_start = crosslinking_shard_start + i * committees_per_slot / slots_per_committee; + let shard_start = crosslinking_shard_start + i * committees_per_slot / slots_per_committee; slot_indices.honey_badger_split(committees_per_slot) .enumerate() .map(|(j, shard_indices)| { ShardAndCommittee{ - shard_id: ((shard_id_start + j) % shard_count) as u16, + shard: ((shard_start + j) % shard_count) as u16, committee: shard_indices.to_vec(), } }) @@ -141,8 +141,8 @@ mod tests { slot.iter() .enumerate() .for_each(|(i, sac)| { - println!("#{:?}\tshard_id={}\tcommittee.len()={}", - &i, &sac.shard_id, &sac.committee.len()) + println!("#{:?}\tshard={}\tcommittee.len()={}", + &i, &sac.shard, &sac.committee.len()) }) }); } @@ -167,7 +167,7 @@ mod tests { let mut flattened = vec![]; for slot in cycle.iter() { for sac in slot.iter() { - flattened.push(sac.shard_id as usize); + flattened.push(sac.shard as usize); } } flattened.dedup(); @@ -181,7 +181,7 @@ mod tests { for slot in cycle.iter() { let mut shards: Vec = vec![]; for sac in slot.iter() { - shards.push(sac.shard_id as usize); + shards.push(sac.shard as usize); } shards_in_slots.push(shards); } From 06c20857c8d0e3e6a2225581630ad3804d59a9cb Mon Sep 17 00:00:00 2001 From: Paul Hauner Date: Tue, 23 Oct 2018 12:33:43 +0200 Subject: [PATCH 12/24] Add slot duration to chain config --- beacon_chain/types/src/chain_config.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/beacon_chain/types/src/chain_config.rs b/beacon_chain/types/src/chain_config.rs index 27382ee82..bfb89f792 100644 --- a/beacon_chain/types/src/chain_config.rs +++ b/beacon_chain/types/src/chain_config.rs @@ -6,6 +6,7 @@ pub struct ChainConfig { pub shard_count: u16, pub min_committee_size: u64, pub genesis_time: u64, + pub slot_duration_millis: u64, pub initial_validators: Vec, } @@ -20,7 +21,8 @@ impl ChainConfig { cycle_length: 64, shard_count: 1024, min_committee_size: 128, - genesis_time: GENESIS_TIME, // arbitrary + genesis_time: GENESIS_TIME, + slot_duration_millis: 16 * 1000, initial_validators: vec![], } } @@ -46,6 +48,7 @@ impl ChainConfig { shard_count: 2, min_committee_size: 2, genesis_time: GENESIS_TIME, // arbitrary + slot_duration_millis: 16 * 1000, initial_validators: vec![], } } From a40b49d586115ecdd2a3ed7770b57b86fbb8534f Mon Sep 17 00:00:00 2001 From: Paul Hauner Date: Tue, 23 Oct 2018 13:15:08 +0200 Subject: [PATCH 13/24] Updates to block validation - Rename "parent_hashes" -> "recent_block_hashes" - Expect block has to be computed prior to function --- beacon_chain/validation/src/attestation_validation.rs | 4 ++-- beacon_chain/validation/src/block_validation.rs | 7 +++---- .../validation/tests/attestation_validation/helpers.rs | 2 +- beacon_chain/validation/tests/block_validation/helpers.rs | 5 +++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/beacon_chain/validation/src/attestation_validation.rs b/beacon_chain/validation/src/attestation_validation.rs index 53b45cad8..76bf61b8d 100644 --- a/beacon_chain/validation/src/attestation_validation.rs +++ b/beacon_chain/validation/src/attestation_validation.rs @@ -63,7 +63,7 @@ pub struct AttestationValidationContext /// The last justified slot as per the client's view of the canonical chain. pub last_justified_slot: u64, /// A vec of the hashes of the blocks preceeding the present slot. - pub parent_hashes: Arc>, + pub recent_block_hashes: Arc>, /// The store containing block information. pub block_store: Arc>, /// The store containing validator information. @@ -155,7 +155,7 @@ impl AttestationValidationContext self.cycle_length, self.block_slot, a.slot, - &self.parent_hashes, + &self.recent_block_hashes, &a.oblique_parent_hashes)?; /* diff --git a/beacon_chain/validation/src/block_validation.rs b/beacon_chain/validation/src/block_validation.rs index 8c2c28b1a..492f5f13a 100644 --- a/beacon_chain/validation/src/block_validation.rs +++ b/beacon_chain/validation/src/block_validation.rs @@ -80,7 +80,7 @@ pub struct BeaconBlockValidationContext /// The last finalized slot as per the client's view of the canonical chain. pub last_finalized_slot: u64, /// A vec of the hashes of the blocks preceeding the present slot. - pub parent_hashes: Arc>, + pub recent_block_hashes: Arc>, /// A map of slots to a block proposer validation index. pub proposer_map: Arc, /// A map of (slot, shard_id) to the attestation set of validation indices. @@ -109,7 +109,7 @@ impl BeaconBlockValidationContext /// Note: this function does not implement randao_reveal checking as it is not in the /// specification. #[allow(dead_code)] - pub fn validate_ssz_block(&self, b: &SszBeaconBlock) + pub fn validate_ssz_block(&self, block_hash: &Hash256, b: &SszBeaconBlock) -> Result<(BeaconBlockStatus, Option), SszBeaconBlockValidationError> where T: ClientDB + Sized { @@ -118,7 +118,6 @@ impl BeaconBlockValidationContext * If this block is already known, return immediately and indicate the the block is * known. Don't attempt to deserialize the block. */ - let block_hash = &b.block_hash(); if self.block_store.block_exists(&block_hash)? { return Ok((BeaconBlockStatus::KnownBlock, None)); } @@ -225,7 +224,7 @@ impl BeaconBlockValidationContext parent_block_slot, cycle_length: self.cycle_length, last_justified_slot: self.last_justified_slot, - parent_hashes: self.parent_hashes.clone(), + recent_block_hashes: self.recent_block_hashes.clone(), block_store: self.block_store.clone(), validator_store: self.validator_store.clone(), attester_map: self.attester_map.clone(), diff --git a/beacon_chain/validation/tests/attestation_validation/helpers.rs b/beacon_chain/validation/tests/attestation_validation/helpers.rs index 0bb0bb77d..eb686b612 100644 --- a/beacon_chain/validation/tests/attestation_validation/helpers.rs +++ b/beacon_chain/validation/tests/attestation_validation/helpers.rs @@ -208,7 +208,7 @@ pub fn setup_attestation_validation_test(shard_id: u16, attester_count: usize) parent_block_slot, cycle_length, last_justified_slot, - parent_hashes: parent_hashes.clone(), + recent_block_hashes: parent_hashes.clone(), block_store: stores.block.clone(), validator_store: stores.validator.clone(), attester_map: Arc::new(attester_map), diff --git a/beacon_chain/validation/tests/block_validation/helpers.rs b/beacon_chain/validation/tests/block_validation/helpers.rs index 3e367fa96..7fd0c364a 100644 --- a/beacon_chain/validation/tests/block_validation/helpers.rs +++ b/beacon_chain/validation/tests/block_validation/helpers.rs @@ -225,14 +225,15 @@ pub fn run_block_validation_scenario( last_justified_slot: params.validation_context_justified_slot, last_justified_block_hash: params.validation_context_justified_block_hash, last_finalized_slot: params.validation_context_finalized_slot, - parent_hashes: Arc::new(parent_hashes), + recent_block_hashes: Arc::new(parent_hashes), proposer_map: Arc::new(proposer_map), attester_map: Arc::new(attester_map), block_store: stores.block.clone(), validator_store: stores.validator.clone(), pow_store: stores.pow_chain.clone() }; - let validation_status = context.validate_ssz_block(&ssz_block); + let block_hash = Hash256::from(&ssz_block.block_hash()[..]); + let validation_status = context.validate_ssz_block(&block_hash, &ssz_block); /* * If validation returned a block, make sure it's the same block we supplied to it. * From a34266de0a44cd99435ca3fc706329aceb5b0199 Mon Sep 17 00:00:00 2001 From: Paul Hauner Date: Tue, 23 Oct 2018 13:16:26 +0200 Subject: [PATCH 14/24] Add unfinished progress on BeaconChain struct --- beacon_chain/chain/Cargo.toml | 3 + beacon_chain/chain/src/blocks.rs | 135 +++++++++++++++++++++++++ beacon_chain/chain/src/genesis.rs | 157 +++++++++++++++--------------- beacon_chain/chain/src/lib.rs | 78 +++++++++++++-- beacon_chain/chain/src/maps.rs | 45 +++++++++ beacon_chain/chain/src/stores.rs | 15 +++ 6 files changed, 343 insertions(+), 90 deletions(-) create mode 100644 beacon_chain/chain/src/blocks.rs create mode 100644 beacon_chain/chain/src/maps.rs create mode 100644 beacon_chain/chain/src/stores.rs diff --git a/beacon_chain/chain/Cargo.toml b/beacon_chain/chain/Cargo.toml index fd20d8100..dc4053cbb 100644 --- a/beacon_chain/chain/Cargo.toml +++ b/beacon_chain/chain/Cargo.toml @@ -5,6 +5,9 @@ authors = ["Paul Hauner "] [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" } diff --git a/beacon_chain/chain/src/blocks.rs b/beacon_chain/chain/src/blocks.rs new file mode 100644 index 000000000..2ff8a245d --- /dev/null +++ b/beacon_chain/chain/src/blocks.rs @@ -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 for BeaconChainBlockError { + fn from(e: BeaconBlockAtSlotError) -> BeaconChainBlockError { + BeaconChainBlockError::BlockSlotLookupError(e) + } +} + +impl From for BeaconChainBlockError { + fn from(e: SszBeaconBlockValidationError) -> BeaconChainBlockError { + BeaconChainBlockError::BlockValidationError(e) + } +} + +pub type BlockStatusTriple = (BeaconBlockStatus, Hash256, BeaconBlock); + + +impl BeaconChain + where T: ClientDB + Sized +{ + pub fn process_incoming_block(&self, ssz: &[u8], rx_time: u64) + -> Result + { + /* + * 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 for BeaconChainBlockError { + fn from(e: SszBeaconBlockError) -> BeaconChainBlockError { + BeaconChainBlockError::BadSsz(e) + } +} diff --git a/beacon_chain/chain/src/genesis.rs b/beacon_chain/chain/src/genesis.rs index 54ff7969f..646c18894 100644 --- a/beacon_chain/chain/src/genesis.rs +++ b/beacon_chain/chain/src/genesis.rs @@ -6,7 +6,6 @@ use types::{ use super::{ ActiveState, CrystallizedState, - BeaconChain, BeaconChainError, ChainConfig, }; @@ -24,89 +23,87 @@ impl From 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(®istration); - }; - 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(®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 - }; + /* + * 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); diff --git a/beacon_chain/chain/src/lib.rs b/beacon_chain/chain/src/lib.rs index 808a9c641..27ce34faa 100644 --- a/beacon_chain/chain/src/lib.rs +++ b/beacon_chain/chain/src/lib.rs @@ -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, +impl From for BeaconChainError { + fn from(e: AttesterAndProposerMapError) -> BeaconChainError { + BeaconChainError::UnableToGenerateMaps(e) + } +} + +pub struct BeaconChain { + /// 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, + /// A map where the value is an active state the the key is its hash. pub active_states: HashMap, + /// A map where the value is crystallized state the the key is its hash. pub crystallized_states: HashMap, + /// A map of crystallized state to a proposer and attester map. + pub attester_proposer_maps: HashMap, Arc)>, + /// A collection of database stores used by the chain. + pub store: BeaconChainStore, + /// The chain configuration. pub config: ChainConfig, } -impl BeaconChain { - pub fn new(config: ChainConfig) +impl BeaconChain + where T: ClientDB + Sized +{ + pub fn new(store: BeaconChainStore, config: ChainConfig) -> Result { - 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(); diff --git a/beacon_chain/chain/src/maps.rs b/beacon_chain/chain/src/maps.rs new file mode 100644 index 000000000..722cd73f8 --- /dev/null +++ b/beacon_chain/chain/src/maps.rs @@ -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)) +} diff --git a/beacon_chain/chain/src/stores.rs b/beacon_chain/chain/src/stores.rs new file mode 100644 index 000000000..e5bacdda8 --- /dev/null +++ b/beacon_chain/chain/src/stores.rs @@ -0,0 +1,15 @@ +use db::{ + ClientDB, +}; +use db::stores::{ + BeaconBlockStore, + PoWChainStore, + ValidatorStore, +}; +use std::sync::Arc; + +pub struct BeaconChainStore { + pub block: Arc>, + pub pow_chain: Arc>, + pub validator: Arc>, +} From da25a6619686b9cbcc397aa2626c79a93dd9001d Mon Sep 17 00:00:00 2001 From: Paul Hauner Date: Tue, 23 Oct 2018 23:42:01 +0200 Subject: [PATCH 15/24] Move active_validators into own crate --- Cargo.toml | 1 + beacon_chain/utils/active-validators/Cargo.toml | 7 +++++++ .../active-validators/src/lib.rs} | 13 ++++++++++--- beacon_chain/validator_shuffling/Cargo.toml | 1 + beacon_chain/validator_shuffling/src/lib.rs | 2 +- beacon_chain/validator_shuffling/src/shuffle.rs | 2 +- 6 files changed, 21 insertions(+), 5 deletions(-) create mode 100644 beacon_chain/utils/active-validators/Cargo.toml rename beacon_chain/{validator_shuffling/src/active_validator_indices.rs => utils/active-validators/src/lib.rs} (59%) diff --git a/Cargo.toml b/Cargo.toml index 57f0feb47..24a379b3c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,6 +34,7 @@ name = "lighthouse" members = [ "beacon_chain/chain", "beacon_chain/types", + "beacon_chain/utils/active-validators", "beacon_chain/utils/bls", "beacon_chain/utils/boolean-bitfield", "beacon_chain/utils/hashing", diff --git a/beacon_chain/utils/active-validators/Cargo.toml b/beacon_chain/utils/active-validators/Cargo.toml new file mode 100644 index 000000000..4729747d9 --- /dev/null +++ b/beacon_chain/utils/active-validators/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "active-validators" +version = "0.1.0" +authors = ["Paul Hauner "] + +[dependencies] +types = { path = "../../types" } diff --git a/beacon_chain/validator_shuffling/src/active_validator_indices.rs b/beacon_chain/utils/active-validators/src/lib.rs similarity index 59% rename from beacon_chain/validator_shuffling/src/active_validator_indices.rs rename to beacon_chain/utils/active-validators/src/lib.rs index 1a6b7b99a..ea7812193 100644 --- a/beacon_chain/validator_shuffling/src/active_validator_indices.rs +++ b/beacon_chain/utils/active-validators/src/lib.rs @@ -1,8 +1,14 @@ +extern crate types; + use types::{ ValidatorRecord, ValidatorStatus, }; +pub fn validator_is_active(v: &ValidatorRecord) -> bool { + v.status == ValidatorStatus::Active as u8 +} + /// Returns the indicies of each active validator in a given vec of validators. pub fn active_validator_indices(validators: &[ValidatorRecord]) -> Vec @@ -10,9 +16,10 @@ pub fn active_validator_indices(validators: &[ValidatorRecord]) validators.iter() .enumerate() .filter_map(|(i, validator)| { - match validator.status { - x if x == ValidatorStatus::Active as u8 => Some(i), - _ => None + if validator_is_active(&validator) { + Some(i) + } else { + None } }) .collect() diff --git a/beacon_chain/validator_shuffling/Cargo.toml b/beacon_chain/validator_shuffling/Cargo.toml index ba99841b8..269b5c557 100644 --- a/beacon_chain/validator_shuffling/Cargo.toml +++ b/beacon_chain/validator_shuffling/Cargo.toml @@ -4,6 +4,7 @@ version = "0.1.0" authors = ["Paul Hauner "] [dependencies] +active-validators = { path = "../utils/active-validators" } honey-badger-split = { path = "../utils/honey-badger-split" } types = { path = "../types" } vec_shuffle = { path = "../utils/vec_shuffle" } diff --git a/beacon_chain/validator_shuffling/src/lib.rs b/beacon_chain/validator_shuffling/src/lib.rs index ee6f5e8fb..a36cf0edd 100644 --- a/beacon_chain/validator_shuffling/src/lib.rs +++ b/beacon_chain/validator_shuffling/src/lib.rs @@ -1,8 +1,8 @@ +extern crate active_validators; extern crate honey_badger_split; extern crate vec_shuffle; extern crate types; -mod active_validator_indices; mod shuffle; pub use shuffle::{ diff --git a/beacon_chain/validator_shuffling/src/shuffle.rs b/beacon_chain/validator_shuffling/src/shuffle.rs index 6e0e89c33..7db13a4df 100644 --- a/beacon_chain/validator_shuffling/src/shuffle.rs +++ b/beacon_chain/validator_shuffling/src/shuffle.rs @@ -1,5 +1,6 @@ use std::cmp::min; +use active_validators::active_validator_indices; use honey_badger_split::SplitExt; use vec_shuffle::{ shuffle, @@ -11,7 +12,6 @@ use types::{ ChainConfig, }; -use super::active_validator_indices::active_validator_indices; type DelegatedCycle = Vec>; From cc28b7370dc5b5961f396657dfea6e870bb88d6e Mon Sep 17 00:00:00 2001 From: Paul Hauner Date: Tue, 23 Oct 2018 23:44:46 +0200 Subject: [PATCH 16/24] Extend chain config --- beacon_chain/types/src/chain_config.rs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/beacon_chain/types/src/chain_config.rs b/beacon_chain/types/src/chain_config.rs index bfb89f792..49128b49f 100644 --- a/beacon_chain/types/src/chain_config.rs +++ b/beacon_chain/types/src/chain_config.rs @@ -3,8 +3,10 @@ use super::ValidatorRegistration; #[derive(Debug, Clone, PartialEq)] pub struct ChainConfig { pub cycle_length: u8, + pub deposit_size_gwei: u64, pub shard_count: u16, pub min_committee_size: u64, + pub max_validator_churn_quotient: u64, pub genesis_time: u64, pub slot_duration_millis: u64, pub initial_validators: Vec, @@ -13,15 +15,17 @@ pub struct ChainConfig { /* * Presently this is just some arbitrary time in Sept 2018. */ -const GENESIS_TIME: u64 = 1_537_488_655; +const TEST_GENESIS_TIME: u64 = 1_537_488_655; impl ChainConfig { pub fn standard() -> Self { Self { cycle_length: 64, + deposit_size_gwei: 32 * 10^9, shard_count: 1024, min_committee_size: 128, - genesis_time: GENESIS_TIME, + max_validator_churn_quotient: 32, + genesis_time: TEST_GENESIS_TIME, slot_duration_millis: 16 * 1000, initial_validators: vec![], } @@ -45,9 +49,11 @@ impl ChainConfig { pub fn super_fast_tests() -> Self { Self { cycle_length: 2, + deposit_size_gwei: 32 * 10^9, shard_count: 2, min_committee_size: 2, - genesis_time: GENESIS_TIME, // arbitrary + max_validator_churn_quotient: 32, + genesis_time: TEST_GENESIS_TIME, // arbitrary slot_duration_millis: 16 * 1000, initial_validators: vec![], } From a170bfe4fccccdab289d8440f03d3f19910708b9 Mon Sep 17 00:00:00 2001 From: Paul Hauner Date: Tue, 23 Oct 2018 23:45:54 +0200 Subject: [PATCH 17/24] Add untested validator change code --- Cargo.toml | 1 + beacon_chain/validator_change/Cargo.toml | 10 ++ beacon_chain/validator_change/src/lib.rs | 148 +++++++++++++++++++++++ 3 files changed, 159 insertions(+) create mode 100644 beacon_chain/validator_change/Cargo.toml create mode 100644 beacon_chain/validator_change/src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index 24a379b3c..6c9b8886d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -43,6 +43,7 @@ members = [ "beacon_chain/utils/ssz_helpers", "beacon_chain/utils/vec_shuffle", "beacon_chain/validation", + "beacon_chain/validator_change", "beacon_chain/validator_induction", "beacon_chain/validator_shuffling", "lighthouse/db", diff --git a/beacon_chain/validator_change/Cargo.toml b/beacon_chain/validator_change/Cargo.toml new file mode 100644 index 000000000..88e78c9f8 --- /dev/null +++ b/beacon_chain/validator_change/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "validator_change" +version = "0.1.0" +authors = ["Paul Hauner "] + +[dependencies] +active-validators = { path = "../utils/active-validators" } +bytes = "0.4.10" +hashing = { path = "../utils/hashing" } +types = { path = "../types" } diff --git a/beacon_chain/validator_change/src/lib.rs b/beacon_chain/validator_change/src/lib.rs new file mode 100644 index 000000000..d913a4be0 --- /dev/null +++ b/beacon_chain/validator_change/src/lib.rs @@ -0,0 +1,148 @@ +extern crate active_validators; +extern crate bytes; +extern crate hashing; +extern crate types; + +use active_validators::validator_is_active; +use bytes::{ + BytesMut, + BufMut, +}; +use hashing::canonical_hash; +use std::cmp::max; +use types::{ + Hash256, + ValidatorRecord, + ValidatorStatus, +}; + +pub enum UpdateValidatorSetError { + ArithmeticOverflow, +} + +const VALIDATOR_FLAG_ENTRY: u8 = 0; +const VALIDATOR_FLAG_EXIT: u8 = 1; + +pub fn update_validator_set( + validators: &mut Vec, + hash_chain: Hash256, + present_slot: u64, + deposit_size_gwei: u64, + max_validator_churn_quotient: u64) + -> Result<(), UpdateValidatorSetError> +{ + /* + * Total balance of all active validators. + * + * Return an error if an overflow occurs. + */ + let total_balance = { + let mut bal: u64 = 0; + for v in validators.iter() { + if validator_is_active(&v) { + bal = bal.checked_add(v.balance) + .ok_or(UpdateValidatorSetError::ArithmeticOverflow)?; + } + } + bal + }; + + /* + * Note: this is not the maximum allowable change, it can actually be higher. + */ + let max_allowable_change = { + let double_deposit_size = deposit_size_gwei.checked_mul(2) + .ok_or(UpdateValidatorSetError::ArithmeticOverflow)?; + max(double_deposit_size, total_balance / max_validator_churn_quotient) + }; + + let mut hasher = ValidatorChangeHashChain { + bytes: hash_chain.to_vec(), + }; + let mut total_changed: u64 = 0; + for (i, v) in validators.iter_mut().enumerate() { + match v.status { + /* + * Validator is pending activiation. + */ + x if x == ValidatorStatus::PendingActivation as u8 => { + let new_total_changed = total_changed.checked_add(deposit_size_gwei) + .ok_or(UpdateValidatorSetError::ArithmeticOverflow)?; + /* + * If entering this validator would not exceed the max balance delta, + * activate the validator. + */ + if new_total_changed <= max_allowable_change { + v.status = ValidatorStatus::Active as u8; + hasher.extend(i, &v.pubkey.as_bytes(), VALIDATOR_FLAG_ENTRY); + total_changed = new_total_changed; + } else { + // Entering the validator would exceed the balance delta. + break; + } + } + /* + * Validator is pending exit. + */ + x if x == ValidatorStatus::PendingExit as u8 => { + let new_total_changed = total_changed.checked_add(v.balance) + .ok_or(UpdateValidatorSetError::ArithmeticOverflow)?; + /* + * If exiting this validator would not exceed the max balance delta, + * exit the validator + */ + if new_total_changed <= max_allowable_change { + v.status = ValidatorStatus::PendingWithdraw as u8; + v.exit_slot = present_slot; + hasher.extend(i, &v.pubkey.as_bytes(), VALIDATOR_FLAG_EXIT); + total_changed = new_total_changed; + } else { + // Exiting the validator would exceed the balance delta. + break; + } + } + _ => () + }; + if total_changed >= max_allowable_change { + break; + } + } + Ok(()) +} + +pub struct ValidatorChangeHashChain { + bytes: Vec, +} + +impl ValidatorChangeHashChain { + pub fn extend(&mut self, index: usize, pubkey: &Vec, flag: u8) + { + let mut message = self.bytes.clone(); + message.append(&mut serialize_validator_change_record(index, pubkey, flag)); + self.bytes = canonical_hash(&message); + } +} + +fn serialize_validator_change_record(index: usize, pubkey: &Vec, flag: u8) + -> Vec +{ + let mut buf = BytesMut::with_capacity(68); + buf.put_u8(flag); + let index_bytes = { + let mut buf = BytesMut::with_capacity(8); + buf.put_u64_be(index as u64); + buf.take()[8 - 3..8].to_vec() + }; + buf.put(index_bytes); + buf.put(pubkey); + buf.take().to_vec() +} + + +#[cfg(test)] +mod tests { + #[test] + fn it_works() { + assert_eq!(2 + 2, 4); + } +} From 896810fb9fed1ba891f62b5e9dc63d02c1a4ea67 Mon Sep 17 00:00:00 2001 From: Paul Hauner Date: Wed, 24 Oct 2018 10:13:47 +0200 Subject: [PATCH 18/24] Add `status` to validator induction. Allows for inducting validators with a specific status. --- .../validator_induction/src/inductor.rs | 38 +++++++++++++------ 1 file changed, 27 insertions(+), 11 deletions(-) diff --git a/beacon_chain/validator_induction/src/inductor.rs b/beacon_chain/validator_induction/src/inductor.rs index e025d0d43..c4344bf67 100644 --- a/beacon_chain/validator_induction/src/inductor.rs +++ b/beacon_chain/validator_induction/src/inductor.rs @@ -40,15 +40,15 @@ impl ValidatorInductor { /// /// Returns an error if the registration is invalid, otherwise returns the index of the /// validator in `CrystallizedState.validators`. - pub fn induct(&mut self, rego: &ValidatorRegistration) + pub fn induct(&mut self, rego: &ValidatorRegistration, status: ValidatorStatus) -> Result { - let v = self.process_registration(rego)?; + let v = self.process_registration(rego, status)?; Ok(self.add_validator(v)) } /// Verify a `ValidatorRegistration` and return a `ValidatorRecord` if valid. - fn process_registration(&self, r: &ValidatorRegistration) + fn process_registration(&self, r: &ValidatorRegistration, status: ValidatorStatus) -> Result { /* @@ -72,7 +72,7 @@ impl ValidatorInductor { randao_commitment: r.randao_commitment, randao_last_change: self.current_slot, balance: DEPOSIT_GWEI, - status: ValidatorStatus::PendingActivation as u8, + status: status as u8, exit_slot: 0, }) } @@ -166,7 +166,7 @@ mod tests { let r = get_registration(); let mut inductor = ValidatorInductor::new(0, 1024, validators); - let result = inductor.induct(&r); + let result = inductor.induct(&r, ValidatorStatus::PendingActivation); let validators = inductor.to_vec(); assert_eq!(result.unwrap(), 0); @@ -174,6 +174,22 @@ mod tests { assert_eq!(validators.len(), 1); } + #[test] + fn test_validator_inductor_status() { + let validators = vec![]; + + let r = get_registration(); + + let mut inductor = ValidatorInductor::new(0, 1024, validators); + let _ = inductor.induct(&r, ValidatorStatus::PendingActivation); + let _ = inductor.induct(&r, ValidatorStatus::Active); + let validators = inductor.to_vec(); + + assert!(validators[0].status == ValidatorStatus::PendingActivation as u8); + assert!(validators[1].status == ValidatorStatus::Active as u8); + assert_eq!(validators.len(), 2); + } + #[test] fn test_validator_inductor_valid_all_active_validators() { let mut validators = vec![]; @@ -186,7 +202,7 @@ mod tests { let r = get_registration(); let mut inductor = ValidatorInductor::new(0, 1024, validators); - let result = inductor.induct(&r); + let result = inductor.induct(&r, ValidatorStatus::PendingActivation); let validators = inductor.to_vec(); assert_eq!(result.unwrap(), 5); @@ -209,7 +225,7 @@ mod tests { let r = get_registration(); let mut inductor = ValidatorInductor::new(0, 1024, validators); - let result = inductor.induct(&r); + let result = inductor.induct(&r, ValidatorStatus::PendingActivation); let validators = inductor.to_vec(); assert_eq!(result.unwrap(), 1); @@ -231,7 +247,7 @@ mod tests { */ let r = get_registration(); let mut inductor = ValidatorInductor::new(0, 1024, validators); - let result = inductor.induct(&r); + let result = inductor.induct(&r, ValidatorStatus::PendingActivation); let validators = inductor.to_vec(); assert_eq!(result.unwrap(), 0); assert!(registration_equals_record(&r, &validators[0])); @@ -241,7 +257,7 @@ mod tests { */ let r_two = get_registration(); let mut inductor = ValidatorInductor::new(0, 1024, validators); - let result = inductor.induct(&r_two); + let result = inductor.induct(&r_two, ValidatorStatus::PendingActivation); let validators = inductor.to_vec(); assert_eq!(result.unwrap(), 1); assert!(registration_equals_record(&r_two, &validators[1])); @@ -256,7 +272,7 @@ mod tests { r.withdrawal_shard = 1025; let mut inductor = ValidatorInductor::new(0, 1024, validators); - let result = inductor.induct(&r); + let result = inductor.induct(&r, ValidatorStatus::PendingActivation); let validators = inductor.to_vec(); assert_eq!(result, Err(ValidatorInductionError::InvalidShard)); @@ -272,7 +288,7 @@ mod tests { r.proof_of_possession = get_proof_of_possession(&kp); let mut inductor = ValidatorInductor::new(0, 1024, validators); - let result = inductor.induct(&r); + let result = inductor.induct(&r, ValidatorStatus::PendingActivation); let validators = inductor.to_vec(); assert_eq!(result, Err(ValidatorInductionError::InvaidProofOfPossession)); From 58c6465f44dbc4e9670b3e4dc989e04b1c1d7197 Mon Sep 17 00:00:00 2001 From: Paul Hauner Date: Wed, 24 Oct 2018 10:15:59 +0200 Subject: [PATCH 19/24] Ensure genesis inducts all validators as active --- beacon_chain/chain/src/genesis.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/beacon_chain/chain/src/genesis.rs b/beacon_chain/chain/src/genesis.rs index 646c18894..f3426729c 100644 --- a/beacon_chain/chain/src/genesis.rs +++ b/beacon_chain/chain/src/genesis.rs @@ -2,6 +2,7 @@ use types::{ CrosslinkRecord, Hash256, ValidatorRegistration, + ValidatorStatus, }; use super::{ ActiveState, @@ -37,7 +38,7 @@ pub fn genesis_states(config: &ChainConfig) let validators = { let mut inductor = ValidatorInductor::new(0, config.shard_count, vec![]); for registration in &config.initial_validators { - let _ = inductor.induct(®istration); + let _ = inductor.induct(®istration, ValidatorStatus::Active); }; inductor.to_vec() }; From 68e4c47cd381186e09da42bd1d00a296d3462701 Mon Sep 17 00:00:00 2001 From: Paul Hauner Date: Wed, 24 Oct 2018 13:12:55 +0200 Subject: [PATCH 20/24] Add slot-clock --- Cargo.toml | 1 + beacon_chain/utils/slot-clock/Cargo.toml | 6 ++ beacon_chain/utils/slot-clock/src/lib.rs | 71 ++++++++++++++++++++++++ 3 files changed, 78 insertions(+) create mode 100644 beacon_chain/utils/slot-clock/Cargo.toml create mode 100644 beacon_chain/utils/slot-clock/src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index 6c9b8886d..b0872b8e0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,6 +39,7 @@ members = [ "beacon_chain/utils/boolean-bitfield", "beacon_chain/utils/hashing", "beacon_chain/utils/honey-badger-split", + "beacon_chain/utils/slot-clock", "beacon_chain/utils/ssz", "beacon_chain/utils/ssz_helpers", "beacon_chain/utils/vec_shuffle", diff --git a/beacon_chain/utils/slot-clock/Cargo.toml b/beacon_chain/utils/slot-clock/Cargo.toml new file mode 100644 index 000000000..c10fb6bd9 --- /dev/null +++ b/beacon_chain/utils/slot-clock/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "slot-clock" +version = "0.1.0" +authors = ["Paul Hauner "] + +[dependencies] diff --git a/beacon_chain/utils/slot-clock/src/lib.rs b/beacon_chain/utils/slot-clock/src/lib.rs new file mode 100644 index 000000000..1d801fe10 --- /dev/null +++ b/beacon_chain/utils/slot-clock/src/lib.rs @@ -0,0 +1,71 @@ +use std::time::{ + Duration, + SystemTime, + SystemTimeError, +}; + +pub fn slot_now(genesis_seconds: u64, slot_duration_seconds: u64) + -> Result, SystemTimeError> +{ + let sys_time = SystemTime::now(); + let duration_since_epoch = sys_time.duration_since(SystemTime::UNIX_EPOCH)?; + let duration_since_genesis = duration_since_epoch + .checked_sub(Duration::from_secs(genesis_seconds)); + match duration_since_genesis { + None => Ok(None), + Some(d) => Ok(slot_from_duration(slot_duration_seconds, d)) + } +} + +fn slot_from_duration(slot_duration_seconds: u64, duration: Duration) + -> Option +{ + duration.as_secs().checked_div(slot_duration_seconds) +} + + +#[cfg(test)] +mod tests { + use super::*; + + /* + * Note: these tests are using actual system times and could fail if they are executed on a + * very slow machine. + */ + #[test] + fn test_slot_now() { + let s_time = 100; + + let now = SystemTime::now(); + let since_epoch = now.duration_since(SystemTime::UNIX_EPOCH).unwrap(); + + let genesis = since_epoch.as_secs() - s_time * 89; + assert_eq!(slot_now(genesis, s_time).unwrap(), Some(89)); + + let genesis = since_epoch.as_secs(); + assert_eq!(slot_now(genesis, s_time).unwrap(), Some(0)); + + let genesis = since_epoch.as_secs() - s_time * 42 - 5; + assert_eq!(slot_now(genesis, s_time).unwrap(), Some(42)); + } + + #[test] + fn test_slot_from_duration() { + let s_time = 100; + + assert_eq!(slot_from_duration(s_time, Duration::from_secs(0)), Some(0)); + assert_eq!(slot_from_duration(s_time, Duration::from_secs(10)), Some(0)); + assert_eq!(slot_from_duration(s_time, Duration::from_secs(100)), Some(1)); + assert_eq!(slot_from_duration(s_time, Duration::from_secs(101)), Some(1)); + assert_eq!(slot_from_duration(s_time, Duration::from_secs(1000)), Some(10)); + } + + #[test] + fn test_slot_from_duration_slot_time_zero() { + let s_time = 0; + + assert_eq!(slot_from_duration(s_time, Duration::from_secs(0)), None); + assert_eq!(slot_from_duration(s_time, Duration::from_secs(10)), None); + assert_eq!(slot_from_duration(s_time, Duration::from_secs(1000)), None); + } +} From e48e423b30ac719f2e901127008399fd269b0104 Mon Sep 17 00:00:00 2001 From: Paul Hauner Date: Wed, 24 Oct 2018 14:48:20 +0200 Subject: [PATCH 21/24] Update and add tests for chain maps --- beacon_chain/chain/src/lib.rs | 2 +- beacon_chain/chain/src/maps.rs | 99 ++++++++++++++++++++++++++++++---- 2 files changed, 90 insertions(+), 11 deletions(-) diff --git a/beacon_chain/chain/src/lib.rs b/beacon_chain/chain/src/lib.rs index 27ce34faa..baf315e17 100644 --- a/beacon_chain/chain/src/lib.rs +++ b/beacon_chain/chain/src/lib.rs @@ -78,7 +78,7 @@ impl BeaconChain let mut attester_proposer_maps = HashMap::new(); let (attester_map, proposer_map) = generate_attester_and_proposer_maps( - &crystallized_state, 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); diff --git a/beacon_chain/chain/src/maps.rs b/beacon_chain/chain/src/maps.rs index 722cd73f8..dfed49e7a 100644 --- a/beacon_chain/chain/src/maps.rs +++ b/beacon_chain/chain/src/maps.rs @@ -1,7 +1,7 @@ use types::{ AttesterMap, - CrystallizedState, ProposerMap, + ShardAndCommittee, }; #[derive(Debug, PartialEq)] @@ -13,25 +13,24 @@ pub enum AttesterAndProposerMapError { /// 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) +pub fn generate_attester_and_proposer_maps( + shard_and_committee_for_slots: &Vec>, + 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() { + for (i, slot) in 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 first_committee = &slot.get(0) + .ok_or(AttesterAndProposerMapError::NoShardAndCommitteeForSlot)? + .committee; let proposer_index = (slot_number as usize).checked_rem(first_committee.len()) .ok_or(AttesterAndProposerMapError::NoAvailableProposer)?; - proposer_map.insert(slot_number, proposer_index); + proposer_map.insert(slot_number, first_committee[proposer_index]); /* * Loop through the shards and extend the attester map. @@ -43,3 +42,83 @@ pub fn generate_attester_and_proposer_maps(cry_state: &CrystallizedState, start_ }; Ok((attester_map, proposer_map)) } + +#[cfg(test)] +mod tests { + use super::*; + + fn sac_generator(shard_count: u16, + slot_count: usize, + sac_per_slot: usize, + committee_size: usize) + -> Vec> + { + let mut shard = 0; + let mut validator = 0; + let mut cycle = vec![]; + + for _ in 0..slot_count { + let mut slot: Vec = vec![]; + for _ in 0..sac_per_slot { + let mut sac = ShardAndCommittee { + shard: shard % shard_count, + committee: vec![], + }; + for _ in 0..committee_size { + sac.committee.push(validator); + validator += 1; + } + slot.push(sac); + shard += 1; + } + cycle.push(slot); + } + cycle + } + + #[test] + 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)); + } + + #[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)); + } + + #[test] + fn test_attester_proposer_maps_scenario_a() { + let sac = sac_generator(4, 4, 1, 1); + let (a, p) = generate_attester_and_proposer_maps(&sac, 0).unwrap(); + + assert_eq!(*p.get(&0).unwrap(), 0); + assert_eq!(*p.get(&1).unwrap(), 1); + assert_eq!(*p.get(&2).unwrap(), 2); + assert_eq!(*p.get(&3).unwrap(), 3); + + assert_eq!(*a.get(&(0, 0)).unwrap(), vec![0]); + assert_eq!(*a.get(&(1, 1)).unwrap(), vec![1]); + assert_eq!(*a.get(&(2, 2)).unwrap(), vec![2]); + assert_eq!(*a.get(&(3, 3)).unwrap(), vec![3]); + } + + #[test] + fn test_attester_proposer_maps_scenario_b() { + let sac = sac_generator(4, 4, 1, 4); + let (a, p) = generate_attester_and_proposer_maps(&sac, 0).unwrap(); + + assert_eq!(*p.get(&0).unwrap(), 0); + assert_eq!(*p.get(&1).unwrap(), 5); + assert_eq!(*p.get(&2).unwrap(), 10); + assert_eq!(*p.get(&3).unwrap(), 15); + + assert_eq!(*a.get(&(0, 0)).unwrap(), vec![0, 1, 2, 3]); + assert_eq!(*a.get(&(1, 1)).unwrap(), vec![4, 5, 6, 7]); + assert_eq!(*a.get(&(2, 2)).unwrap(), vec![8, 9, 10, 11]); + assert_eq!(*a.get(&(3, 3)).unwrap(), vec![12, 13, 14, 15]); + } +} From b17a15a378f096acbe6de5bb28836d78a6be1151 Mon Sep 17 00:00:00 2001 From: Paul Hauner Date: Wed, 24 Oct 2018 14:48:35 +0200 Subject: [PATCH 22/24] Rename block preprocessing --- beacon_chain/chain/src/{blocks.rs => block_preprocessing.rs} | 5 +---- beacon_chain/chain/src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 5 deletions(-) rename beacon_chain/chain/src/{blocks.rs => block_preprocessing.rs} (97%) diff --git a/beacon_chain/chain/src/blocks.rs b/beacon_chain/chain/src/block_preprocessing.rs similarity index 97% rename from beacon_chain/chain/src/blocks.rs rename to beacon_chain/chain/src/block_preprocessing.rs index 2ff8a245d..263782d30 100644 --- a/beacon_chain/chain/src/blocks.rs +++ b/beacon_chain/chain/src/block_preprocessing.rs @@ -54,11 +54,10 @@ impl From for BeaconChainBlockError { pub type BlockStatusTriple = (BeaconBlockStatus, Hash256, BeaconBlock); - impl BeaconChain where T: ClientDB + Sized { - pub fn process_incoming_block(&self, ssz: &[u8], rx_time: u64) + fn block_preprocessing(&self, ssz: &[u8], present_slot: u64) -> Result { /* @@ -102,8 +101,6 @@ impl BeaconChain 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. */ diff --git a/beacon_chain/chain/src/lib.rs b/beacon_chain/chain/src/lib.rs index baf315e17..ac62a4068 100644 --- a/beacon_chain/chain/src/lib.rs +++ b/beacon_chain/chain/src/lib.rs @@ -4,7 +4,7 @@ extern crate validator_induction; extern crate validator_shuffling; mod stores; -mod blocks; +mod block_preprocessing; mod maps; mod genesis; From 98076d6439102791b27968c9a152edaf698af179 Mon Sep 17 00:00:00 2001 From: Paul Hauner Date: Wed, 24 Oct 2018 15:22:39 +0200 Subject: [PATCH 23/24] Add test for utils/active-validators --- .../utils/active-validators/src/lib.rs | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/beacon_chain/utils/active-validators/src/lib.rs b/beacon_chain/utils/active-validators/src/lib.rs index ea7812193..0d79ba0e9 100644 --- a/beacon_chain/utils/active-validators/src/lib.rs +++ b/beacon_chain/utils/active-validators/src/lib.rs @@ -24,3 +24,45 @@ pub fn active_validator_indices(validators: &[ValidatorRecord]) }) .collect() } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_active_validator() { + let mut validators = vec![]; + + let (mut v, _) = ValidatorRecord::zero_with_thread_rand_keypair(); + v.status = ValidatorStatus::Active as u8; + assert!(validator_is_active(&v)); + validators.push(v); + + let (mut v, _) = ValidatorRecord::zero_with_thread_rand_keypair(); + v.status = ValidatorStatus::PendingActivation as u8; + assert!(!validator_is_active(&v)); + validators.push(v); + + let (mut v, _) = ValidatorRecord::zero_with_thread_rand_keypair(); + v.status = ValidatorStatus::PendingExit as u8; + assert!(!validator_is_active(&v)); + validators.push(v); + + let (mut v, _) = ValidatorRecord::zero_with_thread_rand_keypair(); + v.status = ValidatorStatus::PendingWithdraw as u8; + assert!(!validator_is_active(&v)); + validators.push(v); + + let (mut v, _) = ValidatorRecord::zero_with_thread_rand_keypair(); + v.status = ValidatorStatus::Withdrawn as u8; + assert!(!validator_is_active(&v)); + validators.push(v); + + let (mut v, _) = ValidatorRecord::zero_with_thread_rand_keypair(); + v.status = ValidatorStatus::Penalized as u8; + assert!(!validator_is_active(&v)); + validators.push(v); + + assert_eq!(active_validator_indices(&validators), vec![0]); + } +} From b2495cbcff91e4e6879c7201883d06d249b59560 Mon Sep 17 00:00:00 2001 From: Paul Hauner Date: Wed, 24 Oct 2018 15:40:09 +0200 Subject: [PATCH 24/24] Fix clippy lints --- beacon_chain/types/src/chain_config.rs | 6 +++--- beacon_chain/types/src/special_record.rs | 2 +- beacon_chain/utils/ssz_helpers/src/ssz_beacon_block.rs | 1 + 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/beacon_chain/types/src/chain_config.rs b/beacon_chain/types/src/chain_config.rs index 49128b49f..7437bed47 100644 --- a/beacon_chain/types/src/chain_config.rs +++ b/beacon_chain/types/src/chain_config.rs @@ -21,7 +21,7 @@ impl ChainConfig { pub fn standard() -> Self { Self { cycle_length: 64, - deposit_size_gwei: 32 * 10^9, + deposit_size_gwei: 32 * (10^9), shard_count: 1024, min_committee_size: 128, max_validator_churn_quotient: 32, @@ -36,7 +36,7 @@ impl ChainConfig { // shard_count / cycle_length > 0 otherwise validator delegation // will fail. - if self.shard_count / self.cycle_length as u16 == 0 { + if self.shard_count / u16::from(self.cycle_length) == 0 { return false; } @@ -49,7 +49,7 @@ impl ChainConfig { pub fn super_fast_tests() -> Self { Self { cycle_length: 2, - deposit_size_gwei: 32 * 10^9, + deposit_size_gwei: 32 * (10^9), shard_count: 2, min_committee_size: 2, max_validator_churn_quotient: 32, diff --git a/beacon_chain/types/src/special_record.rs b/beacon_chain/types/src/special_record.rs index 9189728df..186e449b1 100644 --- a/beacon_chain/types/src/special_record.rs +++ b/beacon_chain/types/src/special_record.rs @@ -49,7 +49,7 @@ impl SpecialRecord { /// Match `self.kind` to a `SpecialRecordKind`. /// /// Returns `None` if `self.kind` is an unknown value. - fn resolve_kind(&self) -> Option { + pub fn resolve_kind(&self) -> Option { match self.kind { x if x == SpecialRecordKind::Logout as u8 => Some(SpecialRecordKind::Logout), diff --git a/beacon_chain/utils/ssz_helpers/src/ssz_beacon_block.rs b/beacon_chain/utils/ssz_helpers/src/ssz_beacon_block.rs index 3066f5679..d466d3abc 100644 --- a/beacon_chain/utils/ssz_helpers/src/ssz_beacon_block.rs +++ b/beacon_chain/utils/ssz_helpers/src/ssz_beacon_block.rs @@ -127,6 +127,7 @@ impl<'a> SszBeaconBlock<'a> { } pub fn len(&self) -> usize { self.ssz.len() } + pub fn is_empty(&self) -> bool { self.ssz.is_empty() } /// Return the canonical hash for this block. pub fn block_hash(&self) -> Vec {