diff --git a/beacon_node/beacon_chain/src/attestation_aggregator.rs b/beacon_node/beacon_chain/src/attestation_aggregator.rs index 70348dc945..54f178068d 100644 --- a/beacon_node/beacon_chain/src/attestation_aggregator.rs +++ b/beacon_node/beacon_chain/src/attestation_aggregator.rs @@ -129,10 +129,13 @@ impl AttestationAggregator { Some(validator_record) => validator_record, }; - if !free_attestation - .signature - .verify(&signable_message, &validator_record.pubkey) - { + if !free_attestation.signature.verify( + &signable_message, + cached_state + .fork + .get_domain(cached_state.current_epoch(spec), spec.domain_attestation), + &validator_record.pubkey, + ) { invalid_outcome!(Message::BadSignature); } 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 aa46a1c9a4..3f249cb199 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 7352dd2ea8..13a1d72bb9 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 { @@ -136,8 +137,10 @@ impl Attester Option { self.store_produce(attestation_data); - self.signer - .sign_attestation_message(&attestation_data.signable_message(PHASE_0_CUSTODY_BIT)[..]) + self.signer.sign_attestation_message( + &attestation_data.signable_message(PHASE_0_CUSTODY_BIT)[..], + DOMAIN_ATTESTATION, + ) } /// Returns `true` if signing some attestation_data is safe (non-slashable). diff --git a/eth2/attester/src/test_utils/local_signer.rs b/eth2/attester/src/test_utils/local_signer.rs index c256d10506..896d907752 100644 --- a/eth2/attester/src/test_utils/local_signer.rs +++ b/eth2/attester/src/test_utils/local_signer.rs @@ -25,7 +25,7 @@ impl LocalSigner { } impl Signer for LocalSigner { - fn sign_attestation_message(&self, message: &[u8]) -> 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 53bce3aaa7..6062460cb1 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_proposer/src/lib.rs b/eth2/block_proposer/src/lib.rs index cf71edd99f..0e66bdc70a 100644 --- a/eth2/block_proposer/src/lib.rs +++ b/eth2/block_proposer/src/lib.rs @@ -134,7 +134,10 @@ impl BlockProducer return Ok(PollOutcome::SignerRejection(slot)), Some(signature) => signature, } @@ -166,10 +169,10 @@ impl BlockProducer Option { self.store_produce(&block); - match self - .signer - .sign_block_proposal(&block.proposal_root(&self.spec)[..]) - { + match self.signer.sign_block_proposal( + &block.proposal_root(&self.spec)[..], + self.spec.domain_proposal, + ) { None => None, Some(signature) => { block.signature = signature; diff --git a/eth2/block_proposer/src/test_utils/local_signer.rs b/eth2/block_proposer/src/test_utils/local_signer.rs index 0ebefa29d6..d7f490c300 100644 --- a/eth2/block_proposer/src/test_utils/local_signer.rs +++ b/eth2/block_proposer/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_proposer/src/traits.rs b/eth2/block_proposer/src/traits.rs index 5eb27bce71..c6e57d833f 100644 --- a/eth2/block_proposer/src/traits.rs +++ b/eth2/block_proposer/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/protolambda_lmd_ghost.rs b/eth2/fork_choice/src/protolambda_lmd_ghost.rs new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/eth2/fork_choice/src/protolambda_lmd_ghost.rs @@ -0,0 +1 @@ + diff --git a/eth2/state_processing/src/block_processable.rs b/eth2/state_processing/src/block_processable.rs index 0e6b57cf08..effaa65079 100644 --- a/eth2/state_processing/src/block_processable.rs +++ b/eth2/state_processing/src/block_processable.rs @@ -396,14 +396,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 66140decbc..73a587c26b 100644 --- a/eth2/types/src/attestation.rs +++ b/eth2/types/src/attestation.rs @@ -26,11 +26,13 @@ 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) + self.aggregate_signature.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 0270bb87c6..85b49bf01c 100644 --- a/eth2/types/src/beacon_state.rs +++ b/eth2/types/src/beacon_state.rs @@ -2,8 +2,8 @@ use self::epoch_cache::EpochCache; use crate::test_utils::TestRandom; use crate::{ validator::StatusFlags, validator_registry::get_active_validator_indices, AttestationData, - Bitfield, ChainSpec, Crosslink, Deposit, DepositData, Epoch, Eth1Data, Eth1DataVote, Fork, - Hash256, PendingAttestation, PublicKey, Signature, Slot, Validator, + Bitfield, ChainSpec, Crosslink, Deposit, DepositData, DepositInput, Epoch, Eth1Data, + Eth1DataVote, Fork, Hash256, PendingAttestation, PublicKey, Signature, Slot, Validator, }; use bls::verify_proof_of_possession; use honey_badger_split::SplitExt; @@ -515,6 +515,7 @@ impl BeaconState { &self, slot: Slot, registry_change: bool, + spec: &ChainSpec, ) -> Result>, Error> { let (_committees_per_epoch, seed, shuffling_epoch, _shuffling_start_shard) = @@ -792,6 +793,30 @@ impl BeaconState { self.validator_registry_update_epoch = current_epoch; } + /// Confirm validator owns PublicKey + /// + /// Spec v0.2.0 + 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: Signature::empty_signature(), + }; + + 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 multiple deposits in sequence. /// /// Builds a hashmap of validator pubkeys to validator index and passes it to each successive @@ -843,8 +868,17 @@ impl BeaconState { pubkey_map: Option<&HashMap>, spec: &ChainSpec, ) -> Result { - // TODO: ensure verify proof-of-possession represents the spec accurately. - if !verify_proof_of_possession(&proof_of_possession, &pubkey) { + // TODO: update proof of possession to function written above ( + // requires bls::create_proof_of_possession to be updated + // https://github.com/sigp/lighthouse/issues/239 + 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 35285d6ed9..bf168f03f4 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 TestRandom for Fork { fn random_for_test(rng: &mut T) -> Self { Self { diff --git a/eth2/types/src/test_utils/signature.rs b/eth2/types/src/test_utils/signature.rs index 9ec7aec60a..d9995835ac 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 465510c591..7a436307b5 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.5.2" } 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 b606a5ebd3..4ee79d0aa8 100644 --- a/eth2/utils/bls/src/aggregate_signature.rs +++ b/eth2/utils/bls/src/aggregate_signature.rs @@ -27,8 +27,13 @@ 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 +78,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 646047d183..8f2e9fac03 100644 --- a/eth2/utils/bls/src/lib.rs +++ b/eth2/utils/bls/src/lib.rs @@ -1,5 +1,4 @@ extern crate bls_aggregates; -extern crate hashing; extern crate ssz; mod aggregate_signature; @@ -16,37 +15,29 @@ pub use crate::signature::Signature; pub use self::bls_aggregates::AggregatePublicKey; -pub const BLS_AGG_SIG_BYTE_SIZE: usize = 97; +pub const BLS_AGG_SIG_BYTE_SIZE: usize = 96; -use hashing::hash; use ssz::ssz_encode; -use std::default::Default; - -fn extend_if_needed(hash: &mut Vec) { - // NOTE: bls_aggregates crate demands 48 bytes, this may be removed as we get closer to production - hash.resize(48, Default::default()) -} /// 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) + // TODO: replace this function with state.validate_proof_of_possession + // https://github.com/sigp/lighthouse/issues/239 + sig.verify(&ssz_encode(pubkey), 0, &pubkey) } +// TODO: Update this method +// https://github.com/sigp/lighthouse/issues/239 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 511681957c..c0c31ef27b 100644 --- a/eth2/utils/bls/src/signature.rs +++ b/eth2/utils/bls/src/signature.rs @@ -14,24 +14,34 @@ 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 +51,9 @@ impl Signature { /// Returns a new empty signature. pub fn empty_signature() -> Self { - let empty: Vec = vec![0; 97]; + let mut empty: Vec = vec![0; 96]; + // TODO: Modify the way flags are used (b_flag should not be used for empty_signature in the future) + empty[0] += u8::pow(2, 6); Signature(RawSignature::from_bytes(&empty).unwrap()) } } @@ -85,7 +97,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(); @@ -99,9 +111,13 @@ mod tests { let sig_as_bytes: Vec = sig.as_raw().as_bytes(); - assert_eq!(sig_as_bytes.len(), 97); - for one_byte in sig_as_bytes.iter() { - assert_eq!(*one_byte, 0); + assert_eq!(sig_as_bytes.len(), 96); + for (i, one_byte) in sig_as_bytes.iter().enumerate() { + if i == 0 { + assert_eq!(*one_byte, u8::pow(2, 6)); + } else { + assert_eq!(*one_byte, 0); + } } } }