diff --git a/blockchain/src/block_production/mod.rs b/blockchain/src/block_production/mod.rs index 698467aa0c..bffa4e3808 100644 --- a/blockchain/src/block_production/mod.rs +++ b/blockchain/src/block_production/mod.rs @@ -147,7 +147,7 @@ impl BlockProducer { // leader. prev_seed } else { - prev_seed.sign_next_with_rng(&self.signing_key, rng) + prev_seed.sign_next_with_rng(&self.signing_key, block_number, rng) }; // Create the inherents from the equivocation proofs or skip block info. @@ -303,10 +303,11 @@ impl BlockProducer { // Calculate the seed for this block by signing the previous block seed with the validator // key. - let seed = blockchain - .head() - .seed() - .sign_next_with_rng(&self.signing_key, rng); + let seed = + blockchain + .head() + .seed() + .sign_next_with_rng(&self.signing_key, block_number, rng); // If this is an election block, calculate the validator set for the next epoch. let validators = match Policy::is_election_block_at(block_number) { diff --git a/pow-migration/src/genesis.rs b/pow-migration/src/genesis.rs index 5a459a815e..06407414e2 100644 --- a/pow-migration/src/genesis.rs +++ b/pow-migration/src/genesis.rs @@ -77,7 +77,11 @@ pub async fn get_pos_genesis( let mut parent_hash_bytes = [0u8; 32]; parent_hash_bytes.copy_from_slice(parent_hash.as_slice()); let mut rng = StdRng::from_seed(parent_hash_bytes); - let vrf_seed = VrfSeed::default().sign_next_with_rng(&KeyPair::generate(&mut rng), &mut rng); + let vrf_seed = VrfSeed::default().sign_next_with_rng( + &KeyPair::generate(&mut rng), + final_block.number, + &mut rng, + ); log::info!("Getting PoW account state"); diff --git a/primitives/block/src/block.rs b/primitives/block/src/block.rs index b8a57eda0c..af687fae0a 100644 --- a/primitives/block/src/block.rs +++ b/primitives/block/src/block.rs @@ -596,7 +596,7 @@ impl Block { // Verify VRF seed. if !self.is_skip() { self.seed() - .verify(prev_seed, signing_key) + .verify(prev_seed, signing_key, self.block_number()) .map_err(|_| BlockError::InvalidSeed)?; } else { // XXX This is also checked in `verify_immediate_successor`. diff --git a/primitives/block/src/equivocation_proof.rs b/primitives/block/src/equivocation_proof.rs index c0f801c2bd..3124c3fd05 100644 --- a/primitives/block/src/equivocation_proof.rs +++ b/primitives/block/src/equivocation_proof.rs @@ -636,7 +636,7 @@ mod test { block_number: Policy::genesis_block_number(), timestamp: 1, parent_hash: "".hash(), - seed: VrfSeed::default().sign_next(&key), + seed: VrfSeed::default().sign_next(&key, Policy::genesis_block_number()), extra_data: vec![1], state_root: "".hash(), body_root: "".hash(), @@ -650,7 +650,9 @@ mod test { block_number: Policy::genesis_block_number(), timestamp: 2, parent_hash: "1".hash(), - seed: VrfSeed::default().sign_next(&key).sign_next(&key), + seed: VrfSeed::default() + .sign_next(&key, Policy::genesis_block_number()) + .sign_next(&key, Policy::genesis_block_number()), extra_data: vec![2], state_root: "1".hash(), body_root: "1".hash(), @@ -748,7 +750,7 @@ mod test { parent_hash: "".hash(), parent_election_hash: "".hash(), interlink: Some(vec![]), - seed: VrfSeed::default().sign_next(&key), + seed: VrfSeed::default().sign_next(&key, Policy::genesis_block_number()), extra_data: vec![1], state_root: "".hash(), body_root: "".hash(), @@ -767,7 +769,9 @@ mod test { parent_hash: "1".hash(), parent_election_hash: "1".hash(), interlink: Some(vec![Blake2bHash::default()]), - seed: VrfSeed::default().sign_next(&key).sign_next(&key), + seed: VrfSeed::default() + .sign_next(&key, Policy::genesis_block_number()) + .sign_next(&key, Policy::genesis_block_number()), extra_data: vec![2], state_root: "1".hash(), body_root: "1".hash(), diff --git a/test-utils/src/test_custom_block.rs b/test-utils/src/test_custom_block.rs index 0a6c68df06..6123cd3d33 100644 --- a/test-utils/src/test_custom_block.rs +++ b/test-utils/src/test_custom_block.rs @@ -104,7 +104,7 @@ pub fn next_micro_block( let seed = config .seed .clone() - .unwrap_or_else(|| prev_seed.sign_next(signing_key)); + .unwrap_or_else(|| prev_seed.sign_next(signing_key, block_number)); let mut transactions = config.transactions.clone(); transactions.sort_unstable(); @@ -313,10 +313,12 @@ pub fn next_macro_block_proposal( } }); - let seed = config - .seed - .clone() - .unwrap_or_else(|| blockchain.head().seed().sign_next(signing_key)); + let seed = config.seed.clone().unwrap_or_else(|| { + blockchain + .head() + .seed() + .sign_next(signing_key, block_number) + }); let validators = if Policy::is_election_block_at(blockchain.block_number() + 1) { Some(blockchain.next_validators(&seed)) diff --git a/vrf/src/vrf.rs b/vrf/src/vrf.rs index 9ac5717d62..f32b72e28b 100644 --- a/vrf/src/vrf.rs +++ b/vrf/src/vrf.rs @@ -88,12 +88,13 @@ pub struct VrfSeed { impl VrfSeed { const SIZE: usize = 96; - /// Verifies the current VRF Seed given the previous VRF Seed (which is part of the message) - /// and the signer's public key. + /// Verifies the current VRF Seed given the previous VRF Seed (which is part of the message), + /// the signer's public key and the nonce. pub fn verify( &self, prev_seed: &VrfSeed, public_key: &Ed25519PublicKey, + nonce: u32, ) -> Result<(), VrfError> { // Deserialize signature. let V = CompressedEdwardsY::from_slice(&self.signature[..32]) @@ -119,9 +120,10 @@ impl VrfSeed { .decompress() .ok_or(VrfError::InvalidSignature)?; - // Concatenate use case prefix and previous entropy to form message. Note that we use the - // entropy here and not the signature, that's because we need the message to be unique. + // Concatenate use case prefix, nonce, and previous entropy to form message. Note that we use + // the entropy here and not the signature, that's because we need the message to be unique. let mut message = vec![VrfUseCase::Seed as u8]; + message.extend(nonce.to_be_bytes()); message.extend_from_slice(prev_seed.entropy().as_slice()); // Follow the verification algorithm for VXEdDSA. @@ -159,16 +161,17 @@ impl VrfSeed { /// Produces the next VRF Seed given the current VRF Seed (which is part of the message) and a /// key pair. #[must_use] - pub fn sign_next(&self, keypair: &KeyPair) -> Self { - self.sign_next_with_rng(keypair, &mut rand::thread_rng()) + pub fn sign_next(&self, keypair: &KeyPair, nonce: u32) -> Self { + self.sign_next_with_rng(keypair, nonce, &mut rand::thread_rng()) } - /// Produces the next VRF Seed given the current VRF Seed (which is part of the message) and a - /// key pair. + /// Produces the next VRF Seed given the current VRF Seed (which is part of the message), a + /// key pair and a nonce. #[must_use] pub fn sign_next_with_rng( &self, keypair: &KeyPair, + nonce: u32, rng: &mut R, ) -> Self { // Get random bytes. @@ -179,9 +182,10 @@ impl VrfSeed { let a = keypair.private.to_scalar(); let A_bytes = keypair.public.as_bytes(); - // Concatenate use case prefix and entropy to form message. Note that we use the entropy - // here and not the signature, that's because we need the message to be unique. + // Concatenate use case prefix, nonce, and entropy to form message. Note that we use the + // entropy here and not the signature, that's because we need the message to be unique. let mut message = vec![VrfUseCase::Seed as u8]; + message.extend(nonce.to_be_bytes()); message.extend_from_slice(self.entropy().as_slice()); // Follow the signing algorithm for VXEdDSA. @@ -326,9 +330,9 @@ mod tests { for _ in 0..1000 { let key_pair = KeyPair::generate(&mut rng); - let next_seed = prev_seed.sign_next(&key_pair); + let next_seed = prev_seed.sign_next(&key_pair, 0); - assert!(next_seed.verify(&prev_seed, &key_pair.public).is_ok()); + assert!(next_seed.verify(&prev_seed, &key_pair.public, 0).is_ok()); next_seed.entropy(); @@ -342,13 +346,13 @@ mod tests { let key_pair = KeyPair::generate(&mut rng); let prev_seed = VrfSeed::default(); - let next_seed = prev_seed.sign_next(&key_pair); + let next_seed = prev_seed.sign_next(&key_pair, 0); for _ in 0..1000 { let fake_pk = KeyPair::generate(&mut rng).public; assert_eq!( - next_seed.verify(&prev_seed, &fake_pk), + next_seed.verify(&prev_seed, &fake_pk, 0), Err(VrfError::Forged) ); } @@ -360,14 +364,14 @@ mod tests { let key_pair = KeyPair::generate(&mut rng); let prev_seed = VrfSeed::default(); - let next_seed = prev_seed.sign_next(&key_pair); + let next_seed = prev_seed.sign_next(&key_pair, 0); for _ in 0..1000 { let fake_key_pair = KeyPair::generate(&mut rng); - let fake_seed = VrfSeed::default().sign_next(&fake_key_pair); + let fake_seed = VrfSeed::default().sign_next(&fake_key_pair, 0); assert_eq!( - next_seed.verify(&fake_seed, &key_pair.public), + next_seed.verify(&fake_seed, &key_pair.public, 0), Err(VrfError::Forged) ); } @@ -384,7 +388,21 @@ mod tests { rng.fill_bytes(&mut bytes); let fake_seed = VrfSeed { signature: bytes }; - assert!(fake_seed.verify(&prev_seed, &key_pair.public).is_err()); + assert!(fake_seed.verify(&prev_seed, &key_pair.public, 0).is_err()); } } + + #[test] + fn wrong_nonce() { + let mut rng = test_rng(false); + let key_pair = KeyPair::generate(&mut rng); + let prev_seed = VrfSeed::default(); + + let next_seed = prev_seed.sign_next(&key_pair, 0); + + assert_eq!( + next_seed.verify(&prev_seed, &key_pair.public, 1), + Err(VrfError::Forged) + ); + } }