From d5675062c1d3456d8c7992411363c54c928fdb81 Mon Sep 17 00:00:00 2001 From: Paul Hauner Date: Sat, 20 Oct 2018 03:09:42 +1100 Subject: [PATCH 1/8] Add proof-of-possession hash fn --- beacon_chain/utils/hashing/src/lib.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/beacon_chain/utils/hashing/src/lib.rs b/beacon_chain/utils/hashing/src/lib.rs index 8e2bd37a9e..7c349e39d0 100644 --- a/beacon_chain/utils/hashing/src/lib.rs +++ b/beacon_chain/utils/hashing/src/lib.rs @@ -6,3 +6,12 @@ pub fn canonical_hash(input: &[u8]) -> Vec { let result = blake2b(64, &[], input); result.as_bytes()[0..32].to_vec() } + +pub fn proof_of_possession_hash(input: &[u8]) -> Vec { + let result = blake2b(64, &[], input); + let mut hash = result.as_bytes()[32..64].to_vec(); + // TODO: this padding is not part of the spec, it is required otherwise Milagro will panic. + // We should either drop the padding or ensure the padding is in the spec. + hash.append(&mut vec![0; 18]); + hash +} From b4566a776aefa66889c971da809960994856143f Mon Sep 17 00:00:00 2001 From: Paul Hauner Date: Sat, 20 Oct 2018 03:11:45 +1100 Subject: [PATCH 2/8] Add validator induction functionality --- Cargo.toml | 1 + beacon_chain/validator_induction/Cargo.toml | 9 + .../validator_induction/src/inductor.rs | 256 ++++++++++++++++++ beacon_chain/validator_induction/src/lib.rs | 12 + .../src/proof_of_possession.rs | 14 + .../validator_induction/src/registration.rs | 105 +++++++ 6 files changed, 397 insertions(+) create mode 100644 beacon_chain/validator_induction/Cargo.toml create mode 100644 beacon_chain/validator_induction/src/inductor.rs create mode 100644 beacon_chain/validator_induction/src/lib.rs create mode 100644 beacon_chain/validator_induction/src/proof_of_possession.rs create mode 100644 beacon_chain/validator_induction/src/registration.rs diff --git a/Cargo.toml b/Cargo.toml index da5d1b9050..55bd4de532 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -42,5 +42,6 @@ members = [ "beacon_chain/utils/ssz", "beacon_chain/utils/ssz_helpers", "beacon_chain/validation", + "beacon_chain/validator_induction", "lighthouse/db", ] diff --git a/beacon_chain/validator_induction/Cargo.toml b/beacon_chain/validator_induction/Cargo.toml new file mode 100644 index 0000000000..9a443133e9 --- /dev/null +++ b/beacon_chain/validator_induction/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "validator_induction" +version = "0.1.0" +authors = ["Paul Hauner "] + +[dependencies] +bls = { path = "../utils/bls" } +hashing = { path = "../utils/hashing" } +types = { path = "../types" } diff --git a/beacon_chain/validator_induction/src/inductor.rs b/beacon_chain/validator_induction/src/inductor.rs new file mode 100644 index 0000000000..15d2c44494 --- /dev/null +++ b/beacon_chain/validator_induction/src/inductor.rs @@ -0,0 +1,256 @@ +use types::{ + ValidatorRecord, + ValidatorStatus, +}; + +use super::proof_of_possession::verify_proof_of_possession; +use super::registration::ValidatorRegistration; + +/// 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<'a> { + pub current_slot: u64, + pub shard_count: u16, + validators: &'a mut Vec, + empty_validator_start: usize, +} + +#[derive(Debug, PartialEq, Clone)] +pub enum ValidatorInductionError { + InvalidShard, + InvaidProofOfPossession, +} + +impl<'a> ValidatorInductor<'a> { + /// 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) + -> Result + { + let v = self.process_registration(rego)?; + Ok(self.add_validator(v)) + } + + /// Verify a `ValidatorRegistration` and return a `ValidatorRecord` if valid. + fn process_registration(&self, r: &ValidatorRegistration) + -> 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: ValidatorStatus::PendingActivation as u8, + 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 as u8 { + self.empty_validator_start = i + 1; + return Some(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 + } + } + } +} + + +#[cfg(test)] +mod tests { + use super::*; + + use bls::{ + Keypair, + Signature, + }; + use types::{ + Address, + Hash256, + }; + use hashing::proof_of_possession_hash; + + /// Generate a proof of possession for some keypair. + fn get_proof_of_possession(kp: &Keypair) -> Signature { + let pop_message = proof_of_possession_hash(&kp.pk.as_bytes()); + Signature::new_hashed(&pop_message, &kp.sk) + } + + /// 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: get_proof_of_possession(&kp), + } + } + + /// Induct a validator using the ValidatorInductor, return the result. + fn do_induction(validator_rego: &ValidatorRegistration, + validators: &mut Vec, + current_slot: u64, + shard_count: u16) + -> Result + { + let mut inductor = ValidatorInductor { + current_slot, + shard_count, + empty_validator_start: 0, + validators, + }; + inductor.induct(&validator_rego) + } + + #[test] + fn test_validator_inductor_valid_empty_validators() { + let mut validators = vec![]; + + let r = get_registration(); + + let result = do_induction(&r, &mut validators, 0, 1024); + + assert_eq!(result.unwrap(), 0); + assert_eq!(r, validators[0]); + assert_eq!(validators.len(), 1); + } + + #[test] + fn test_validator_inductor_valid_all_active_validators() { + let mut validators = vec![]; + for _ in 0..5 { + let (mut v, _) = ValidatorRecord::zero_with_thread_rand_keypair(); + v.status = ValidatorStatus::Active as u8; + validators.push(v); + } + + let r = get_registration(); + + let result = do_induction(&r, &mut validators, 0, 1024); + + assert_eq!(result.unwrap(), 5); + assert_eq!(r, validators[5]); + assert_eq!(validators.len(), 6); + } + + #[test] + fn test_validator_inductor_valid_all_second_validator_withdrawn() { + let mut validators = vec![]; + let (mut v, _) = ValidatorRecord::zero_with_thread_rand_keypair(); + v.status = ValidatorStatus::Active as u8; + validators.push(v); + for _ in 0..4 { + let (mut v, _) = ValidatorRecord::zero_with_thread_rand_keypair(); + v.status = ValidatorStatus::Withdrawn as u8; + validators.push(v); + } + + let r = get_registration(); + + let result = do_induction(&r, &mut validators, 0, 1024); + + assert_eq!(result.unwrap(), 1); + assert_eq!(r, validators[1]); + assert_eq!(validators.len(), 5); + } + + #[test] + fn test_validator_inductor_valid_all_withdrawn_validators() { + let mut validators = vec![]; + for _ in 0..5 { + let (mut v, _) = ValidatorRecord::zero_with_thread_rand_keypair(); + v.status = ValidatorStatus::Withdrawn as u8; + validators.push(v); + } + + /* + * Ensure the first validator gets the 0'th slot + */ + let r = get_registration(); + let result = do_induction(&r, &mut validators, 0, 1024); + assert_eq!(result.unwrap(), 0); + assert_eq!(r, validators[0]); + assert_eq!(validators.len(), 5); + + /* + * Ensure the second validator gets the 1'st slot + */ + let r_two = get_registration(); + let result = do_induction(&r_two, &mut validators, 0, 1024); + assert_eq!(result.unwrap(), 1); + assert_eq!(r_two, validators[1]); + assert_eq!(validators.len(), 5); + } + + #[test] + fn test_validator_inductor_shard_too_high() { + let mut validators = vec![]; + + let mut r = get_registration(); + r.withdrawal_shard = 1025; + + let result = do_induction(&r, &mut validators, 0, 1024); + + assert_eq!(result, Err(ValidatorInductionError::InvalidShard)); + assert_eq!(validators.len(), 0); + } + + #[test] + fn test_validator_inductor_shard_proof_of_possession_failure() { + let mut validators = vec![]; + + let mut r = get_registration(); + let kp = Keypair::random(); + r.proof_of_possession = get_proof_of_possession(&kp); + + let result = do_induction(&r, &mut validators, 0, 1024); + + assert_eq!(result, Err(ValidatorInductionError::InvaidProofOfPossession)); + assert_eq!(validators.len(), 0); + } +} diff --git a/beacon_chain/validator_induction/src/lib.rs b/beacon_chain/validator_induction/src/lib.rs new file mode 100644 index 0000000000..6c5bf11a96 --- /dev/null +++ b/beacon_chain/validator_induction/src/lib.rs @@ -0,0 +1,12 @@ +extern crate bls; +extern crate hashing; +extern crate types; + +mod inductor; +mod proof_of_possession; +mod registration; + +pub use inductor::{ + ValidatorInductor, + ValidatorInductionError, +}; diff --git a/beacon_chain/validator_induction/src/proof_of_possession.rs b/beacon_chain/validator_induction/src/proof_of_possession.rs new file mode 100644 index 0000000000..85719c0147 --- /dev/null +++ b/beacon_chain/validator_induction/src/proof_of_possession.rs @@ -0,0 +1,14 @@ +use bls::{ + Signature, + PublicKey, +}; +use hashing::proof_of_possession_hash; + +/// For some signature and public key, ensure that the signature message was the public key and it +/// was signed by the secret key that corresponds to that public key. +pub fn verify_proof_of_possession(sig: &Signature, pubkey: &PublicKey) + -> bool +{ + let hash = proof_of_possession_hash(&pubkey.as_bytes()); + sig.verify_hashed(&hash, &pubkey) +} diff --git a/beacon_chain/validator_induction/src/registration.rs b/beacon_chain/validator_induction/src/registration.rs new file mode 100644 index 0000000000..0cb62c6bd9 --- /dev/null +++ b/beacon_chain/validator_induction/src/registration.rs @@ -0,0 +1,105 @@ +use bls::{ + PublicKey, + Signature, +}; +use types::{ + Address, + Hash256, + ValidatorRecord, +}; + +use super::proof_of_possession::verify_proof_of_possession; + + +/// The information gathered from the PoW chain validator registration function. +#[derive(Debug, Clone, PartialEq)] +pub struct ValidatorRegistration { + pub pubkey: PublicKey, + pub withdrawal_shard: u16, + pub withdrawal_address: Address, + pub randao_commitment: Hash256, + pub proof_of_possession: Signature, +} + +impl PartialEq for ValidatorRegistration { + fn eq(&self, v: &ValidatorRecord) -> bool { + (self.pubkey == v.pubkey) & + (self.withdrawal_shard == v.withdrawal_shard) & + (self.withdrawal_address == v.withdrawal_address) & + (self.randao_commitment == v.randao_commitment) & + (verify_proof_of_possession(&self.proof_of_possession, &v.pubkey)) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + use bls::{ + Keypair, + Signature, + }; + use types::{ + Address, + Hash256, + ValidatorRecord, + }; + use hashing::proof_of_possession_hash; + + fn get_proof_of_possession(kp: &Keypair) -> Signature { + let pop_message = proof_of_possession_hash(&kp.pk.as_bytes()); + Signature::new_hashed(&pop_message, &kp.sk) + } + + fn get_equal_validator_registrations_and_records() + -> (ValidatorRegistration, ValidatorRecord) + { + let kp = Keypair::random(); + let rego = ValidatorRegistration { + pubkey: kp.pk.clone(), + withdrawal_shard: 0, + withdrawal_address: Address::zero(), + randao_commitment: Hash256::zero(), + proof_of_possession: get_proof_of_possession(&kp), + }; + let record = ValidatorRecord { + pubkey: rego.pubkey.clone(), + withdrawal_shard: rego.withdrawal_shard, + withdrawal_address: rego.withdrawal_address.clone(), + randao_commitment: rego.randao_commitment.clone(), + randao_last_change: 0, + balance: 0, + status: 0, + exit_slot: 0, + }; + (rego, record) + } + + #[test] + fn test_validator_registration_and_record_partial_eq() { + let (rego, record) = get_equal_validator_registrations_and_records(); + assert!(rego == record); + + let (mut rego, record) = get_equal_validator_registrations_and_records(); + let kp = Keypair::random(); + rego.pubkey = kp.pk.clone(); + assert!(rego != record); + + let (mut rego, record) = get_equal_validator_registrations_and_records(); + rego.withdrawal_shard = record.withdrawal_shard + 1; + assert!(rego != record); + + let (mut rego, record) = get_equal_validator_registrations_and_records(); + rego.withdrawal_address = Address::from(42); + assert!(rego != record); + + let (mut rego, record) = get_equal_validator_registrations_and_records(); + rego.randao_commitment = Hash256::from(42); + assert!(rego != record); + + let (mut rego, record) = get_equal_validator_registrations_and_records(); + let kp = Keypair::random(); + rego.proof_of_possession = get_proof_of_possession(&kp); + assert!(rego != record); + } +} From 99ce9a2dd268af51a5f2531a57ae63667bd32ec6 Mon Sep 17 00:00:00 2001 From: Paul Hauner Date: Sat, 20 Oct 2018 19:18:16 +1100 Subject: [PATCH 3/8] Add new method for ValidatorInductor --- .../validator_induction/src/inductor.rs | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/beacon_chain/validator_induction/src/inductor.rs b/beacon_chain/validator_induction/src/inductor.rs index 15d2c44494..b18231c874 100644 --- a/beacon_chain/validator_induction/src/inductor.rs +++ b/beacon_chain/validator_induction/src/inductor.rs @@ -24,6 +24,17 @@ pub enum ValidatorInductionError { } impl<'a> ValidatorInductor<'a> { + pub fn new(current_slot: u64, shard_count: u16, validators: &'a mut Vec) + -> Self + { + Self { + current_slot, + shard_count, + validators, + empty_validator_start: 0, + } + } + /// Attempt to induct a validator into the CrystallizedState. /// /// Returns an error if the registration is invalid, otherwise returns the index of the @@ -138,12 +149,10 @@ mod tests { shard_count: u16) -> Result { - let mut inductor = ValidatorInductor { + let mut inductor = ValidatorInductor::new( current_slot, shard_count, - empty_validator_start: 0, - validators, - }; + validators); inductor.induct(&validator_rego) } From a8179897a9db6d7a0ceccd5030d75bf5abfded37 Mon Sep 17 00:00:00 2001 From: Paul Hauner Date: Sat, 20 Oct 2018 21:01:35 +1100 Subject: [PATCH 4/8] Add to_ref method for inductor --- beacon_chain/validator_induction/src/inductor.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/beacon_chain/validator_induction/src/inductor.rs b/beacon_chain/validator_induction/src/inductor.rs index b18231c874..dca4421ae1 100644 --- a/beacon_chain/validator_induction/src/inductor.rs +++ b/beacon_chain/validator_induction/src/inductor.rs @@ -107,6 +107,12 @@ impl<'a> ValidatorInductor<'a> { } } } + + fn to_ref(self) + -> &'a Vec + { + self.validators + } } From f495ed845bf1049f81b0d1e285eb70cc2dc75b75 Mon Sep 17 00:00:00 2001 From: Paul Hauner Date: Sat, 20 Oct 2018 21:28:57 +1100 Subject: [PATCH 5/8] Change ValidatorInductor to own instead of borrow --- .../validator_induction/src/inductor.rs | 60 +++++++++---------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/beacon_chain/validator_induction/src/inductor.rs b/beacon_chain/validator_induction/src/inductor.rs index dca4421ae1..9b3027ed2a 100644 --- a/beacon_chain/validator_induction/src/inductor.rs +++ b/beacon_chain/validator_induction/src/inductor.rs @@ -10,10 +10,10 @@ use super::registration::ValidatorRegistration; pub const DEPOSIT_GWEI: u64 = 32_000_000_000; /// Inducts validators into a `CrystallizedState`. -pub struct ValidatorInductor<'a> { +pub struct ValidatorInductor { pub current_slot: u64, pub shard_count: u16, - validators: &'a mut Vec, + validators: Vec, empty_validator_start: usize, } @@ -23,8 +23,8 @@ pub enum ValidatorInductionError { InvaidProofOfPossession, } -impl<'a> ValidatorInductor<'a> { - pub fn new(current_slot: u64, shard_count: u16, validators: &'a mut Vec) +impl ValidatorInductor { + pub fn new(current_slot: u64, shard_count: u16, validators: Vec) -> Self { Self { @@ -108,8 +108,8 @@ impl<'a> ValidatorInductor<'a> { } } - fn to_ref(self) - -> &'a Vec + pub fn to_vec(self) + -> Vec { self.validators } @@ -148,27 +148,15 @@ mod tests { } } - /// Induct a validator using the ValidatorInductor, return the result. - fn do_induction(validator_rego: &ValidatorRegistration, - validators: &mut Vec, - current_slot: u64, - shard_count: u16) - -> Result - { - let mut inductor = ValidatorInductor::new( - current_slot, - shard_count, - validators); - inductor.induct(&validator_rego) - } - #[test] fn test_validator_inductor_valid_empty_validators() { - let mut validators = vec![]; + let validators = vec![]; let r = get_registration(); - let result = do_induction(&r, &mut validators, 0, 1024); + let mut inductor = ValidatorInductor::new(0, 1024, validators); + let result = inductor.induct(&r); + let validators = inductor.to_vec(); assert_eq!(result.unwrap(), 0); assert_eq!(r, validators[0]); @@ -186,7 +174,9 @@ mod tests { let r = get_registration(); - let result = do_induction(&r, &mut validators, 0, 1024); + let mut inductor = ValidatorInductor::new(0, 1024, validators); + let result = inductor.induct(&r); + let validators = inductor.to_vec(); assert_eq!(result.unwrap(), 5); assert_eq!(r, validators[5]); @@ -207,7 +197,9 @@ mod tests { let r = get_registration(); - let result = do_induction(&r, &mut validators, 0, 1024); + let mut inductor = ValidatorInductor::new(0, 1024, validators); + let result = inductor.induct(&r); + let validators = inductor.to_vec(); assert_eq!(result.unwrap(), 1); assert_eq!(r, validators[1]); @@ -227,7 +219,9 @@ mod tests { * Ensure the first validator gets the 0'th slot */ let r = get_registration(); - let result = do_induction(&r, &mut validators, 0, 1024); + let mut inductor = ValidatorInductor::new(0, 1024, validators); + let result = inductor.induct(&r); + let validators = inductor.to_vec(); assert_eq!(result.unwrap(), 0); assert_eq!(r, validators[0]); assert_eq!(validators.len(), 5); @@ -236,7 +230,9 @@ mod tests { * Ensure the second validator gets the 1'st slot */ let r_two = get_registration(); - let result = do_induction(&r_two, &mut validators, 0, 1024); + let mut inductor = ValidatorInductor::new(0, 1024, validators); + let result = inductor.induct(&r); + let validators = inductor.to_vec(); assert_eq!(result.unwrap(), 1); assert_eq!(r_two, validators[1]); assert_eq!(validators.len(), 5); @@ -244,12 +240,14 @@ mod tests { #[test] fn test_validator_inductor_shard_too_high() { - let mut validators = vec![]; + let validators = vec![]; let mut r = get_registration(); r.withdrawal_shard = 1025; - let result = do_induction(&r, &mut validators, 0, 1024); + let mut inductor = ValidatorInductor::new(0, 1024, validators); + let result = inductor.induct(&r); + let validators = inductor.to_vec(); assert_eq!(result, Err(ValidatorInductionError::InvalidShard)); assert_eq!(validators.len(), 0); @@ -257,13 +255,15 @@ mod tests { #[test] fn test_validator_inductor_shard_proof_of_possession_failure() { - let mut validators = vec![]; + let validators = vec![]; let mut r = get_registration(); let kp = Keypair::random(); r.proof_of_possession = get_proof_of_possession(&kp); - let result = do_induction(&r, &mut validators, 0, 1024); + let mut inductor = ValidatorInductor::new(0, 1024, validators); + let result = inductor.induct(&r); + let validators = inductor.to_vec(); assert_eq!(result, Err(ValidatorInductionError::InvaidProofOfPossession)); assert_eq!(validators.len(), 0); From 12b5d7434cdecc091996677858901b4511bfec68 Mon Sep 17 00:00:00 2001 From: Paul Hauner Date: Mon, 22 Oct 2018 05:22:16 +1100 Subject: [PATCH 6/8] Move ValidatorRegistration into types --- beacon_chain/types/src/lib.rs | 2 + .../validator_induction/src/inductor.rs | 25 +++-- beacon_chain/validator_induction/src/lib.rs | 5 +- .../src/proof_of_possession.rs | 10 +- .../validator_induction/src/registration.rs | 105 ------------------ 5 files changed, 32 insertions(+), 115 deletions(-) delete mode 100644 beacon_chain/validator_induction/src/registration.rs diff --git a/beacon_chain/types/src/lib.rs b/beacon_chain/types/src/lib.rs index a00e904e94..887a2b1cee 100644 --- a/beacon_chain/types/src/lib.rs +++ b/beacon_chain/types/src/lib.rs @@ -12,6 +12,7 @@ pub mod crosslink_record; pub mod shard_and_committee; pub mod special_record; pub mod validator_record; +pub mod validator_registration; use self::ethereum_types::{ H256, @@ -30,6 +31,7 @@ pub use crosslink_record::CrosslinkRecord; pub use shard_and_committee::ShardAndCommittee; pub use special_record::{ SpecialRecord, SpecialRecordKind }; pub use validator_record::{ ValidatorRecord, ValidatorStatus }; +pub use validator_registration::{ ValidatorRegistration }; pub type Hash256 = H256; pub type Address = H160; diff --git a/beacon_chain/validator_induction/src/inductor.rs b/beacon_chain/validator_induction/src/inductor.rs index 9b3027ed2a..75a32c2b5e 100644 --- a/beacon_chain/validator_induction/src/inductor.rs +++ b/beacon_chain/validator_induction/src/inductor.rs @@ -1,10 +1,10 @@ use types::{ ValidatorRecord, ValidatorStatus, + ValidatorRegistration, }; use super::proof_of_possession::verify_proof_of_possession; -use super::registration::ValidatorRegistration; /// The size of a validators deposit in GWei. pub const DEPOSIT_GWEI: u64 = 32_000_000_000; @@ -130,6 +130,16 @@ mod tests { }; use hashing::proof_of_possession_hash; + 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)) + } + /// Generate a proof of possession for some keypair. fn get_proof_of_possession(kp: &Keypair) -> Signature { let pop_message = proof_of_possession_hash(&kp.pk.as_bytes()); @@ -159,7 +169,7 @@ mod tests { let validators = inductor.to_vec(); assert_eq!(result.unwrap(), 0); - assert_eq!(r, validators[0]); + assert!(registration_equals_record(&r, &validators[0])); assert_eq!(validators.len(), 1); } @@ -179,7 +189,7 @@ mod tests { let validators = inductor.to_vec(); assert_eq!(result.unwrap(), 5); - assert_eq!(r, validators[5]); + assert!(registration_equals_record(&r, &validators[5])); assert_eq!(validators.len(), 6); } @@ -202,7 +212,7 @@ mod tests { let validators = inductor.to_vec(); assert_eq!(result.unwrap(), 1); - assert_eq!(r, validators[1]); + assert!(registration_equals_record(&r, &validators[1])); assert_eq!(validators.len(), 5); } @@ -223,18 +233,17 @@ mod tests { let result = inductor.induct(&r); let validators = inductor.to_vec(); assert_eq!(result.unwrap(), 0); - assert_eq!(r, validators[0]); - assert_eq!(validators.len(), 5); + 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); + let result = inductor.induct(&r_two); let validators = inductor.to_vec(); assert_eq!(result.unwrap(), 1); - assert_eq!(r_two, validators[1]); + assert!(registration_equals_record(&r_two, &validators[1])); assert_eq!(validators.len(), 5); } diff --git a/beacon_chain/validator_induction/src/lib.rs b/beacon_chain/validator_induction/src/lib.rs index 6c5bf11a96..f35548480e 100644 --- a/beacon_chain/validator_induction/src/lib.rs +++ b/beacon_chain/validator_induction/src/lib.rs @@ -4,9 +4,12 @@ extern crate types; mod inductor; mod proof_of_possession; -mod registration; pub use inductor::{ ValidatorInductor, ValidatorInductionError, }; + +pub use proof_of_possession::{ + create_proof_of_possession, +}; diff --git a/beacon_chain/validator_induction/src/proof_of_possession.rs b/beacon_chain/validator_induction/src/proof_of_possession.rs index 85719c0147..391625e851 100644 --- a/beacon_chain/validator_induction/src/proof_of_possession.rs +++ b/beacon_chain/validator_induction/src/proof_of_possession.rs @@ -1,6 +1,7 @@ use bls::{ - Signature, + Keypair, PublicKey, + Signature, }; use hashing::proof_of_possession_hash; @@ -12,3 +13,10 @@ pub fn verify_proof_of_possession(sig: &Signature, pubkey: &PublicKey) let hash = proof_of_possession_hash(&pubkey.as_bytes()); sig.verify_hashed(&hash, &pubkey) } + +pub fn create_proof_of_possession(keypair: &Keypair) + -> Signature +{ + let hash = proof_of_possession_hash(&keypair.pk.as_bytes()); + Signature::new_hashed(&hash, &keypair.sk) +} diff --git a/beacon_chain/validator_induction/src/registration.rs b/beacon_chain/validator_induction/src/registration.rs deleted file mode 100644 index 0cb62c6bd9..0000000000 --- a/beacon_chain/validator_induction/src/registration.rs +++ /dev/null @@ -1,105 +0,0 @@ -use bls::{ - PublicKey, - Signature, -}; -use types::{ - Address, - Hash256, - ValidatorRecord, -}; - -use super::proof_of_possession::verify_proof_of_possession; - - -/// The information gathered from the PoW chain validator registration function. -#[derive(Debug, Clone, PartialEq)] -pub struct ValidatorRegistration { - pub pubkey: PublicKey, - pub withdrawal_shard: u16, - pub withdrawal_address: Address, - pub randao_commitment: Hash256, - pub proof_of_possession: Signature, -} - -impl PartialEq for ValidatorRegistration { - fn eq(&self, v: &ValidatorRecord) -> bool { - (self.pubkey == v.pubkey) & - (self.withdrawal_shard == v.withdrawal_shard) & - (self.withdrawal_address == v.withdrawal_address) & - (self.randao_commitment == v.randao_commitment) & - (verify_proof_of_possession(&self.proof_of_possession, &v.pubkey)) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - use bls::{ - Keypair, - Signature, - }; - use types::{ - Address, - Hash256, - ValidatorRecord, - }; - use hashing::proof_of_possession_hash; - - fn get_proof_of_possession(kp: &Keypair) -> Signature { - let pop_message = proof_of_possession_hash(&kp.pk.as_bytes()); - Signature::new_hashed(&pop_message, &kp.sk) - } - - fn get_equal_validator_registrations_and_records() - -> (ValidatorRegistration, ValidatorRecord) - { - let kp = Keypair::random(); - let rego = ValidatorRegistration { - pubkey: kp.pk.clone(), - withdrawal_shard: 0, - withdrawal_address: Address::zero(), - randao_commitment: Hash256::zero(), - proof_of_possession: get_proof_of_possession(&kp), - }; - let record = ValidatorRecord { - pubkey: rego.pubkey.clone(), - withdrawal_shard: rego.withdrawal_shard, - withdrawal_address: rego.withdrawal_address.clone(), - randao_commitment: rego.randao_commitment.clone(), - randao_last_change: 0, - balance: 0, - status: 0, - exit_slot: 0, - }; - (rego, record) - } - - #[test] - fn test_validator_registration_and_record_partial_eq() { - let (rego, record) = get_equal_validator_registrations_and_records(); - assert!(rego == record); - - let (mut rego, record) = get_equal_validator_registrations_and_records(); - let kp = Keypair::random(); - rego.pubkey = kp.pk.clone(); - assert!(rego != record); - - let (mut rego, record) = get_equal_validator_registrations_and_records(); - rego.withdrawal_shard = record.withdrawal_shard + 1; - assert!(rego != record); - - let (mut rego, record) = get_equal_validator_registrations_and_records(); - rego.withdrawal_address = Address::from(42); - assert!(rego != record); - - let (mut rego, record) = get_equal_validator_registrations_and_records(); - rego.randao_commitment = Hash256::from(42); - assert!(rego != record); - - let (mut rego, record) = get_equal_validator_registrations_and_records(); - let kp = Keypair::random(); - rego.proof_of_possession = get_proof_of_possession(&kp); - assert!(rego != record); - } -} From 7eac75fcf6af20ae5920da9abefa98d99dce1c16 Mon Sep 17 00:00:00 2001 From: Paul Hauner Date: Mon, 22 Oct 2018 05:51:31 +1100 Subject: [PATCH 7/8] Add missed file --- .../types/src/validator_registration.rs | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 beacon_chain/types/src/validator_registration.rs diff --git a/beacon_chain/types/src/validator_registration.rs b/beacon_chain/types/src/validator_registration.rs new file mode 100644 index 0000000000..1ebb90eec1 --- /dev/null +++ b/beacon_chain/types/src/validator_registration.rs @@ -0,0 +1,24 @@ +use bls::{ + Keypair, + PublicKey, + Signature, +}; +use super::{ + Address, + Hash256, +}; + + +/// The information gathered from the PoW chain validator registration function. +#[derive(Debug, Clone, PartialEq)] +pub struct ValidatorRegistration { + pub pubkey: PublicKey, + pub withdrawal_shard: u16, + pub withdrawal_address: Address, + pub randao_commitment: Hash256, + pub proof_of_possession: Signature, +} + +impl ValidatorRegistration { + pub fn random() +} From 8a2baa7b260628d7a2fa841e877253ba6d303a96 Mon Sep 17 00:00:00 2001 From: Paul Hauner Date: Mon, 22 Oct 2018 05:58:12 +1100 Subject: [PATCH 8/8] Move proof_of_possession into bls crate --- .../types/src/validator_registration.rs | 4 ---- beacon_chain/utils/bls/Cargo.toml | 1 + beacon_chain/utils/bls/src/lib.rs | 19 ++++++++++++++++ .../validator_induction/src/inductor.rs | 5 +++-- beacon_chain/validator_induction/src/lib.rs | 5 ----- .../src/proof_of_possession.rs | 22 ------------------- 6 files changed, 23 insertions(+), 33 deletions(-) delete mode 100644 beacon_chain/validator_induction/src/proof_of_possession.rs diff --git a/beacon_chain/types/src/validator_registration.rs b/beacon_chain/types/src/validator_registration.rs index 1ebb90eec1..384f2dc729 100644 --- a/beacon_chain/types/src/validator_registration.rs +++ b/beacon_chain/types/src/validator_registration.rs @@ -18,7 +18,3 @@ pub struct ValidatorRegistration { pub randao_commitment: Hash256, pub proof_of_possession: Signature, } - -impl ValidatorRegistration { - pub fn random() -} diff --git a/beacon_chain/utils/bls/Cargo.toml b/beacon_chain/utils/bls/Cargo.toml index eef0ec0aac..1199efc154 100644 --- a/beacon_chain/utils/bls/Cargo.toml +++ b/beacon_chain/utils/bls/Cargo.toml @@ -5,3 +5,4 @@ authors = ["Paul Hauner "] [dependencies] bls-aggregates = { git = "https://github.com/sigp/signature-schemes" } +hashing = { path = "../hashing" } diff --git a/beacon_chain/utils/bls/src/lib.rs b/beacon_chain/utils/bls/src/lib.rs index ff9a0919aa..0578eb0959 100644 --- a/beacon_chain/utils/bls/src/lib.rs +++ b/beacon_chain/utils/bls/src/lib.rs @@ -1,4 +1,5 @@ extern crate bls_aggregates; +extern crate hashing; pub use self::bls_aggregates::AggregateSignature; pub use self::bls_aggregates::AggregatePublicKey; @@ -8,3 +9,21 @@ pub use self::bls_aggregates::PublicKey; pub use self::bls_aggregates::SecretKey; pub const BLS_AGG_SIG_BYTE_SIZE: usize = 97; + +use hashing::proof_of_possession_hash; + +/// For some signature and public key, ensure that the signature message was the public key and it +/// was signed by the secret key that corresponds to that public key. +pub fn verify_proof_of_possession(sig: &Signature, pubkey: &PublicKey) + -> bool +{ + let hash = proof_of_possession_hash(&pubkey.as_bytes()); + sig.verify_hashed(&hash, &pubkey) +} + +pub fn create_proof_of_possession(keypair: &Keypair) + -> Signature +{ + let hash = proof_of_possession_hash(&keypair.pk.as_bytes()); + Signature::new_hashed(&hash, &keypair.sk) +} diff --git a/beacon_chain/validator_induction/src/inductor.rs b/beacon_chain/validator_induction/src/inductor.rs index 75a32c2b5e..e025d0d437 100644 --- a/beacon_chain/validator_induction/src/inductor.rs +++ b/beacon_chain/validator_induction/src/inductor.rs @@ -1,11 +1,12 @@ +use bls::{ + verify_proof_of_possession, +}; use types::{ ValidatorRecord, ValidatorStatus, ValidatorRegistration, }; -use super::proof_of_possession::verify_proof_of_possession; - /// The size of a validators deposit in GWei. pub const DEPOSIT_GWEI: u64 = 32_000_000_000; diff --git a/beacon_chain/validator_induction/src/lib.rs b/beacon_chain/validator_induction/src/lib.rs index f35548480e..e1f5ef09d1 100644 --- a/beacon_chain/validator_induction/src/lib.rs +++ b/beacon_chain/validator_induction/src/lib.rs @@ -3,13 +3,8 @@ extern crate hashing; extern crate types; mod inductor; -mod proof_of_possession; pub use inductor::{ ValidatorInductor, ValidatorInductionError, }; - -pub use proof_of_possession::{ - create_proof_of_possession, -}; diff --git a/beacon_chain/validator_induction/src/proof_of_possession.rs b/beacon_chain/validator_induction/src/proof_of_possession.rs deleted file mode 100644 index 391625e851..0000000000 --- a/beacon_chain/validator_induction/src/proof_of_possession.rs +++ /dev/null @@ -1,22 +0,0 @@ -use bls::{ - Keypair, - PublicKey, - Signature, -}; -use hashing::proof_of_possession_hash; - -/// For some signature and public key, ensure that the signature message was the public key and it -/// was signed by the secret key that corresponds to that public key. -pub fn verify_proof_of_possession(sig: &Signature, pubkey: &PublicKey) - -> bool -{ - let hash = proof_of_possession_hash(&pubkey.as_bytes()); - sig.verify_hashed(&hash, &pubkey) -} - -pub fn create_proof_of_possession(keypair: &Keypair) - -> Signature -{ - let hash = proof_of_possession_hash(&keypair.pk.as_bytes()); - Signature::new_hashed(&hash, &keypair.sk) -}