diff --git a/beacon_node/beacon_chain/src/attestation_aggregator.rs b/beacon_node/beacon_chain/src/attestation_aggregator.rs index 149f0d60d..e8576276c 100644 --- a/beacon_node/beacon_chain/src/attestation_aggregator.rs +++ b/beacon_node/beacon_chain/src/attestation_aggregator.rs @@ -112,7 +112,7 @@ impl AttestationAggregator { if !free_attestation .signature - .verify(&signable_message, &validator_record.pubkey) + .verify(&signable_message, spec.domain_attestation, &validator_record.pubkey) { return Ok(Outcome { valid: false, diff --git a/beacon_node/beacon_chain/test_harness/src/validator_harness/local_signer.rs b/beacon_node/beacon_chain/test_harness/src/validator_harness/local_signer.rs index 8e901b057..f176a6889 100644 --- a/beacon_node/beacon_chain/test_harness/src/validator_harness/local_signer.rs +++ b/beacon_node/beacon_chain/test_harness/src/validator_harness/local_signer.rs @@ -25,23 +25,23 @@ impl LocalSigner { } /// Sign some message. - fn bls_sign(&self, message: &[u8]) -> Option { - Some(Signature::new(message, &self.keypair.sk)) + fn bls_sign(&self, message: &[u8], domain: u64) -> Option { + Some(Signature::new(message, domain, &self.keypair.sk)) } } impl BlockProposerSigner for LocalSigner { - fn sign_block_proposal(&self, message: &[u8]) -> Option { - self.bls_sign(message) + fn sign_block_proposal(&self, message: &[u8], domain: u64) -> Option { + self.bls_sign(message, domain) } - fn sign_randao_reveal(&self, message: &[u8]) -> Option { - self.bls_sign(message) + fn sign_randao_reveal(&self, message: &[u8], domain: u64) -> Option { + self.bls_sign(message, domain) } } impl AttesterSigner for LocalSigner { - fn sign_attestation_message(&self, message: &[u8]) -> Option { - self.bls_sign(message) + fn sign_attestation_message(&self, message: &[u8], domain: u64) -> Option { + self.bls_sign(message, domain) } } diff --git a/eth2/attester/src/lib.rs b/eth2/attester/src/lib.rs index 7352dd2ea..f2bbd6db3 100644 --- a/eth2/attester/src/lib.rs +++ b/eth2/attester/src/lib.rs @@ -10,6 +10,7 @@ pub use self::traits::{ }; const PHASE_0_CUSTODY_BIT: bool = false; +const DOMAIN_ATTESTATION: u64 = 1; #[derive(Debug, PartialEq)] pub enum PollOutcome { @@ -137,7 +138,7 @@ impl Attester Option { - Some(Signature::new(message, &self.keypair.sk)) + fn sign_attestation_message(&self, message: &[u8], domain: u64) -> Option { + Some(Signature::new(message, domain, &self.keypair.sk)) } } diff --git a/eth2/attester/src/traits.rs b/eth2/attester/src/traits.rs index 53bce3aaa..6062460cb 100644 --- a/eth2/attester/src/traits.rs +++ b/eth2/attester/src/traits.rs @@ -45,5 +45,5 @@ pub trait DutiesReader: Send + Sync { /// Signs message using an internally-maintained private key. pub trait Signer { - fn sign_attestation_message(&self, message: &[u8]) -> Option; + fn sign_attestation_message(&self, message: &[u8], domain: u64) -> Option; } diff --git a/eth2/block_producer/src/lib.rs b/eth2/block_producer/src/lib.rs index f6a0fd6df..e5651780a 100644 --- a/eth2/block_producer/src/lib.rs +++ b/eth2/block_producer/src/lib.rs @@ -134,7 +134,7 @@ impl BlockProducer return Ok(PollOutcome::SignerRejection(slot)), Some(signature) => signature, } @@ -168,7 +168,7 @@ impl BlockProducer None, Some(signature) => { diff --git a/eth2/block_producer/src/test_utils/local_signer.rs b/eth2/block_producer/src/test_utils/local_signer.rs index 0ebefa29d..d7f490c30 100644 --- a/eth2/block_producer/src/test_utils/local_signer.rs +++ b/eth2/block_producer/src/test_utils/local_signer.rs @@ -25,11 +25,11 @@ impl LocalSigner { } impl Signer for LocalSigner { - fn sign_block_proposal(&self, message: &[u8]) -> Option { - Some(Signature::new(message, &self.keypair.sk)) + fn sign_block_proposal(&self, message: &[u8], domain: u64) -> Option { + Some(Signature::new(message, domain, &self.keypair.sk)) } - fn sign_randao_reveal(&self, message: &[u8]) -> Option { - Some(Signature::new(message, &self.keypair.sk)) + fn sign_randao_reveal(&self, message: &[u8], domain: u64) -> Option { + Some(Signature::new(message, domain, &self.keypair.sk)) } } diff --git a/eth2/block_producer/src/traits.rs b/eth2/block_producer/src/traits.rs index 5eb27bce7..c6e57d833 100644 --- a/eth2/block_producer/src/traits.rs +++ b/eth2/block_producer/src/traits.rs @@ -44,6 +44,6 @@ pub trait DutiesReader: Send + Sync { /// Signs message using an internally-maintained private key. pub trait Signer { - fn sign_block_proposal(&self, message: &[u8]) -> Option; - fn sign_randao_reveal(&self, message: &[u8]) -> Option; + fn sign_block_proposal(&self, message: &[u8], domain: u64) -> Option; + fn sign_randao_reveal(&self, message: &[u8], domain: u64) -> Option; } diff --git a/eth2/fork_choice/src/optimised_lmd_ghost.rs b/eth2/fork_choice/src/optimised_lmd_ghost.rs index 7104834cb..6b73c2a8f 100644 --- a/eth2/fork_choice/src/optimised_lmd_ghost.rs +++ b/eth2/fork_choice/src/optimised_lmd_ghost.rs @@ -31,7 +31,8 @@ use std::collections::HashMap; use std::sync::Arc; use types::{ readers::BeaconBlockReader, - slot_epoch_height::{Height, Slot}, + slot_epoch::Slot, + slot_height::SlotHeight, validator_registry::get_active_validator_indices, BeaconBlock, Hash256, }; @@ -77,7 +78,7 @@ pub struct OptimisedLMDGhost { block_store: Arc>, /// State storage access. state_store: Arc>, - max_known_height: Height, + max_known_height: SlotHeight, } impl OptimisedLMDGhost @@ -93,7 +94,7 @@ where ancestors: vec![HashMap::new(); 16], latest_attestation_targets: HashMap::new(), children: HashMap::new(), - max_known_height: Height::new(0), + max_known_height: SlotHeight::new(0), block_store, state_store, } @@ -137,7 +138,7 @@ where } /// Gets the ancestor at a given height `at_height` of a block specified by `block_hash`. - fn get_ancestor(&mut self, block_hash: Hash256, at_height: Height) -> Option { + fn get_ancestor(&mut self, block_hash: Hash256, at_height: SlotHeight) -> Option { // return None if we can't get the block from the db. let block_height = { let block_slot = self @@ -186,7 +187,7 @@ where fn get_clear_winner( &mut self, latest_votes: &HashMap, - block_height: Height, + block_height: SlotHeight, ) -> Option { // map of vote counts for every hash at this height let mut current_votes: HashMap = HashMap::new(); diff --git a/eth2/fork_choice/src/slow_lmd_ghost.rs b/eth2/fork_choice/src/slow_lmd_ghost.rs index e0e347cef..44b429fa8 100644 --- a/eth2/fork_choice/src/slow_lmd_ghost.rs +++ b/eth2/fork_choice/src/slow_lmd_ghost.rs @@ -29,7 +29,7 @@ use std::collections::HashMap; use std::sync::Arc; use types::{ readers::{BeaconBlockReader, BeaconStateReader}, - slot_epoch_height::Slot, + slot_epoch::Slot, validator_registry::get_active_validator_indices, BeaconBlock, Hash256, }; diff --git a/eth2/state_processing/src/block_processable.rs b/eth2/state_processing/src/block_processable.rs index f043a723d..1bf6022ec 100644 --- a/eth2/state_processing/src/block_processable.rs +++ b/eth2/state_processing/src/block_processable.rs @@ -374,14 +374,12 @@ fn validate_attestation_signature_optional( Ok(()) } -fn get_domain(_fork: &Fork, _epoch: Epoch, _domain_type: u64) -> u64 { - // TODO: stubbed out. - 0 +fn get_domain(fork: &Fork, epoch: Epoch, domain_type: u64) -> u64 { + fork.get_domain(epoch, domain_type) } -fn bls_verify(pubkey: &PublicKey, message: &[u8], signature: &Signature, _domain: u64) -> bool { - // TODO: add domain - signature.verify(message, pubkey) +fn bls_verify(pubkey: &PublicKey, message: &[u8], signature: &Signature, domain: u64) -> bool { + signature.verify(message, domain, pubkey) } impl From for Error { diff --git a/eth2/types/src/attestation.rs b/eth2/types/src/attestation.rs index eb375d490..be0b12d9e 100644 --- a/eth2/types/src/attestation.rs +++ b/eth2/types/src/attestation.rs @@ -25,11 +25,10 @@ impl Attestation { &self, group_public_key: &AggregatePublicKey, custody_bit: bool, - // TODO: use domain. - _domain: u64, + domain: u64, ) -> bool { self.aggregate_signature - .verify(&self.signable_message(custody_bit), group_public_key) + .verify(&self.signable_message(custody_bit), domain, group_public_key) } } diff --git a/eth2/types/src/beacon_state.rs b/eth2/types/src/beacon_state.rs index ed53bfea9..278569609 100644 --- a/eth2/types/src/beacon_state.rs +++ b/eth2/types/src/beacon_state.rs @@ -1,10 +1,9 @@ use crate::test_utils::TestRandom; use crate::{ validator::StatusFlags, validator_registry::get_active_validator_indices, AttestationData, - Bitfield, ChainSpec, Crosslink, Deposit, Epoch, Eth1Data, Eth1DataVote, Fork, Hash256, + Bitfield, ChainSpec, Crosslink, Deposit, DepositInput, Epoch, Eth1Data, Eth1DataVote, Fork, Hash256, PendingAttestation, PublicKey, Signature, Slot, Validator, }; -use bls::verify_proof_of_possession; use honey_badger_split::SplitExt; use rand::RngCore; use serde_derive::Serialize; @@ -587,6 +586,32 @@ impl BeaconState { self.validator_registry_update_epoch = current_epoch; } + + /// Confirm validator owns PublicKey + pub fn validate_proof_of_possession( + &self, + pubkey: PublicKey, + proof_of_possession: Signature, + withdrawal_credentials: Hash256, + spec: &ChainSpec + ) -> bool { + let proof_of_possession_data = DepositInput { + pubkey: pubkey.clone(), + withdrawal_credentials, + proof_of_possession: proof_of_possession.clone(), + }; + + proof_of_possession.verify( + &proof_of_possession_data.hash_tree_root(), + self.fork.get_domain( + self.slot.epoch(spec.epoch_length), + spec.domain_deposit, + ), + &pubkey, + ) + } + + /// Process a validator deposit, returning the validator index if the deposit is valid. /// /// Spec v0.2.0 @@ -598,8 +623,7 @@ impl BeaconState { withdrawal_credentials: Hash256, spec: &ChainSpec, ) -> Result { - // TODO: ensure verify proof-of-possession represents the spec accurately. - if !verify_proof_of_possession(&proof_of_possession, &pubkey) { + if !self.validate_proof_of_possession(pubkey.clone(), proof_of_possession, withdrawal_credentials, &spec) { return Err(()); } diff --git a/eth2/types/src/fork.rs b/eth2/types/src/fork.rs index 1c96a34ac..c103a2653 100644 --- a/eth2/types/src/fork.rs +++ b/eth2/types/src/fork.rs @@ -10,6 +10,22 @@ pub struct Fork { pub epoch: Epoch, } +impl Fork { + /// Return the fork version of the given ``epoch``. + pub fn get_fork_version(&self, epoch: Epoch) -> u64 { + if epoch < self.epoch { + return self.previous_version; + } + self.current_version + } + + /// Get the domain number that represents the fork meta and signature domain. + pub fn get_domain(&self, epoch: Epoch, domain_type: u64) -> u64 { + let fork_version = self.get_fork_version(epoch); + fork_version * u64::pow(2,32) + domain_type + } +} + impl Encodable for Fork { fn ssz_append(&self, s: &mut SszStream) { s.append(&self.previous_version); diff --git a/eth2/types/src/test_utils/signature.rs b/eth2/types/src/test_utils/signature.rs index 9ec7aec60..d9995835a 100644 --- a/eth2/types/src/test_utils/signature.rs +++ b/eth2/types/src/test_utils/signature.rs @@ -8,6 +8,6 @@ impl TestRandom for Signature { let mut message = vec![0; 32]; rng.fill_bytes(&mut message); - Signature::new(&message, &secret_key) + Signature::new(&message, 0, &secret_key) } } diff --git a/eth2/utils/bls/Cargo.toml b/eth2/utils/bls/Cargo.toml index 465510c59..c8204ca7a 100644 --- a/eth2/utils/bls/Cargo.toml +++ b/eth2/utils/bls/Cargo.toml @@ -5,7 +5,7 @@ authors = ["Paul Hauner "] edition = "2018" [dependencies] -bls-aggregates = { git = "https://github.com/sigp/signature-schemes", tag = "v0.3.0" } +bls-aggregates = { git = "https://github.com/sigp/signature-schemes", tag = "0.4.1" } hashing = { path = "../hashing" } hex = "0.3" serde = "1.0" diff --git a/eth2/utils/bls/src/aggregate_signature.rs b/eth2/utils/bls/src/aggregate_signature.rs index 6fed183f0..b684c2b5b 100644 --- a/eth2/utils/bls/src/aggregate_signature.rs +++ b/eth2/utils/bls/src/aggregate_signature.rs @@ -27,8 +27,8 @@ impl AggregateSignature { /// /// Only returns `true` if the set of keys in the `AggregatePublicKey` match the set of keys /// that signed the `AggregateSignature`. - pub fn verify(&self, msg: &[u8], aggregate_public_key: &AggregatePublicKey) -> bool { - self.0.verify(msg, aggregate_public_key) + pub fn verify(&self, msg: &[u8], domain: u64, aggregate_public_key: &AggregatePublicKey) -> bool { + self.0.verify(msg, domain, aggregate_public_key) } } @@ -73,7 +73,7 @@ mod tests { let keypair = Keypair::random(); let mut original = AggregateSignature::new(); - original.add(&Signature::new(&[42, 42], &keypair.sk)); + original.add(&Signature::new(&[42, 42], 0, &keypair.sk)); let bytes = ssz_encode(&original); let (decoded, _) = AggregateSignature::ssz_decode(&bytes, 0).unwrap(); diff --git a/eth2/utils/bls/src/lib.rs b/eth2/utils/bls/src/lib.rs index 646047d18..39d4a95f2 100644 --- a/eth2/utils/bls/src/lib.rs +++ b/eth2/utils/bls/src/lib.rs @@ -29,24 +29,18 @@ fn extend_if_needed(hash: &mut Vec) { /// 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 mut hash = hash(&ssz_encode(pubkey)); - extend_if_needed(&mut hash); - sig.verify_hashed(&hash, &pubkey) -} + pub fn create_proof_of_possession(keypair: &Keypair) -> Signature { - let mut hash = hash(&ssz_encode(&keypair.pk)); - extend_if_needed(&mut hash); - Signature::new_hashed(&hash, &keypair.sk) + Signature::new(&ssz_encode(&keypair.pk), 0, &keypair.sk) } pub fn bls_verify_aggregate( pubkey: &AggregatePublicKey, message: &[u8], signature: &AggregateSignature, - _domain: u64, + domain: u64, ) -> bool { // TODO: add domain - signature.verify(message, pubkey) + signature.verify(message, domain, pubkey) } diff --git a/eth2/utils/bls/src/signature.rs b/eth2/utils/bls/src/signature.rs index 396e4eab7..61440498e 100644 --- a/eth2/utils/bls/src/signature.rs +++ b/eth2/utils/bls/src/signature.rs @@ -14,24 +14,24 @@ pub struct Signature(RawSignature); impl Signature { /// Instantiate a new Signature from a message and a SecretKey. - pub fn new(msg: &[u8], sk: &SecretKey) -> Self { - Signature(RawSignature::new(msg, sk.as_raw())) + pub fn new(msg: &[u8], domain: u64, sk: &SecretKey) -> Self { + Signature(RawSignature::new(msg, domain, sk.as_raw())) } /// Instantiate a new Signature from a message and a SecretKey, where the message has already /// been hashed. - pub fn new_hashed(msg_hashed: &[u8], sk: &SecretKey) -> Self { - Signature(RawSignature::new_hashed(msg_hashed, sk.as_raw())) + pub fn new_hashed(x_real_hashed: &[u8], x_imaginary_hashed: &[u8], sk: &SecretKey) -> Self { + Signature(RawSignature::new_hashed(x_real_hashed, x_imaginary_hashed, sk.as_raw())) } /// Verify the Signature against a PublicKey. - pub fn verify(&self, msg: &[u8], pk: &PublicKey) -> bool { - self.0.verify(msg, pk.as_raw()) + pub fn verify(&self, msg: &[u8], domain: u64, pk: &PublicKey) -> bool { + self.0.verify(msg, domain, pk.as_raw()) } /// Verify the Signature against a PublicKey, where the message has already been hashed. - pub fn verify_hashed(&self, msg_hash: &[u8], pk: &PublicKey) -> bool { - self.0.verify_hashed(msg_hash, pk.as_raw()) + pub fn verify_hashed(&self, x_real_hashed: &[u8], x_imaginary_hashed: &[u8], pk: &PublicKey) -> bool { + self.0.verify_hashed(x_real_hashed, x_imaginary_hashed, pk.as_raw()) } /// Returns the underlying signature. @@ -41,7 +41,7 @@ impl Signature { /// Returns a new empty signature. pub fn empty_signature() -> Self { - let empty: Vec = vec![0; 97]; + let empty: Vec = vec![0; 96]; Signature(RawSignature::from_bytes(&empty).unwrap()) } } @@ -85,7 +85,7 @@ mod tests { pub fn test_ssz_round_trip() { let keypair = Keypair::random(); - let original = Signature::new(&[42, 42], &keypair.sk); + let original = Signature::new(&[42, 42], 0, &keypair.sk); let bytes = ssz_encode(&original); let (decoded, _) = Signature::ssz_decode(&bytes, 0).unwrap();