diff --git a/Cargo.toml b/Cargo.toml index ee19d12772..6884109a99 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,6 +35,7 @@ name = "lighthouse" members = [ "beacon_chain/attestation_validation", "beacon_chain/chain", + "beacon_chain/genesis", "beacon_chain/naive_fork_choice", "beacon_chain/spec", "beacon_chain/types", diff --git a/beacon_chain/attestation_validation/src/justified_slot.rs b/beacon_chain/attestation_validation/src/justified_slot.rs index c27dfe8f27..ea71f2616f 100644 --- a/beacon_chain/attestation_validation/src/justified_slot.rs +++ b/beacon_chain/attestation_validation/src/justified_slot.rs @@ -12,8 +12,9 @@ use super::{Error, Invalid, Outcome}; pub fn validate_attestation_justified_slot( data: &AttestationData, state: &BeaconState, + epoch_length: u64, ) -> Result { - let permissable_justified_slot = if data.slot >= state.latest_state_recalculation_slot { + let permissable_justified_slot = if data.slot >= state.slot - (state.slot % epoch_length) { state.justified_slot } else { state.previous_justified_slot diff --git a/beacon_chain/attestation_validation/src/shard_block.rs b/beacon_chain/attestation_validation/src/shard_block.rs index 7fb48ff627..e1d9487de8 100644 --- a/beacon_chain/attestation_validation/src/shard_block.rs +++ b/beacon_chain/attestation_validation/src/shard_block.rs @@ -19,7 +19,7 @@ where match state.latest_crosslinks.get(data.shard as usize) { None => reject!(Invalid::UnknownShard), Some(crosslink) => { - let local_shard_block_hash = crosslink.shard_block_hash; + let local_shard_block_hash = crosslink.shard_block_root; let shard_block_hash_is_permissable = { (local_shard_block_hash == data.latest_crosslink_hash) || (local_shard_block_hash == data.shard_block_hash) diff --git a/beacon_chain/chain/Cargo.toml b/beacon_chain/chain/Cargo.toml index 31af587004..3302d15d47 100644 --- a/beacon_chain/chain/Cargo.toml +++ b/beacon_chain/chain/Cargo.toml @@ -7,7 +7,9 @@ edition = "2018" [dependencies] bls = { path = "../utils/bls" } db = { path = "../../lighthouse/db" } +genesis = { path = "../genesis" } naive_fork_choice = { path = "../naive_fork_choice" } +spec = { path = "../spec" } ssz = { path = "../utils/ssz" } types = { path = "../types" } validator_induction = { path = "../validator_induction" } diff --git a/beacon_chain/chain/src/genesis.rs b/beacon_chain/chain/src/genesis.rs deleted file mode 100644 index e1b39cd238..0000000000 --- a/beacon_chain/chain/src/genesis.rs +++ /dev/null @@ -1,157 +0,0 @@ -use super::{ActiveState, ChainConfig, CrystallizedState}; -use types::ValidatorStatus; -use validator_shuffling::{shard_and_committees_for_cycle, ValidatorAssignmentError}; - -#[derive(Debug, PartialEq)] -pub enum Error { - ValidationAssignmentError(ValidatorAssignmentError), - NotImplemented, -} - -impl From for Error { - fn from(e: ValidatorAssignmentError) -> Error { - Error::ValidationAssignmentError(e) - } -} - -/// Initialize a new ChainHead with genesis parameters. -/// -/// Used when syncing a chain from scratch. -pub fn genesis_states(config: &ChainConfig) -> Result<(ActiveState, CrystallizedState), Error> { - /* - * Parse the ValidatorRegistrations into ValidatorRecords and induct them. - * - * Ignore any records which fail proof-of-possession or are invalid. - */ - /* - TODO: refactor this - let validators = { - let mut inductor = ValidatorInductor::new(0, config.shard_count, vec![]); - for registration in &config.initial_validators { - let _ = inductor.induct(®istration, ValidatorStatus::Active); - } - inductor.to_vec() - }; - */ - let validators = 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 - }; - - // TODO: implement genesis for `BeaconState` - // https://github.com/sigp/lighthouse/issues/99 - - Err(Error::NotImplemented) -} - -#[cfg(test)] -mod tests { - extern crate bls; - extern crate validator_induction; - - // TODO: implement genesis for `BeaconState` - // https://github.com/sigp/lighthouse/issues/99 - // - /* - use self::bls::{create_proof_of_possession, Keypair}; - use super::*; - use types::{Address, Hash256, ValidatorRegistration}; - - #[test] - fn test_genesis_no_validators() { - let config = ChainConfig::standard(); - let (act, cry) = genesis_states(&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 mut config = ChainConfig::standard(); - let validator_count = 5; - - for _ in 0..validator_count { - config.initial_validators.push(random_registration()); - } - - let (_, cry) = genesis_states(&config).unwrap(); - - assert_eq!(cry.validators.len(), validator_count); - } - - #[test] - fn test_genesis_invalid_validators() { - let mut config = ChainConfig::standard(); - let good_validator_count = 5; - - for _ in 0..good_validator_count { - 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); - config.initial_validators.push(bad_v); - - let mut bad_v = random_registration(); - bad_v.withdrawal_shard = config.shard_count + 1; - config.initial_validators.push(bad_v); - - let (_, cry) = genesis_states(&config).unwrap(); - - assert!( - config.initial_validators.len() != good_validator_count, - "test is invalid" - ); - assert_eq!(cry.validators.len(), good_validator_count); - } - */ -} diff --git a/beacon_chain/chain/src/lib.rs b/beacon_chain/chain/src/lib.rs index 8eb076d478..b1499ac1ac 100644 --- a/beacon_chain/chain/src/lib.rs +++ b/beacon_chain/chain/src/lib.rs @@ -1,22 +1,24 @@ extern crate db; extern crate naive_fork_choice; +extern crate genesis; +extern crate spec; extern crate ssz; extern crate types; extern crate validator_induction; extern crate validator_shuffling; mod block_processing; -mod genesis; mod maps; mod stores; use db::ClientDB; -use crate::genesis::{genesis_states, Error as GenesisError}; use crate::maps::{generate_attester_and_proposer_maps, AttesterAndProposerMapError}; +use crate::stores::BeaconChainStore; +use genesis::{genesis_beacon_state, GenesisError}; +use spec::ChainSpec; use std::collections::HashMap; use std::sync::Arc; -use crate::stores::BeaconChainStore; -use types::{ActiveState, AttesterMap, ChainConfig, CrystallizedState, Hash256, ProposerMap}; +use types::{AttesterMap, BeaconState, Hash256, ProposerMap}; #[derive(Debug, PartialEq)] pub enum BeaconChainError { @@ -34,43 +36,46 @@ pub struct BeaconChain { pub head_block_hashes: Vec, /// The index of the canonical block in `head_block_hashes`. pub canonical_head_block_hash: usize, - /// 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, + /// An in-memory map of root hash to beacon state. + pub beacon_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, + pub spec: ChainSpec, } impl BeaconChain where T: ClientDB + Sized, { - pub fn new(store: BeaconChainStore, config: ChainConfig) -> Result { - if config.initial_validators.is_empty() { + pub fn new(store: BeaconChainStore, spec: ChainSpec) -> Result { + if spec.initial_validators.is_empty() { return Err(BeaconChainError::InsufficientValidators); } - let (active_state, crystallized_state) = genesis_states(&config)?; + /* + * Generate and process the genesis state. + */ + let genesis_state = genesis_beacon_state(&spec)?; + let mut beacon_states = HashMap::new(); + beacon_states.insert(genesis_state.canonical_root(), genesis_state.clone()); + // TODO: implement genesis block + // https://github.com/sigp/lighthouse/issues/105 let canonical_latest_block_hash = Hash256::zero(); + let head_block_hashes = vec![canonical_latest_block_hash]; let canonical_head_block_hash = 0; - 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.shard_and_committee_for_slots, + &genesis_state.shard_committees_at_slots, 0, )?; - active_states.insert(canonical_latest_block_hash, active_state); - crystallized_states.insert(canonical_latest_block_hash, crystallized_state); attester_proposer_maps.insert( canonical_latest_block_hash, (Arc::new(attester_map), Arc::new(proposer_map)), @@ -80,11 +85,10 @@ where last_finalized_slot: 0, head_block_hashes, canonical_head_block_hash, - active_states, - crystallized_states, + beacon_states, attester_proposer_maps, store, - config, + spec, }) } diff --git a/beacon_chain/chain/src/maps.rs b/beacon_chain/chain/src/maps.rs index 9188531f28..cecd87c8d8 100644 --- a/beacon_chain/chain/src/maps.rs +++ b/beacon_chain/chain/src/maps.rs @@ -1,8 +1,8 @@ -use types::{AttesterMap, ProposerMap, ShardAndCommittee}; +use types::{AttesterMap, ProposerMap, ShardCommittee}; #[derive(Debug, PartialEq)] pub enum AttesterAndProposerMapError { - NoShardAndCommitteeForSlot, + NoShardCommitteeForSlot, NoAvailableProposer, } @@ -10,7 +10,7 @@ pub enum AttesterAndProposerMapError { /// /// The attester map is used to optimise the lookup of a committee. pub fn generate_attester_and_proposer_maps( - shard_and_committee_for_slots: &Vec>, + shard_and_committee_for_slots: &Vec>, start_slot: u64, ) -> Result<(AttesterMap, ProposerMap), AttesterAndProposerMapError> { let mut attester_map = AttesterMap::new(); @@ -22,7 +22,7 @@ pub fn generate_attester_and_proposer_maps( let slot_number = (i as u64).saturating_add(start_slot); let first_committee = &slot .get(0) - .ok_or(AttesterAndProposerMapError::NoShardAndCommitteeForSlot)? + .ok_or(AttesterAndProposerMapError::NoShardCommitteeForSlot)? .committee; let proposer_index = (slot_number as usize) .checked_rem(first_committee.len()) @@ -49,15 +49,15 @@ mod tests { slot_count: usize, sac_per_slot: usize, committee_size: usize, - ) -> Vec> { + ) -> Vec> { let mut shard = 0; let mut validator = 0; let mut cycle = vec![]; for _ in 0..slot_count { - let mut slot: Vec = vec![]; + let mut slot: Vec = vec![]; for _ in 0..sac_per_slot { - let mut sac = ShardAndCommittee { + let mut sac = ShardCommittee { shard: shard % shard_count, committee: vec![], }; @@ -79,7 +79,7 @@ mod tests { let result = generate_attester_and_proposer_maps(&sac, 0); assert_eq!( result, - Err(AttesterAndProposerMapError::NoShardAndCommitteeForSlot) + Err(AttesterAndProposerMapError::NoShardCommitteeForSlot) ); } diff --git a/beacon_chain/chain/src/transition.rs b/beacon_chain/chain/src/transition.rs new file mode 100644 index 0000000000..df8803b27f --- /dev/null +++ b/beacon_chain/chain/src/transition.rs @@ -0,0 +1,29 @@ +use super::BeaconChain; +use db::ClientDB; +use state_transition::{extend_active_state, StateTransitionError}; +use types::{ActiveState, BeaconBlock, CrystallizedState, Hash256}; + +impl BeaconChain +where + T: ClientDB + Sized, +{ + pub(crate) fn transition_states( + &self, + act_state: &ActiveState, + cry_state: &CrystallizedState, + block: &BeaconBlock, + block_hash: &Hash256, + ) -> Result<(ActiveState, Option), StateTransitionError> { + let state_recalc_distance = block + .slot + .checked_sub(cry_state.last_state_recalculation_slot) + .ok_or(StateTransitionError::BlockSlotBeforeRecalcSlot)?; + + if state_recalc_distance >= u64::from(self.spec.epoch_length) { + panic!("Not implemented!") + } else { + let new_act_state = extend_active_state(act_state, block, block_hash)?; + Ok((new_act_state, None)) + } + } +} diff --git a/beacon_chain/genesis/Cargo.toml b/beacon_chain/genesis/Cargo.toml new file mode 100644 index 0000000000..4993339797 --- /dev/null +++ b/beacon_chain/genesis/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "genesis" +version = "0.1.0" +authors = ["Paul Hauner "] +edition = "2018" + +[dependencies] +bls = { path = "../utils/bls" } +spec = { path = "../spec" } +ssz = { path = "../utils/ssz" } +types = { path = "../types" } +validator_induction = { path = "../validator_induction" } +validator_shuffling = { path = "../validator_shuffling" } diff --git a/beacon_chain/genesis/src/beacon_block.rs b/beacon_chain/genesis/src/beacon_block.rs new file mode 100644 index 0000000000..120903564a --- /dev/null +++ b/beacon_chain/genesis/src/beacon_block.rs @@ -0,0 +1,49 @@ +use bls::{Signature, BLS_AGG_SIG_BYTE_SIZE}; +use spec::ChainSpec; +use ssz::{encode::encode_length, Decodable, LENGTH_BYTES}; +use types::{BeaconBlock, BeaconBlockBody}; + +/// Generate a genesis BeaconBlock. +pub fn genesis_beacon_block(spec: &ChainSpec) -> BeaconBlock { + BeaconBlock { + slot: spec.initial_slot_number, + parent_root: spec.zero_hash, + state_root: spec.zero_hash, + randao_reveal: spec.zero_hash, + candidate_pow_receipt_root: spec.zero_hash, + signature: genesis_signature(), + body: BeaconBlockBody { + proposer_slashings: vec![], + casper_slashings: vec![], + attestations: vec![], + deposits: vec![], + exits: vec![], + }, + } +} + +fn genesis_signature() -> Signature { + let mut bytes = encode_length(BLS_AGG_SIG_BYTE_SIZE, LENGTH_BYTES); + bytes.append(&mut vec![0; BLS_AGG_SIG_BYTE_SIZE]); + let (signature, _) = match Signature::ssz_decode(&bytes, 0) { + Ok(sig) => sig, + Err(_) => unreachable!(), + }; + signature +} + +#[cfg(test)] +mod tests { + use super::*; + + // TODO: enhance these tests. + // https://github.com/sigp/lighthouse/issues/117 + + #[test] + fn test_genesis() { + let spec = ChainSpec::foundation(); + + // This only checks that the function runs without panic. + genesis_beacon_block(&spec); + } +} diff --git a/beacon_chain/genesis/src/beacon_state.rs b/beacon_chain/genesis/src/beacon_state.rs new file mode 100644 index 0000000000..fda94552fd --- /dev/null +++ b/beacon_chain/genesis/src/beacon_state.rs @@ -0,0 +1,135 @@ +use spec::ChainSpec; +use types::{BeaconState, CrosslinkRecord, ForkData, ValidatorStatus}; +use validator_induction::ValidatorInductor; +use validator_shuffling::{shard_and_committees_for_cycle, ValidatorAssignmentError}; + +#[derive(Debug, PartialEq)] +pub enum Error { + NoValidators, + ValidationAssignmentError(ValidatorAssignmentError), + NotImplemented, +} + +pub fn genesis_beacon_state(spec: &ChainSpec) -> 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, spec.shard_count, vec![]); + for registration in &spec.initial_validators { + let _ = inductor.induct(®istration, ValidatorStatus::Active); + } + 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(&[0; 32], &validators, 0, &spec)?; + let mut b = a.clone(); + a.append(&mut b); + a + }; + + let initial_crosslink = CrosslinkRecord { + slot: spec.initial_slot_number, + shard_block_root: spec.zero_hash, + }; + + Ok(BeaconState { + /* + * Misc + */ + slot: spec.initial_slot_number, + genesis_time: spec.genesis_time, + fork_data: ForkData { + pre_fork_version: spec.initial_fork_version, + post_fork_version: spec.initial_fork_version, + fork_slot: spec.initial_slot_number, + }, + /* + * Validator registry + */ + validator_registry: validators, + validator_registry_latest_change_slot: spec.initial_slot_number, + validator_registry_exit_count: 0, + validator_registry_delta_chain_tip: spec.zero_hash, + /* + * Randomness and committees + */ + randao_mix: spec.zero_hash, + next_seed: spec.zero_hash, + shard_committees_at_slots: vec![], + persistent_committees: vec![], + persistent_committee_reassignments: vec![], + /* + * Finality + */ + previous_justified_slot: spec.initial_slot_number, + justified_slot: spec.initial_slot_number, + justification_bitfield: 0, + finalized_slot: spec.initial_slot_number, + /* + * Recent state + */ + latest_crosslinks: vec![initial_crosslink; spec.shard_count as usize], + latest_block_roots: vec![spec.zero_hash; spec.epoch_length as usize], + latest_penalized_exit_balances: vec![], + latest_attestations: vec![], + /* + * PoW receipt root + */ + processed_pow_receipt_root: spec.processed_pow_receipt_root, + candidate_pow_receipt_roots: vec![], + }) +} + +impl From for Error { + fn from(e: ValidatorAssignmentError) -> Error { + Error::ValidationAssignmentError(e) + } +} + +#[cfg(test)] +mod tests { + extern crate bls; + extern crate validator_induction; + + use self::bls::{create_proof_of_possession, Keypair}; + use super::*; + + // TODO: enhance these tests. + // https://github.com/sigp/lighthouse/issues/117 + + #[test] + fn test_genesis() { + let spec = ChainSpec::foundation(); + + let state = genesis_beacon_state(&spec).unwrap(); + + assert_eq!( + state.validator_registry.len(), + spec.initial_validators.len() + ); + } + + #[test] + fn test_genesis_bad_validator() { + let mut spec = ChainSpec::foundation(); + + let random_kp = Keypair::random(); + spec.initial_validators[4].proof_of_possession = create_proof_of_possession(&random_kp); + + let state = genesis_beacon_state(&spec).unwrap(); + + assert_eq!( + state.validator_registry.len(), + spec.initial_validators.len() - 1 + ); + } +} diff --git a/beacon_chain/genesis/src/lib.rs b/beacon_chain/genesis/src/lib.rs new file mode 100644 index 0000000000..e590fa8be8 --- /dev/null +++ b/beacon_chain/genesis/src/lib.rs @@ -0,0 +1,10 @@ +extern crate spec; +extern crate types; +extern crate validator_induction; +extern crate validator_shuffling; + +mod beacon_state; +mod beacon_block; + +pub use crate::beacon_block::genesis_beacon_block; +pub use crate::beacon_state::{genesis_beacon_state, Error as GenesisError}; diff --git a/beacon_chain/spec/Cargo.toml b/beacon_chain/spec/Cargo.toml index 2da687fa33..5c535ab414 100644 --- a/beacon_chain/spec/Cargo.toml +++ b/beacon_chain/spec/Cargo.toml @@ -5,4 +5,5 @@ authors = ["Paul Hauner "] edition = "2018" [dependencies] +bls = { path = "../utils/bls" } types = { path = "../types" } diff --git a/beacon_chain/spec/src/foundation.rs b/beacon_chain/spec/src/foundation.rs index 5bf25e1e7a..b80187281a 100644 --- a/beacon_chain/spec/src/foundation.rs +++ b/beacon_chain/spec/src/foundation.rs @@ -1,9 +1,13 @@ use super::ChainSpec; +use bls::{create_proof_of_possession, Keypair, PublicKey, SecretKey}; -use types::{Address, Hash256}; +use types::{Address, Hash256, ValidatorRegistration}; impl ChainSpec { /// Returns a `ChainSpec` compatible with the specification from Ethereum Foundation. + /// + /// Of course, the actual foundation specs are unknown at this point so these are just a rough + /// estimate. pub fn foundation() -> Self { Self { /* @@ -56,6 +60,59 @@ impl ChainSpec { max_attestations: 128, max_deposits: 16, max_exits: 16, + /* + * Intialization parameters + */ + initial_validators: initial_validators_for_testing(), + genesis_time: 1544672897, + processed_pow_receipt_root: Hash256::from("pow_root".as_bytes()), } } } + +/// Generate a set of validator registrations to use with testing until the real chain starts. +fn initial_validators_for_testing() -> Vec { + // Some dummy private keys to start with. + let key_strings = vec![ + "jzjxxgjajfjrmgodszzsgqccmhnyvetcuxobhtynojtpdtbj", + "gpeehcjudxdijzhjgirfuhahmnjutlchjmoffxmimbdejakd", + "ntrrdwwebodokuwaclhoqreqyodngoyhurvesghjfxeswoaj", + "cibmzkqrzdgdlrvqaxinwpvyhcgjkeysrsjkqtkcxvznsvth", + "erqrfuahdwprsstkawggounxmihzhrvbhchcyiwtaypqcedr", + ]; + + let mut initial_validators = Vec::with_capacity(key_strings.len()); + for key_string in key_strings { + let keypair = { + let secret_key = match SecretKey::from_bytes(&key_string.as_bytes()) { + Ok(key) => key, + Err(_) => unreachable!(), // Keys are static and should not fail. + }; + let public_key = PublicKey::from_secret_key(&secret_key); + Keypair { + sk: secret_key, + pk: public_key, + } + }; + let validator_registration = ValidatorRegistration { + pubkey: keypair.pk.clone(), + withdrawal_shard: 0, + withdrawal_address: Address::random(), + randao_commitment: Hash256::random(), + proof_of_possession: create_proof_of_possession(&keypair), + }; + initial_validators.push(validator_registration); + } + + initial_validators +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_foundation_spec_can_be_constructed() { + let _ = ChainSpec::foundation(); + } +} diff --git a/beacon_chain/spec/src/lib.rs b/beacon_chain/spec/src/lib.rs index a15575115b..3a05f26967 100644 --- a/beacon_chain/spec/src/lib.rs +++ b/beacon_chain/spec/src/lib.rs @@ -1,9 +1,11 @@ +extern crate bls; extern crate types; mod foundation; -use types::{Address, Hash256}; +use types::{Address, Hash256, ValidatorRegistration}; +#[derive(PartialEq, Debug)] pub struct ChainSpec { /* * Misc @@ -55,4 +57,10 @@ pub struct ChainSpec { pub max_attestations: u64, pub max_deposits: u64, pub max_exits: u64, + /* + * Intialization parameters + */ + pub initial_validators: Vec, + pub genesis_time: u64, + pub processed_pow_receipt_root: Hash256, } diff --git a/beacon_chain/types/src/beacon_block.rs b/beacon_chain/types/src/beacon_block.rs index df8f939e33..a0c4262330 100644 --- a/beacon_chain/types/src/beacon_block.rs +++ b/beacon_chain/types/src/beacon_block.rs @@ -1,7 +1,7 @@ use super::ssz::{Decodable, DecodeError, Encodable, SszStream}; use super::{BeaconBlockBody, Hash256}; use crate::test_utils::TestRandom; -use bls::AggregateSignature; +use bls::Signature; use rand::RngCore; #[derive(Debug, PartialEq, Clone)] @@ -11,7 +11,7 @@ pub struct BeaconBlock { pub state_root: Hash256, pub randao_reveal: Hash256, pub candidate_pow_receipt_root: Hash256, - pub signature: AggregateSignature, + pub signature: Signature, pub body: BeaconBlockBody, } diff --git a/beacon_chain/types/src/beacon_state.rs b/beacon_chain/types/src/beacon_state.rs index c90595da3c..4bf1bca550 100644 --- a/beacon_chain/types/src/beacon_state.rs +++ b/beacon_chain/types/src/beacon_state.rs @@ -2,37 +2,53 @@ use super::candidate_pow_receipt_root_record::CandidatePoWReceiptRootRecord; use super::crosslink_record::CrosslinkRecord; use super::fork_data::ForkData; use super::pending_attestation_record::PendingAttestationRecord; -use super::shard_and_committee::ShardAndCommittee; +use super::shard_committee::ShardCommittee; use super::shard_reassignment_record::ShardReassignmentRecord; use super::validator_record::ValidatorRecord; use super::Hash256; -#[derive(Debug, PartialEq, Default)] +#[derive(Debug, PartialEq, Clone)] pub struct BeaconState { - pub slot: u64, + // Misc + pub slot: u64, pub genesis_time: u64, pub fork_data: ForkData, + + // Validator registry pub validator_registry: Vec, pub validator_balances: Vec, pub validator_registry_latest_change_slot: u64, pub validator_registry_exit_count: u64, pub validator_registry_delta_chain_tip: Hash256, - pub latest_randao_mixes: Vec, - pub latest_vdf_outputs: Vec, - pub shard_committees_at_slots: Vec>, + + // Randomness and committees + pub randao_mix: Hash256, + pub next_seed: Hash256, + pub shard_committees_at_slots: Vec>, pub persistent_committees: Vec>, pub persistent_committee_reassignments: Vec, + + // Finality pub previous_justified_slot: u64, pub justified_slot: u64, pub justification_bitfield: u64, pub finalized_slot: u64, + + // Recent state pub latest_crosslinks: Vec, - // TODO: remove this, it's no longer in the spec - pub latest_state_recalculation_slot: u64, pub latest_block_roots: Vec, pub latest_penalized_exit_balances: Vec, pub latest_attestations: Vec, - pub batched_block_roots: Vec, + + // PoW receipt root pub processed_pow_receipt_root: Hash256, pub candidate_pow_receipt_roots: Vec, } + +impl BeaconState { + pub fn canonical_root(&self) -> Hash256 { + // TODO: implement tree hashing. + // https://github.com/sigp/lighthouse/issues/70 + Hash256::zero() + } +} diff --git a/beacon_chain/types/src/candidate_pow_receipt_root_record.rs b/beacon_chain/types/src/candidate_pow_receipt_root_record.rs index 9a1e3e5c82..a06e5e7943 100644 --- a/beacon_chain/types/src/candidate_pow_receipt_root_record.rs +++ b/beacon_chain/types/src/candidate_pow_receipt_root_record.rs @@ -1,6 +1,6 @@ use super::Hash256; -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub struct CandidatePoWReceiptRootRecord { pub candidate_pow_receipt_root: Hash256, pub votes: u64, diff --git a/beacon_chain/types/src/crosslink_record.rs b/beacon_chain/types/src/crosslink_record.rs index 3f7ac2f1af..6a70daade4 100644 --- a/beacon_chain/types/src/crosslink_record.rs +++ b/beacon_chain/types/src/crosslink_record.rs @@ -3,7 +3,7 @@ use super::Hash256; #[derive(Clone, Debug, PartialEq)] pub struct CrosslinkRecord { pub slot: u64, - pub shard_block_hash: Hash256, + pub shard_block_root: Hash256, } impl CrosslinkRecord { @@ -11,7 +11,7 @@ impl CrosslinkRecord { pub fn zero() -> Self { Self { slot: 0, - shard_block_hash: Hash256::zero(), + shard_block_root: Hash256::zero(), } } } diff --git a/beacon_chain/types/src/crystallized_state.rs b/beacon_chain/types/src/crystallized_state.rs index ff81202cf4..454454a17c 100644 --- a/beacon_chain/types/src/crystallized_state.rs +++ b/beacon_chain/types/src/crystallized_state.rs @@ -1,5 +1,5 @@ use super::crosslink_record::CrosslinkRecord; -use super::shard_and_committee::ShardAndCommittee; +use super::shard_committee::ShardCommittee; use super::validator_record::ValidatorRecord; use super::Hash256; @@ -12,7 +12,7 @@ pub struct CrystallizedState { pub last_finalized_slot: u64, pub last_justified_slot: u64, pub justified_streak: u64, - pub shard_and_committee_for_slots: Vec>, + pub shard_and_committee_for_slots: Vec>, pub deposits_penalized_in_period: Vec, pub validator_set_delta_hash_chain: Hash256, pub pre_fork_version: u32, diff --git a/beacon_chain/types/src/lib.rs b/beacon_chain/types/src/lib.rs index f2904b3c53..46befaf3e4 100644 --- a/beacon_chain/types/src/lib.rs +++ b/beacon_chain/types/src/lib.rs @@ -24,7 +24,7 @@ pub mod fork_data; pub mod pending_attestation_record; pub mod proposal_signed_data; pub mod proposer_slashing; -pub mod shard_and_committee; +pub mod shard_committee; pub mod shard_reassignment_record; pub mod special_record; pub mod slashable_vote_data; @@ -52,7 +52,7 @@ pub use crate::pending_attestation_record::PendingAttestationRecord; pub use crate::proposal_signed_data::ProposalSignedData; pub use crate::proposer_slashing::ProposerSlashing; pub use crate::slashable_vote_data::SlashableVoteData; -pub use crate::shard_and_committee::ShardAndCommittee; +pub use crate::shard_committee::ShardCommittee; pub use crate::special_record::{SpecialRecord, SpecialRecordKind}; pub use crate::validator_record::{ValidatorRecord, ValidatorStatus}; diff --git a/beacon_chain/types/src/shard_and_committee.rs b/beacon_chain/types/src/shard_committee.rs similarity index 83% rename from beacon_chain/types/src/shard_and_committee.rs rename to beacon_chain/types/src/shard_committee.rs index 44c2e57ffb..75208670f7 100644 --- a/beacon_chain/types/src/shard_and_committee.rs +++ b/beacon_chain/types/src/shard_committee.rs @@ -1,10 +1,10 @@ #[derive(Clone, Debug, PartialEq)] -pub struct ShardAndCommittee { +pub struct ShardCommittee { pub shard: u16, pub committee: Vec, } -impl ShardAndCommittee { +impl ShardCommittee { /// Returns a new instance where the `shard_id` is zero and the /// committee is an empty vector. pub fn zero() -> Self { @@ -21,7 +21,7 @@ mod tests { #[test] fn test_shard_and_committee_zero() { - let s = ShardAndCommittee::zero(); + let s = ShardCommittee::zero(); assert_eq!(s.shard, 0); assert_eq!(s.committee.len(), 0); } diff --git a/beacon_chain/types/src/shard_reassignment_record.rs b/beacon_chain/types/src/shard_reassignment_record.rs index 2f2c8a8d9f..c9a17741c7 100644 --- a/beacon_chain/types/src/shard_reassignment_record.rs +++ b/beacon_chain/types/src/shard_reassignment_record.rs @@ -1,4 +1,4 @@ -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub struct ShardReassignmentRecord { pub validator_index: u64, pub shard: u64, diff --git a/beacon_chain/types/src/validator_registration.rs b/beacon_chain/types/src/validator_registration.rs new file mode 100644 index 0000000000..11f6d11c50 --- /dev/null +++ b/beacon_chain/types/src/validator_registration.rs @@ -0,0 +1,26 @@ +use super::{Address, Hash256}; +use bls::{create_proof_of_possession, Keypair, PublicKey, Signature}; + +/// The information gathered from the PoW chain validator registration function. +#[derive(Debug, Clone, PartialEq)] +pub struct ValidatorRegistration { + pub pubkey: PublicKey, + pub withdrawal_shard: u64, + pub withdrawal_address: Address, + 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), + } + } +} diff --git a/beacon_chain/validator_shuffling/Cargo.toml b/beacon_chain/validator_shuffling/Cargo.toml index ae2babf1a4..99a97c5ec0 100644 --- a/beacon_chain/validator_shuffling/Cargo.toml +++ b/beacon_chain/validator_shuffling/Cargo.toml @@ -6,5 +6,6 @@ edition = "2018" [dependencies] honey-badger-split = { path = "../utils/honey-badger-split" } +spec = { path = "../spec" } 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 b4d3204375..ee59ffeccc 100644 --- a/beacon_chain/validator_shuffling/src/lib.rs +++ b/beacon_chain/validator_shuffling/src/lib.rs @@ -1,4 +1,5 @@ extern crate honey_badger_split; +extern crate spec; extern crate types; extern crate vec_shuffle; diff --git a/beacon_chain/validator_shuffling/src/shuffle.rs b/beacon_chain/validator_shuffling/src/shuffle.rs index 577668a8f8..ee7389949f 100644 --- a/beacon_chain/validator_shuffling/src/shuffle.rs +++ b/beacon_chain/validator_shuffling/src/shuffle.rs @@ -1,10 +1,11 @@ use std::cmp::min; use honey_badger_split::SplitExt; -use types::{ChainConfig, ShardAndCommittee, ValidatorRecord, ValidatorStatus}; +use spec::ChainSpec; +use types::{ShardCommittee, ValidatorRecord, ValidatorStatus}; use vec_shuffle::{shuffle, ShuffleErr}; -type DelegatedCycle = Vec>; +type DelegatedCycle = Vec>; #[derive(Debug, PartialEq)] pub enum ValidatorAssignmentError { @@ -20,10 +21,10 @@ pub fn shard_and_committees_for_cycle( seed: &[u8], validators: &[ValidatorRecord], crosslinking_shard_start: u16, - config: &ChainConfig, + spec: &ChainSpec, ) -> Result { let shuffled_validator_indices = { - let mut validator_indices = validators + let validator_indices = validators .iter() .enumerate() .filter_map(|(i, validator)| { @@ -36,15 +37,15 @@ pub fn shard_and_committees_for_cycle( .collect(); shuffle(seed, validator_indices)? }; - let shard_indices: Vec = (0_usize..config.shard_count as usize).into_iter().collect(); + let shard_indices: Vec = (0_usize..spec.shard_count as usize).into_iter().collect(); let crosslinking_shard_start = crosslinking_shard_start as usize; - let cycle_length = config.cycle_length as usize; - let min_committee_size = config.min_committee_size as usize; + let epoch_length = spec.epoch_length as usize; + let min_committee_size = spec.target_committee_size as usize; generate_cycle( &shuffled_validator_indices, &shard_indices, crosslinking_shard_start, - cycle_length, + epoch_length, min_committee_size, ) } @@ -54,29 +55,29 @@ fn generate_cycle( validator_indices: &[usize], shard_indices: &[usize], crosslinking_shard_start: usize, - cycle_length: usize, + epoch_length: usize, min_committee_size: usize, ) -> Result { let validator_count = validator_indices.len(); let shard_count = shard_indices.len(); - if shard_count / cycle_length == 0 { + if shard_count / epoch_length == 0 { return Err(ValidatorAssignmentError::TooFewShards); } let (committees_per_slot, slots_per_committee) = { - if validator_count >= cycle_length * min_committee_size { + if validator_count >= epoch_length * min_committee_size { let committees_per_slot = min( - validator_count / cycle_length / (min_committee_size * 2) + 1, - shard_count / cycle_length, + validator_count / epoch_length / (min_committee_size * 2) + 1, + shard_count / epoch_length, ); let slots_per_committee = 1; (committees_per_slot, slots_per_committee) } else { let committees_per_slot = 1; let mut slots_per_committee = 1; - while (validator_count * slots_per_committee < cycle_length * min_committee_size) - & (slots_per_committee < cycle_length) + while (validator_count * slots_per_committee < epoch_length * min_committee_size) + & (slots_per_committee < epoch_length) { slots_per_committee *= 2; } @@ -85,7 +86,7 @@ fn generate_cycle( }; let cycle = validator_indices - .honey_badger_split(cycle_length) + .honey_badger_split(epoch_length) .enumerate() .map(|(i, slot_indices)| { let shard_start = @@ -93,7 +94,7 @@ fn generate_cycle( slot_indices .honey_badger_split(committees_per_slot) .enumerate() - .map(|(j, shard_indices)| ShardAndCommittee { + .map(|(j, shard_indices)| ShardCommittee { shard: ((shard_start + j) % shard_count) as u16, committee: shard_indices.to_vec(), }) @@ -119,7 +120,7 @@ mod tests { validator_count: &usize, shard_count: &usize, crosslinking_shard_start: usize, - cycle_length: usize, + epoch_length: usize, min_committee_size: usize, ) -> ( Vec, @@ -132,7 +133,7 @@ mod tests { &validator_indices, &shard_indices, crosslinking_shard_start, - cycle_length, + epoch_length, min_committee_size, ); (validator_indices, shard_indices, result) @@ -194,13 +195,13 @@ mod tests { let validator_count: usize = 100; let shard_count: usize = 20; let crosslinking_shard_start: usize = 0; - let cycle_length: usize = 20; + let epoch_length: usize = 20; let min_committee_size: usize = 10; let (validators, shards, result) = generate_cycle_helper( &validator_count, &shard_count, crosslinking_shard_start, - cycle_length, + epoch_length, min_committee_size, ); let cycle = result.unwrap(); @@ -253,13 +254,13 @@ mod tests { let validator_count: usize = 523; let shard_count: usize = 31; let crosslinking_shard_start: usize = 0; - let cycle_length: usize = 11; + let epoch_length: usize = 11; let min_committee_size: usize = 5; let (validators, shards, result) = generate_cycle_helper( &validator_count, &shard_count, crosslinking_shard_start, - cycle_length, + epoch_length, min_committee_size, ); let cycle = result.unwrap();