diff --git a/Cargo.toml b/Cargo.toml index be3bdf381..a12317daa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -45,7 +45,6 @@ members = [ "beacon_chain/utils/slot_clock", "beacon_chain/utils/ssz", "beacon_chain/utils/vec_shuffle", - "beacon_chain/validator_change", "beacon_chain/validator_induction", "beacon_chain/validator_shuffling", "lighthouse/beacon_chain", diff --git a/beacon_chain/genesis/src/beacon_state.rs b/beacon_chain/genesis/src/beacon_state.rs index fda94552f..f9c2ef327 100644 --- a/beacon_chain/genesis/src/beacon_state.rs +++ b/beacon_chain/genesis/src/beacon_state.rs @@ -1,6 +1,5 @@ use spec::ChainSpec; -use types::{BeaconState, CrosslinkRecord, ForkData, ValidatorStatus}; -use validator_induction::ValidatorInductor; +use types::{BeaconState, CrosslinkRecord, ForkData}; use validator_shuffling::{shard_and_committees_for_cycle, ValidatorAssignmentError}; #[derive(Debug, PartialEq)] @@ -11,26 +10,11 @@ pub enum Error { } 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 a = shard_and_committees_for_cycle(&[0; 32], &spec.initial_validators, 0, &spec)?; let mut b = a.clone(); a.append(&mut b); a @@ -55,7 +39,8 @@ pub fn genesis_beacon_state(spec: &ChainSpec) -> Result { /* * Validator registry */ - validator_registry: validators, + validator_registry: spec.initial_validators.clone(), + validator_balances: spec.initial_balances.clone(), validator_registry_latest_change_slot: spec.initial_slot_number, validator_registry_exit_count: 0, validator_registry_delta_chain_tip: spec.zero_hash, @@ -100,7 +85,6 @@ mod tests { extern crate bls; extern crate validator_induction; - use self::bls::{create_proof_of_possession, Keypair}; use super::*; // TODO: enhance these tests. @@ -117,19 +101,4 @@ mod tests { 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/spec/src/foundation.rs b/beacon_chain/spec/src/foundation.rs index 4dd1931d2..31207f058 100644 --- a/beacon_chain/spec/src/foundation.rs +++ b/beacon_chain/spec/src/foundation.rs @@ -1,7 +1,10 @@ use super::ChainSpec; -use bls::{create_proof_of_possession, Keypair, PublicKey, SecretKey}; +use bls::{Keypair, PublicKey, SecretKey}; -use types::{Address, Hash256, ValidatorRegistration}; +use types::{Address, Hash256, ValidatorRecord}; + +/// The size of a validators deposit in GWei. +pub const DEPOSIT_GWEI: u64 = 32_000_000_000; impl ChainSpec { /// Returns a `ChainSpec` compatible with the specification from Ethereum Foundation. @@ -64,14 +67,15 @@ impl ChainSpec { * Intialization parameters */ initial_validators: initial_validators_for_testing(), + initial_balances: initial_balances_for_testing(), genesis_time: 1_544_672_897, 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 { +/// Generate a set of validator records 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", @@ -94,19 +98,28 @@ fn initial_validators_for_testing() -> Vec { pk: public_key, } }; - let validator_registration = ValidatorRegistration { + let validator_record = ValidatorRecord { pubkey: keypair.pk.clone(), - withdrawal_shard: 0, - withdrawal_address: Address::random(), - randao_commitment: Hash256::random(), - proof_of_possession: create_proof_of_possession(&keypair), + withdrawal_credentials: Hash256::zero(), + randao_commitment: Hash256::zero(), + randao_layers: 0, + status: From::from(0), + latest_status_change_slot: 0, + exit_count: 0, + custody_commitment: Hash256::zero(), + latest_custody_reseed_slot: 0, + penultimate_custody_reseed_slot: 0, }; - initial_validators.push(validator_registration); + initial_validators.push(validator_record); } initial_validators } +fn initial_balances_for_testing() -> Vec { + vec![DEPOSIT_GWEI; 4] +} + #[cfg(test)] mod tests { use super::*; diff --git a/beacon_chain/spec/src/lib.rs b/beacon_chain/spec/src/lib.rs index 3a05f2696..786cf326b 100644 --- a/beacon_chain/spec/src/lib.rs +++ b/beacon_chain/spec/src/lib.rs @@ -3,7 +3,7 @@ extern crate types; mod foundation; -use types::{Address, Hash256, ValidatorRegistration}; +use types::{Address, Hash256, ValidatorRecord}; #[derive(PartialEq, Debug)] pub struct ChainSpec { @@ -60,7 +60,8 @@ pub struct ChainSpec { /* * Intialization parameters */ - pub initial_validators: Vec, + pub initial_validators: Vec, + pub initial_balances: Vec, pub genesis_time: u64, pub processed_pow_receipt_root: Hash256, } diff --git a/beacon_chain/types/src/active_state.rs b/beacon_chain/types/src/active_state.rs deleted file mode 100644 index 5661fd60e..000000000 --- a/beacon_chain/types/src/active_state.rs +++ /dev/null @@ -1,17 +0,0 @@ -use super::Hash256; -use super::{Attestation, SpecialRecord}; - -#[derive(Debug, PartialEq)] -pub struct ActiveState { - pub pending_attestations: Vec, - pub pending_specials: Vec, - pub recent_block_hashes: Vec, - pub randao_mix: Hash256, -} - -impl ActiveState { - // TODO: implement this. - pub fn canonical_root(&self) -> Hash256 { - Hash256::zero() - } -} diff --git a/beacon_chain/types/src/beacon_state.rs b/beacon_chain/types/src/beacon_state.rs index 7bd0283c5..588cfb678 100644 --- a/beacon_chain/types/src/beacon_state.rs +++ b/beacon_chain/types/src/beacon_state.rs @@ -11,7 +11,7 @@ use hashing::canonical_hash; use rand::RngCore; use ssz::{ssz_encode, Decodable, DecodeError, Encodable, SszStream}; -#[derive(Debug, PartialEq, Clone)] +#[derive(Debug, PartialEq, Clone, Default)] pub struct BeaconState { // Misc pub slot: u64, @@ -20,6 +20,7 @@ pub struct BeaconState { // 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, @@ -62,6 +63,7 @@ impl Encodable for BeaconState { s.append(&self.genesis_time); s.append(&self.fork_data); s.append(&self.validator_registry); + s.append(&self.validator_balances); s.append(&self.validator_registry_latest_change_slot); s.append(&self.validator_registry_exit_count); s.append(&self.validator_registry_delta_chain_tip); @@ -89,6 +91,7 @@ impl Decodable for BeaconState { let (genesis_time, i) = <_>::ssz_decode(bytes, i)?; let (fork_data, i) = <_>::ssz_decode(bytes, i)?; let (validator_registry, i) = <_>::ssz_decode(bytes, i)?; + let (validator_balances, i) = <_>::ssz_decode(bytes, i)?; let (validator_registry_latest_change_slot, i) = <_>::ssz_decode(bytes, i)?; let (validator_registry_exit_count, i) = <_>::ssz_decode(bytes, i)?; let (validator_registry_delta_chain_tip, i) = <_>::ssz_decode(bytes, i)?; @@ -114,6 +117,7 @@ impl Decodable for BeaconState { genesis_time, fork_data, validator_registry, + validator_balances, validator_registry_latest_change_slot, validator_registry_exit_count, validator_registry_delta_chain_tip, @@ -145,6 +149,7 @@ impl TestRandom for BeaconState { genesis_time: <_>::random_for_test(rng), fork_data: <_>::random_for_test(rng), validator_registry: <_>::random_for_test(rng), + validator_balances: <_>::random_for_test(rng), validator_registry_latest_change_slot: <_>::random_for_test(rng), validator_registry_exit_count: <_>::random_for_test(rng), validator_registry_delta_chain_tip: <_>::random_for_test(rng), diff --git a/beacon_chain/types/src/chain_config.rs b/beacon_chain/types/src/chain_config.rs deleted file mode 100644 index 8428945bb..000000000 --- a/beacon_chain/types/src/chain_config.rs +++ /dev/null @@ -1,72 +0,0 @@ -use super::ValidatorRegistration; - -#[derive(Debug, Clone, PartialEq)] -pub struct ChainConfig { - // Old, potentially outdated constants - 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, - - // New constants - pub epoch_length: u64, - pub min_attestation_inclusion_delay: u64, -} - -/* - * Presently this is just some arbitrary time in Sept 2018. - */ -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, - max_validator_churn_quotient: 32, - genesis_time: TEST_GENESIS_TIME, - slot_duration_millis: 16 * 1000, - initial_validators: vec![], - - // New - epoch_length: 64, - min_attestation_inclusion_delay: 4, - } - } - - pub fn validate(&self) -> bool { - // criteria that ensure the config is valid - - // shard_count / cycle_length > 0 otherwise validator delegation - // will fail. - if self.shard_count / u16::from(self.cycle_length) == 0 { - return false; - } - - true - } - - #[cfg(test)] - pub fn super_fast_tests() -> Self { - Self { - cycle_length: 2, - deposit_size_gwei: 32 * (10 ^ 9), - shard_count: 2, - min_committee_size: 2, - max_validator_churn_quotient: 32, - genesis_time: TEST_GENESIS_TIME, // arbitrary - slot_duration_millis: 16 * 1000, - initial_validators: vec![], - - // New constants - epoch_length: 64, - min_attestation_inclusion_delay: 4, - } - } -} diff --git a/beacon_chain/types/src/crystallized_state.rs b/beacon_chain/types/src/crystallized_state.rs deleted file mode 100644 index 454454a17..000000000 --- a/beacon_chain/types/src/crystallized_state.rs +++ /dev/null @@ -1,28 +0,0 @@ -use super::crosslink_record::CrosslinkRecord; -use super::shard_committee::ShardCommittee; -use super::validator_record::ValidatorRecord; -use super::Hash256; - -#[derive(Debug, PartialEq)] -pub struct CrystallizedState { - pub validator_set_change_slot: u64, - pub validators: Vec, - pub crosslinks: Vec, - pub last_state_recalculation_slot: u64, - pub last_finalized_slot: u64, - pub last_justified_slot: u64, - pub justified_streak: u64, - 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, - pub post_fork_version: u32, - pub fork_slot_number: u32, -} - -impl CrystallizedState { - // TODO: implement this. - pub fn canonical_root(&self) -> Hash256 { - Hash256::zero() - } -} diff --git a/beacon_chain/types/src/deposit_input.rs b/beacon_chain/types/src/deposit_input.rs index d7e2272e9..7a2bfa3c9 100644 --- a/beacon_chain/types/src/deposit_input.rs +++ b/beacon_chain/types/src/deposit_input.rs @@ -9,6 +9,7 @@ pub struct DepositInput { pub pubkey: PublicKey, pub withdrawal_credentials: Hash256, pub randao_commitment: Hash256, + pub custody_commitment: Hash256, pub proof_of_possession: Signature, } @@ -17,6 +18,7 @@ impl Encodable for DepositInput { s.append(&self.pubkey); s.append(&self.withdrawal_credentials); s.append(&self.randao_commitment); + s.append(&self.custody_commitment); s.append(&self.proof_of_possession); } } @@ -26,6 +28,7 @@ impl Decodable for DepositInput { let (pubkey, i) = <_>::ssz_decode(bytes, i)?; let (withdrawal_credentials, i) = <_>::ssz_decode(bytes, i)?; let (randao_commitment, i) = <_>::ssz_decode(bytes, i)?; + let (custody_commitment, i) = <_>::ssz_decode(bytes, i)?; let (proof_of_possession, i) = <_>::ssz_decode(bytes, i)?; Ok(( @@ -33,6 +36,7 @@ impl Decodable for DepositInput { pubkey, withdrawal_credentials, randao_commitment, + custody_commitment, proof_of_possession, }, i, @@ -46,6 +50,7 @@ impl TestRandom for DepositInput { pubkey: <_>::random_for_test(rng), withdrawal_credentials: <_>::random_for_test(rng), randao_commitment: <_>::random_for_test(rng), + custody_commitment: <_>::random_for_test(rng), proof_of_possession: <_>::random_for_test(rng), } } diff --git a/beacon_chain/types/src/fork_data.rs b/beacon_chain/types/src/fork_data.rs index 7cd4dc3ac..a66502584 100644 --- a/beacon_chain/types/src/fork_data.rs +++ b/beacon_chain/types/src/fork_data.rs @@ -2,7 +2,7 @@ use super::ssz::{Decodable, DecodeError, Encodable, SszStream}; use crate::test_utils::TestRandom; use rand::RngCore; -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Default)] pub struct ForkData { pub pre_fork_version: u64, pub post_fork_version: u64, diff --git a/beacon_chain/types/src/lib.rs b/beacon_chain/types/src/lib.rs index 8a7fee0f9..42bea9e0a 100644 --- a/beacon_chain/types/src/lib.rs +++ b/beacon_chain/types/src/lib.rs @@ -5,7 +5,6 @@ extern crate ssz; pub mod test_utils; -pub mod active_state; pub mod attestation; pub mod attestation_data; pub mod beacon_block; @@ -13,9 +12,7 @@ pub mod beacon_block_body; pub mod beacon_state; pub mod candidate_pow_receipt_root_record; pub mod casper_slashing; -pub mod chain_config; pub mod crosslink_record; -pub mod crystallized_state; pub mod deposit; pub mod deposit_data; pub mod deposit_input; @@ -29,23 +26,19 @@ pub mod shard_reassignment_record; pub mod slashable_vote_data; pub mod special_record; pub mod validator_record; -pub mod validator_registration; pub mod readers; use self::ethereum_types::{H160, H256, U256}; use std::collections::HashMap; -pub use crate::active_state::ActiveState; pub use crate::attestation::Attestation; pub use crate::attestation_data::AttestationData; pub use crate::beacon_block::BeaconBlock; pub use crate::beacon_block_body::BeaconBlockBody; pub use crate::beacon_state::BeaconState; pub use crate::casper_slashing::CasperSlashing; -pub use crate::chain_config::ChainConfig; pub use crate::crosslink_record::CrosslinkRecord; -pub use crate::crystallized_state::CrystallizedState; pub use crate::deposit::Deposit; pub use crate::deposit_data::DepositData; pub use crate::deposit_input::DepositInput; @@ -58,7 +51,6 @@ pub use crate::shard_committee::ShardCommittee; pub use crate::slashable_vote_data::SlashableVoteData; pub use crate::special_record::{SpecialRecord, SpecialRecordKind}; pub use crate::validator_record::{ValidatorRecord, ValidatorStatus}; -pub use crate::validator_registration::ValidatorRegistration; pub type Hash256 = H256; pub type Address = H160; diff --git a/beacon_chain/types/src/validator_record.rs b/beacon_chain/types/src/validator_record.rs index 1168296f7..5f74d28d2 100644 --- a/beacon_chain/types/src/validator_record.rs +++ b/beacon_chain/types/src/validator_record.rs @@ -1,5 +1,5 @@ use super::bls::PublicKey; -use super::{Address, Hash256}; +use super::{Hash256}; use crate::test_utils::TestRandom; use rand::RngCore; use ssz::{Decodable, DecodeError, Encodable, SszStream}; @@ -32,13 +32,15 @@ impl convert::From for ValidatorStatus { #[derive(Debug, Clone, PartialEq)] pub struct ValidatorRecord { pub pubkey: PublicKey, - pub withdrawal_shard: u64, - pub withdrawal_address: Address, + pub withdrawal_credentials: Hash256, pub randao_commitment: Hash256, - pub randao_last_change: u64, - pub balance: u64, + pub randao_layers: u64, pub status: ValidatorStatus, - pub exit_slot: u64, + pub latest_status_change_slot: u64, + pub exit_count: u64, + pub custody_commitment: Hash256, + pub latest_custody_reseed_slot: u64, + pub penultimate_custody_reseed_slot: u64 } impl ValidatorRecord { @@ -94,37 +96,43 @@ impl TestRandom for ValidatorStatus { impl Encodable for ValidatorRecord { fn ssz_append(&self, s: &mut SszStream) { s.append(&self.pubkey); - s.append(&self.withdrawal_shard); - s.append(&self.withdrawal_address); + s.append(&self.withdrawal_credentials); s.append(&self.randao_commitment); - s.append(&self.randao_last_change); - s.append(&self.balance); + s.append(&self.randao_layers); s.append(&self.status); - s.append(&self.exit_slot); + s.append(&self.latest_status_change_slot); + s.append(&self.exit_count); + s.append(&self.custody_commitment); + s.append(&self.latest_custody_reseed_slot); + s.append(&self.penultimate_custody_reseed_slot); } } impl Decodable for ValidatorRecord { fn ssz_decode(bytes: &[u8], i: usize) -> Result<(Self, usize), DecodeError> { let (pubkey, i) = <_>::ssz_decode(bytes, i)?; - let (withdrawal_shard, i) = <_>::ssz_decode(bytes, i)?; - let (withdrawal_address, i) = <_>::ssz_decode(bytes, i)?; + let (withdrawal_credentials, i) = <_>::ssz_decode(bytes, i)?; let (randao_commitment, i) = <_>::ssz_decode(bytes, i)?; - let (randao_last_change, i) = <_>::ssz_decode(bytes, i)?; - let (balance, i) = <_>::ssz_decode(bytes, i)?; + let (randao_layers, i) = <_>::ssz_decode(bytes, i)?; let (status, i) = <_>::ssz_decode(bytes, i)?; - let (exit_slot, i) = <_>::ssz_decode(bytes, i)?; + let (latest_status_change_slot, i) = <_>::ssz_decode(bytes, i)?; + let (exit_count, i) = <_>::ssz_decode(bytes, i)?; + let (custody_commitment, i) = <_>::ssz_decode(bytes, i)?; + let (latest_custody_reseed_slot, i) = <_>::ssz_decode(bytes, i)?; + let (penultimate_custody_reseed_slot, i) = <_>::ssz_decode(bytes, i)?; Ok(( Self { pubkey, - withdrawal_shard, - withdrawal_address, + withdrawal_credentials, randao_commitment, - randao_last_change, - balance, + randao_layers, status, - exit_slot, + latest_status_change_slot, + exit_count, + custody_commitment, + latest_custody_reseed_slot, + penultimate_custody_reseed_slot }, i, )) @@ -135,13 +143,15 @@ impl TestRandom for ValidatorRecord { fn random_for_test(rng: &mut T) -> Self { Self { pubkey: <_>::random_for_test(rng), - withdrawal_shard: <_>::random_for_test(rng), - withdrawal_address: <_>::random_for_test(rng), + withdrawal_credentials: <_>::random_for_test(rng), randao_commitment: <_>::random_for_test(rng), - randao_last_change: <_>::random_for_test(rng), - balance: <_>::random_for_test(rng), + randao_layers: <_>::random_for_test(rng), status: <_>::random_for_test(rng), - exit_slot: <_>::random_for_test(rng), + latest_status_change_slot: <_>::random_for_test(rng), + exit_count: <_>::random_for_test(rng), + custody_commitment: <_>::random_for_test(rng), + latest_custody_reseed_slot: <_>::random_for_test(rng), + penultimate_custody_reseed_slot: <_>::random_for_test(rng), } } } diff --git a/beacon_chain/utils/vec_shuffle/src/lib.rs b/beacon_chain/utils/vec_shuffle/src/lib.rs index b3d540174..f5c2b7ebd 100644 --- a/beacon_chain/utils/vec_shuffle/src/lib.rs +++ b/beacon_chain/utils/vec_shuffle/src/lib.rs @@ -42,40 +42,40 @@ mod tests { extern crate yaml_rust; use self::yaml_rust::yaml; - use super::hashing::canonical_hash; - use super::*; - use std::fs::File; - use std::io::prelude::*; - // TODO: update test vectors to use keccak instead of blake. - // https://github.com/sigp/lighthouse/issues/121 + use std::{fs::File, io::prelude::*, path::PathBuf}; + + use super::{hashing::canonical_hash, *}; + #[test] - #[should_panic] fn test_shuffling() { - let mut file = File::open("./src/specs/shuffle_test_vectors.yaml").unwrap(); + let mut file = { + let mut file_path_buf = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + file_path_buf.push("src/specs/shuffle_test_vectors.yaml"); + + File::open(file_path_buf).unwrap() + }; + let mut yaml_str = String::new(); file.read_to_string(&mut yaml_str).unwrap(); let docs = yaml::YamlLoader::load_from_str(&yaml_str).unwrap(); let doc = &docs[0]; - let test_cases = doc["test_cases"].as_vec(); + let test_cases = doc["test_cases"].as_vec().unwrap(); - for test_case in test_cases.unwrap() { + for test_case in test_cases { let input = test_case["input"].clone().into_vec().unwrap(); let output = test_case["output"].clone().into_vec().unwrap(); let seed_bytes = test_case["seed"].as_str().unwrap().as_bytes(); - let mut seed; - if seed_bytes.len() > 0 { - seed = canonical_hash(seed_bytes); + let seed = if seed_bytes.len() > 0 { + canonical_hash(seed_bytes) } else { - seed = vec![]; - } + vec![] + }; - let mut s = shuffle(&seed, input).unwrap(); - - assert_eq!(s, output); + assert_eq!(shuffle(&seed, input).unwrap(), output); } } } diff --git a/beacon_chain/utils/vec_shuffle/src/specs/shuffle_test_vectors.yaml b/beacon_chain/utils/vec_shuffle/src/specs/shuffle_test_vectors.yaml index c97d6d328..2571f0804 100644 --- a/beacon_chain/utils/vec_shuffle/src/specs/shuffle_test_vectors.yaml +++ b/beacon_chain/utils/vec_shuffle/src/specs/shuffle_test_vectors.yaml @@ -1,5 +1,3 @@ -# This file was generated with sigp/shuffling_sandbox -# python3 sandbox.py test_vectors title: Shuffling Algorithm Tests summary: Test vectors for shuffling a list based upon a seed. test_suite: Shuffling @@ -15,13 +13,13 @@ test_cases: output: [255] seed: '' - input: [4, 6, 2, 6, 1, 4, 6, 2, 1, 5] - output: [1, 6, 4, 1, 6, 6, 2, 2, 4, 5] + output: [2, 1, 1, 5, 6, 6, 6, 2, 4, 4] seed: '' - input: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13] - output: [4, 7, 10, 13, 3, 1, 2, 9, 12, 6, 11, 8, 5] + output: [4, 9, 6, 8, 13, 3, 2, 11, 5, 1, 12, 7, 10] seed: '' - input: [65, 6, 2, 6, 1, 4, 6, 2, 1, 5] - output: [1, 6, 65, 1, 6, 6, 2, 2, 4, 5] + output: [2, 1, 1, 5, 6, 6, 6, 2, 4, 65] seed: '' - input: [] output: [] @@ -33,13 +31,13 @@ test_cases: output: [255] seed: 4kn4driuctg8 - input: [4, 6, 2, 6, 1, 4, 6, 2, 1, 5] - output: [6, 4, 2, 5, 4, 2, 6, 6, 1, 1] + output: [2, 4, 4, 2, 1, 1, 6, 5, 6, 6] seed: 4kn4driuctg8 - input: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13] - output: [13, 1, 9, 8, 3, 10, 6, 2, 5, 12, 11, 4, 7] + output: [7, 6, 3, 12, 11, 1, 8, 13, 10, 5, 9, 4, 2] seed: 4kn4driuctg8 - input: [65, 6, 2, 6, 1, 4, 6, 2, 1, 5] - output: [6, 65, 2, 5, 4, 2, 6, 6, 1, 1] + output: [2, 4, 65, 2, 1, 1, 6, 5, 6, 6] seed: 4kn4driuctg8 - input: [] output: [] @@ -51,13 +49,13 @@ test_cases: output: [255] seed: ytre1p - input: [4, 6, 2, 6, 1, 4, 6, 2, 1, 5] - output: [6, 2, 5, 1, 6, 4, 1, 2, 4, 6] + output: [6, 1, 1, 5, 6, 2, 6, 2, 4, 4] seed: ytre1p - input: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13] - output: [3, 8, 10, 4, 7, 11, 6, 1, 2, 5, 13, 9, 12] + output: [6, 2, 3, 4, 8, 5, 12, 9, 7, 11, 10, 1, 13] seed: ytre1p - input: [65, 6, 2, 6, 1, 4, 6, 2, 1, 5] - output: [6, 2, 5, 1, 6, 4, 1, 2, 65, 6] + output: [6, 1, 1, 5, 6, 2, 6, 2, 4, 65] seed: ytre1p - input: [] output: [] @@ -69,13 +67,13 @@ test_cases: output: [255] seed: mytobcffnkvj - input: [4, 6, 2, 6, 1, 4, 6, 2, 1, 5] - output: [5, 6, 2, 1, 6, 4, 6, 4, 1, 2] + output: [2, 4, 1, 1, 6, 4, 6, 5, 6, 2] seed: mytobcffnkvj - input: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13] - output: [12, 4, 11, 6, 13, 10, 9, 2, 3, 7, 8, 1, 5] + output: [11, 5, 9, 7, 2, 4, 12, 10, 8, 1, 6, 3, 13] seed: mytobcffnkvj - input: [65, 6, 2, 6, 1, 4, 6, 2, 1, 5] - output: [5, 6, 2, 1, 6, 65, 6, 4, 1, 2] + output: [2, 65, 1, 1, 6, 4, 6, 5, 6, 2] seed: mytobcffnkvj - input: [] output: [] @@ -87,13 +85,13 @@ test_cases: output: [255] seed: myzu3g7evxp5nkvj - input: [4, 6, 2, 6, 1, 4, 6, 2, 1, 5] - output: [6, 2, 6, 5, 4, 4, 1, 6, 2, 1] + output: [6, 2, 1, 4, 2, 6, 5, 6, 4, 1] seed: myzu3g7evxp5nkvj - input: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13] - output: [10, 12, 13, 3, 7, 11, 2, 4, 9, 8, 6, 5, 1] + output: [2, 1, 11, 3, 9, 7, 8, 13, 4, 10, 5, 6, 12] seed: myzu3g7evxp5nkvj - input: [65, 6, 2, 6, 1, 4, 6, 2, 1, 5] - output: [6, 2, 6, 5, 65, 4, 1, 6, 2, 1] + output: [6, 2, 1, 4, 2, 6, 5, 6, 65, 1] seed: myzu3g7evxp5nkvj - input: [] output: [] @@ -105,13 +103,13 @@ test_cases: output: [255] seed: xdpli1jsx5xb - input: [4, 6, 2, 6, 1, 4, 6, 2, 1, 5] - output: [6, 2, 4, 1, 2, 6, 5, 1, 6, 4] + output: [2, 1, 2, 4, 6, 6, 5, 6, 1, 4] seed: xdpli1jsx5xb - input: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13] - output: [11, 8, 12, 9, 2, 1, 10, 4, 13, 5, 7, 3, 6] + output: [5, 8, 12, 9, 11, 4, 7, 13, 1, 3, 2, 10, 6] seed: xdpli1jsx5xb - input: [65, 6, 2, 6, 1, 4, 6, 2, 1, 5] - output: [6, 2, 65, 1, 2, 6, 5, 1, 6, 4] + output: [2, 1, 2, 65, 6, 6, 5, 6, 1, 4] seed: xdpli1jsx5xb - input: [] output: [] @@ -123,11 +121,11 @@ test_cases: output: [255] seed: oab3mbb3xe8qsx5xb - input: [4, 6, 2, 6, 1, 4, 6, 2, 1, 5] - output: [2, 5, 1, 6, 1, 2, 6, 6, 4, 4] + output: [6, 2, 1, 1, 6, 2, 4, 4, 6, 5] seed: oab3mbb3xe8qsx5xb - input: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13] - output: [5, 13, 9, 7, 11, 10, 12, 2, 6, 8, 3, 1, 4] + output: [1, 8, 5, 13, 2, 10, 7, 11, 12, 6, 3, 4, 9] seed: oab3mbb3xe8qsx5xb - input: [65, 6, 2, 6, 1, 4, 6, 2, 1, 5] - output: [2, 5, 1, 6, 1, 2, 6, 6, 65, 4] + output: [6, 2, 1, 1, 6, 2, 4, 65, 6, 5] seed: oab3mbb3xe8qsx5xb diff --git a/beacon_chain/validator_induction/Cargo.toml b/beacon_chain/validator_induction/Cargo.toml index 5907014df..3530bbbf1 100644 --- a/beacon_chain/validator_induction/Cargo.toml +++ b/beacon_chain/validator_induction/Cargo.toml @@ -8,3 +8,4 @@ edition = "2018" bls = { path = "../utils/bls" } hashing = { path = "../utils/hashing" } types = { path = "../types" } +spec = { path = "../spec" } diff --git a/beacon_chain/validator_induction/src/inductor.rs b/beacon_chain/validator_induction/src/inductor.rs index 3d3b15ce3..720e38fa4 100644 --- a/beacon_chain/validator_induction/src/inductor.rs +++ b/beacon_chain/validator_induction/src/inductor.rs @@ -1,278 +1,206 @@ -use bls::verify_proof_of_possession; -use types::{ValidatorRecord, ValidatorRegistration, ValidatorStatus}; - -/// The size of a validators deposit in GWei. -pub const DEPOSIT_GWEI: u64 = 32_000_000_000; - -/// Inducts validators into a `CrystallizedState`. -pub struct ValidatorInductor { - pub current_slot: u64, - pub shard_count: u64, - validators: Vec, - empty_validator_start: usize, -} +use bls::{verify_proof_of_possession}; +use types::{BeaconState, Deposit, ValidatorRecord, ValidatorStatus}; +use spec::ChainSpec; #[derive(Debug, PartialEq, Clone)] pub enum ValidatorInductionError { InvalidShard, InvaidProofOfPossession, + InvalidWithdrawalCredentials } -impl ValidatorInductor { - pub fn new(current_slot: u64, shard_count: u64, validators: Vec) -> Self { - Self { - current_slot, - shard_count, - validators, - empty_validator_start: 0, - } +pub fn process_deposit( + state: &mut BeaconState, + deposit: &Deposit, + spec: &ChainSpec) +-> Result { + let deposit_input = &deposit.deposit_data.deposit_input; + let deposit_data = &deposit.deposit_data; + + // TODO: Update the signature validation as defined in the spec once issues #91 and #70 are completed + if !verify_proof_of_possession(&deposit_input.proof_of_possession, &deposit_input.pubkey) { + return Err(ValidatorInductionError::InvaidProofOfPossession); } - /// Attempt to induct a validator into the CrystallizedState. - /// - /// 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, - status: ValidatorStatus, - ) -> Result { - let v = self.process_registration(rego, status)?; - Ok(self.add_validator(v)) - } + let validator_index = state.validator_registry.iter() + .position(|validator| validator.pubkey == deposit_input.pubkey); - /// Verify a `ValidatorRegistration` and return a `ValidatorRecord` if valid. - fn process_registration( - &self, - r: &ValidatorRegistration, - status: ValidatorStatus, - ) -> Result { - /* - * Ensure withdrawal shard is not too high. - */ - if r.withdrawal_shard > self.shard_count { - return Err(ValidatorInductionError::InvalidShard); - } - - /* - * Prove validator has knowledge of their secret key. - */ - if !verify_proof_of_possession(&r.proof_of_possession, &r.pubkey) { - return Err(ValidatorInductionError::InvaidProofOfPossession); - } - - Ok(ValidatorRecord { - pubkey: r.pubkey.clone(), - withdrawal_shard: r.withdrawal_shard, - withdrawal_address: r.withdrawal_address, - randao_commitment: r.randao_commitment, - randao_last_change: self.current_slot, - balance: DEPOSIT_GWEI, - status: status, - exit_slot: 0, - }) - } - - /// Returns the index of the first `ValidatorRecord` in the `CrystallizedState` where - /// `validator.status == Withdrawn`. If no such record exists, `None` is returned. - fn first_withdrawn_validator(&mut self) -> Option { - for i in self.empty_validator_start..self.validators.len() { - if self.validators[i].status == ValidatorStatus::Withdrawn { - self.empty_validator_start = i + 1; - return Some(i); + match validator_index { + Some(i) => { + if state.validator_registry[i].withdrawal_credentials == deposit_input.withdrawal_credentials { + state.validator_balances[i] += deposit_data.value; + return Ok(i); } - } - None - } - /// Adds a `ValidatorRecord` to the `CrystallizedState` by replacing first validator where - /// `validator.status == Withdraw`. If no such withdrawn validator exists, adds the new - /// validator to the end of the list. - fn add_validator(&mut self, v: ValidatorRecord) -> usize { - match self.first_withdrawn_validator() { - Some(i) => { - self.validators[i] = v; - i - } - None => { - self.validators.push(v); - self.validators.len() - 1 + Err(ValidatorInductionError::InvalidWithdrawalCredentials) + }, + None => { + let validator = ValidatorRecord { + pubkey: deposit_input.pubkey.clone(), + withdrawal_credentials: deposit_input.withdrawal_credentials, + randao_commitment: deposit_input.randao_commitment, + randao_layers: 0, + status: ValidatorStatus::PendingActivation, + latest_status_change_slot: state.validator_registry_latest_change_slot, + exit_count: 0, + custody_commitment: deposit_input.custody_commitment, + latest_custody_reseed_slot: 0, + penultimate_custody_reseed_slot: 0 + }; + + match min_empty_validator_index(state, spec) { + Some(i) => { + state.validator_registry[i] = validator; + state.validator_balances[i] = deposit_data.value; + Ok(i) + }, + None => { + state.validator_registry.push(validator); + state.validator_balances.push(deposit_data.value); + Ok(state.validator_registry.len() - 1) + } } } } +} - pub fn to_vec(self) -> Vec { - self.validators +fn min_empty_validator_index( + state: &BeaconState, + spec: &ChainSpec +) -> Option { + for i in 0..state.validator_registry.len() { + if state.validator_balances[i] == 0 + && state.validator_registry[i].latest_status_change_slot + + spec.zero_balance_validator_ttl <= state.slot { + return Some(i); + } } + None } #[cfg(test)] mod tests { use super::*; + use types::test_utils::{SeedableRng, TestRandom, XorShiftRng}; use bls::{create_proof_of_possession, Keypair}; - use types::test_utils::{SeedableRng, TestRandom, XorShiftRng}; - use types::{Address, Hash256}; - fn registration_equals_record(reg: &ValidatorRegistration, rec: &ValidatorRecord) -> bool { - (reg.pubkey == rec.pubkey) - & (reg.withdrawal_shard == rec.withdrawal_shard) - & (reg.withdrawal_address == rec.withdrawal_address) - & (reg.randao_commitment == rec.randao_commitment) - & (verify_proof_of_possession(®.proof_of_possession, &rec.pubkey)) + /// The size of a validators deposit in GWei. + pub const DEPOSIT_GWEI: u64 = 32_000_000_000; + + fn get_deposit() -> Deposit { + let mut rng = XorShiftRng::from_seed([42; 16]); + let mut deposit = Deposit::random_for_test(&mut rng); + + let kp = Keypair::random(); + deposit.deposit_data.deposit_input.pubkey = kp.pk.clone(); + deposit.deposit_data.deposit_input.proof_of_possession = create_proof_of_possession(&kp); + deposit } - /// Generate a basic working ValidatorRegistration for use in tests. - fn get_registration() -> ValidatorRegistration { - let kp = Keypair::random(); - ValidatorRegistration { - pubkey: kp.pk.clone(), - withdrawal_shard: 0, - withdrawal_address: Address::zero(), - randao_commitment: Hash256::zero(), - proof_of_possession: create_proof_of_possession(&kp), - } + fn get_validator() -> ValidatorRecord { + let mut rng = XorShiftRng::from_seed([42; 16]); + ValidatorRecord::random_for_test(&mut rng) + } + + fn deposit_equals_record(dep: &Deposit, val: &ValidatorRecord) -> bool { + (dep.deposit_data.deposit_input.pubkey == val.pubkey) + & (dep.deposit_data.deposit_input.withdrawal_credentials == val.withdrawal_credentials) + & (dep.deposit_data.deposit_input.randao_commitment == val.randao_commitment) + & (verify_proof_of_possession(&dep.deposit_data.deposit_input.proof_of_possession, &val.pubkey)) } #[test] - fn test_validator_inductor_valid_empty_validators() { - let validators = vec![]; + fn test_process_deposit_valid_empty_validators() { + let mut state = BeaconState::default(); + let mut deposit = get_deposit(); + let spec = ChainSpec::foundation(); + deposit.deposit_data.value = DEPOSIT_GWEI; - let r = get_registration(); - - let mut inductor = ValidatorInductor::new(0, 1024, validators); - let result = inductor.induct(&r, ValidatorStatus::PendingActivation); - let validators = inductor.to_vec(); + let result = process_deposit(&mut state, &deposit, &spec); assert_eq!(result.unwrap(), 0); - assert!(registration_equals_record(&r, &validators[0])); - assert_eq!(validators.len(), 1); + assert!(deposit_equals_record(&deposit, &state.validator_registry[0])); + assert_eq!(state.validator_registry.len(), 1); + assert_eq!(state.validator_balances.len(), 1); } #[test] - fn test_validator_inductor_status() { - let validators = vec![]; + fn test_process_deposits_empty_validators() { + let mut state = BeaconState::default(); + let spec = ChainSpec::foundation(); - 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); - assert!(validators[1].status == ValidatorStatus::Active); - assert_eq!(validators.len(), 2); - } - - #[test] - fn test_validator_inductor_valid_all_active_validators() { - let mut rng = XorShiftRng::from_seed([42; 16]); - let mut validators = vec![]; - for _ in 0..5 { - let mut v = ValidatorRecord::random_for_test(&mut rng); - v.status = ValidatorStatus::Active; - validators.push(v); + for i in 0..5 { + let mut deposit = get_deposit(); + let result = process_deposit(&mut state, &deposit, &spec); + deposit.deposit_data.value = DEPOSIT_GWEI; + assert_eq!(result.unwrap(), i); + assert!(deposit_equals_record(&deposit, &state.validator_registry[i])); + assert_eq!(state.validator_registry.len(), i + 1); + assert_eq!(state.validator_balances.len(), i + 1); } - - let r = get_registration(); - - let mut inductor = ValidatorInductor::new(0, 1024, validators); - let result = inductor.induct(&r, ValidatorStatus::PendingActivation); - let validators = inductor.to_vec(); - - assert_eq!(result.unwrap(), 5); - assert!(registration_equals_record(&r, &validators[5])); - assert_eq!(validators.len(), 6); } #[test] - fn test_validator_inductor_valid_all_second_validator_withdrawn() { - let mut rng = XorShiftRng::from_seed([42; 16]); - let mut validators = vec![]; - let mut v = ValidatorRecord::random_for_test(&mut rng); - v.status = ValidatorStatus::Active; - validators.push(v); - for _ in 0..4 { - let mut v = ValidatorRecord::random_for_test(&mut rng); - v.status = ValidatorStatus::Withdrawn; - validators.push(v); - } + fn test_process_deposit_top_out() { + let mut state = BeaconState::default(); + let spec = ChainSpec::foundation(); - let r = get_registration(); + let mut deposit = get_deposit(); + let mut validator = get_validator(); - let mut inductor = ValidatorInductor::new(0, 1024, validators); - let result = inductor.induct(&r, ValidatorStatus::PendingActivation); - let validators = inductor.to_vec(); + deposit.deposit_data.value = DEPOSIT_GWEI; + validator.pubkey = deposit.deposit_data.deposit_input.pubkey.clone(); + validator.withdrawal_credentials = deposit.deposit_data.deposit_input.withdrawal_credentials; + validator.randao_commitment = deposit.deposit_data.deposit_input.randao_commitment; - assert_eq!(result.unwrap(), 1); - assert!(registration_equals_record(&r, &validators[1])); - assert_eq!(validators.len(), 5); - } + state.validator_registry.push(validator); + state.validator_balances.push(DEPOSIT_GWEI); - #[test] - fn test_validator_inductor_valid_all_withdrawn_validators() { - let mut rng = XorShiftRng::from_seed([42; 16]); - let mut validators = vec![]; - for _ in 0..5 { - let mut v = ValidatorRecord::random_for_test(&mut rng); - v.status = ValidatorStatus::Withdrawn; - validators.push(v); - } + let result = process_deposit(&mut state, &deposit, &spec); - /* - * Ensure the first validator gets the 0'th slot - */ - let r = get_registration(); - let mut inductor = ValidatorInductor::new(0, 1024, validators); - let result = inductor.induct(&r, ValidatorStatus::PendingActivation); - let validators = inductor.to_vec(); assert_eq!(result.unwrap(), 0); - assert!(registration_equals_record(&r, &validators[0])); - - /* - * Ensure the second validator gets the 1'st slot - */ - let r_two = get_registration(); - let mut inductor = ValidatorInductor::new(0, 1024, validators); - 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])); - assert_eq!(validators.len(), 5); + assert!(deposit_equals_record(&deposit, &state.validator_registry[0])); + assert_eq!(state.validator_balances[0], DEPOSIT_GWEI * 2); + assert_eq!(state.validator_registry.len(), 1); + assert_eq!(state.validator_balances.len(), 1); } #[test] - fn test_validator_inductor_shard_too_high() { - let validators = vec![]; + fn test_process_deposit_replace_validator() { + let mut state = BeaconState::default(); + let spec = ChainSpec::foundation(); - let mut r = get_registration(); - r.withdrawal_shard = 1025; + let mut validator = get_validator(); + validator.latest_status_change_slot = 0; + state.validator_registry.push(validator); + state.validator_balances.push(0); - let mut inductor = ValidatorInductor::new(0, 1024, validators); - let result = inductor.induct(&r, ValidatorStatus::PendingActivation); - let validators = inductor.to_vec(); + let mut deposit = get_deposit(); + deposit.deposit_data.value = DEPOSIT_GWEI; + state.slot = spec.zero_balance_validator_ttl; - assert_eq!(result, Err(ValidatorInductionError::InvalidShard)); - assert_eq!(validators.len(), 0); + let result = process_deposit(&mut state, &deposit, &spec); + + assert_eq!(result.unwrap(), 0); + assert!(deposit_equals_record(&deposit, &state.validator_registry[0])); + assert_eq!(state.validator_balances[0], DEPOSIT_GWEI); + assert_eq!(state.validator_registry.len(), 1); + assert_eq!(state.validator_balances.len(), 1); } #[test] - fn test_validator_inductor_shard_proof_of_possession_failure() { - let validators = vec![]; + fn test_process_deposit_invalid_proof_of_possession() { + let mut state = BeaconState::default(); + let mut deposit = get_deposit(); + let spec = ChainSpec::foundation(); + deposit.deposit_data.value = DEPOSIT_GWEI; + deposit.deposit_data.deposit_input.proof_of_possession = create_proof_of_possession(&Keypair::random()); - let mut r = get_registration(); - let kp = Keypair::random(); - r.proof_of_possession = create_proof_of_possession(&kp); + let result = process_deposit(&mut state, &deposit, &spec); - let mut inductor = ValidatorInductor::new(0, 1024, validators); - let result = inductor.induct(&r, ValidatorStatus::PendingActivation); - let validators = inductor.to_vec(); - - assert_eq!( - result, - Err(ValidatorInductionError::InvaidProofOfPossession) - ); - assert_eq!(validators.len(), 0); + assert_eq!(result, Err(ValidatorInductionError::InvaidProofOfPossession)); + assert_eq!(state.validator_registry.len(), 0); + assert_eq!(state.validator_balances.len(), 0); } } diff --git a/beacon_chain/validator_induction/src/lib.rs b/beacon_chain/validator_induction/src/lib.rs index ded9785da..f6dec3cfa 100644 --- a/beacon_chain/validator_induction/src/lib.rs +++ b/beacon_chain/validator_induction/src/lib.rs @@ -1,7 +1,8 @@ extern crate bls; extern crate hashing; extern crate types; +extern crate spec; mod inductor; -pub use crate::inductor::{ValidatorInductionError, ValidatorInductor}; +pub use crate::inductor::{ValidatorInductionError, process_deposit};