Skip to content

Commit

Permalink
Refinements to VRF types (paritytech#14036)
Browse files Browse the repository at this point in the history
* Allow extra signing data

* Fix tests after renaming

* Rename VrfSecret/VrfVerifier to VrfSecret/VrfPublic

* Further encrapsulation of 'transcript' type to the sr25519 implementation

* Keystore sr25519 pre-output

* Leave additional custom input field hidden in the associated VrfInput type

* Fix test

* More ergonomic output_bytes

* Trigger pipeline

* Define a separated type for vrf signature data

* Fix docs

* Fix doc

* Remove annotation

* Directly use dleq_proove and dleq_verify in sr25519

* Trigger CI

* Remove cruft before merge
  • Loading branch information
davxy authored and nathanwhit committed Jul 19, 2023
1 parent 0d81dc6 commit e4b96dc
Show file tree
Hide file tree
Showing 12 changed files with 382 additions and 136 deletions.
14 changes: 7 additions & 7 deletions client/consensus/babe/src/authorship.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ use sc_consensus_epochs::Epoch as EpochT;
use sp_application_crypto::AppCrypto;
use sp_consensus_babe::{
digests::{PreDigest, PrimaryPreDigest, SecondaryPlainPreDigest, SecondaryVRFPreDigest},
make_transcript, AuthorityId, BabeAuthorityWeight, Randomness, Slot,
make_vrf_sign_data, AuthorityId, BabeAuthorityWeight, Randomness, Slot,
};
use sp_core::{
blake2_256,
Expand Down Expand Up @@ -148,9 +148,9 @@ fn claim_secondary_slot(
for (authority_id, authority_index) in keys {
if authority_id == expected_author {
let pre_digest = if author_secondary_vrf {
let transcript = make_transcript(randomness, slot, epoch_index);
let data = make_vrf_sign_data(randomness, slot, epoch_index);
let result =
keystore.sr25519_vrf_sign(AuthorityId::ID, authority_id.as_ref(), &transcript);
keystore.sr25519_vrf_sign(AuthorityId::ID, authority_id.as_ref(), &data);
if let Ok(Some(vrf_signature)) = result {
Some(PreDigest::SecondaryVRF(SecondaryVRFPreDigest {
slot,
Expand Down Expand Up @@ -239,18 +239,18 @@ fn claim_primary_slot(
epoch_index = epoch.clone_for_slot(slot).epoch_index;
}

let transcript = make_transcript(randomness, slot, epoch_index);
let data = make_vrf_sign_data(randomness, slot, epoch_index);

for (authority_id, authority_index) in keys {
let result = keystore.sr25519_vrf_sign(AuthorityId::ID, authority_id.as_ref(), &transcript);
let result = keystore.sr25519_vrf_sign(AuthorityId::ID, authority_id.as_ref(), &data);
if let Ok(Some(vrf_signature)) = result {
let threshold = calculate_primary_threshold(c, authorities, *authority_index);

let can_claim = authority_id
.as_inner_ref()
.make_bytes::<[u8; AUTHORING_SCORE_LENGTH]>(
.make_bytes::<AUTHORING_SCORE_LENGTH>(
AUTHORING_SCORE_VRF_CONTEXT,
&transcript,
&data.as_ref(),
&vrf_signature.output,
)
.map(|bytes| u128::from_le_bytes(bytes) < threshold)
Expand Down
30 changes: 9 additions & 21 deletions client/consensus/babe/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ use sc_network_test::{Block as TestBlock, *};
use sp_application_crypto::key_types::BABE;
use sp_consensus::{DisableProofRecording, NoNetwork as DummyOracle, Proposal};
use sp_consensus_babe::{
inherents::InherentDataProvider, make_transcript, AllowedSlots, AuthorityId, AuthorityPair,
inherents::InherentDataProvider, make_vrf_sign_data, AllowedSlots, AuthorityId, AuthorityPair,
Slot,
};
use sp_consensus_slots::SlotDuration;
Expand Down Expand Up @@ -630,23 +630,17 @@ fn claim_vrf_check() {
PreDigest::Primary(d) => d,
v => panic!("Unexpected pre-digest variant {:?}", v),
};
let transcript = make_transcript(&epoch.randomness.clone(), 0.into(), epoch.epoch_index);
let sign = keystore
.sr25519_vrf_sign(AuthorityId::ID, &public, &transcript)
.unwrap()
.unwrap();
let data = make_vrf_sign_data(&epoch.randomness.clone(), 0.into(), epoch.epoch_index);
let sign = keystore.sr25519_vrf_sign(AuthorityId::ID, &public, &data).unwrap().unwrap();
assert_eq!(pre_digest.vrf_signature.output, sign.output);

// We expect a SecondaryVRF claim for slot 1
let pre_digest = match claim_slot(1.into(), &epoch, &keystore).unwrap().0 {
PreDigest::SecondaryVRF(d) => d,
v => panic!("Unexpected pre-digest variant {:?}", v),
};
let transcript = make_transcript(&epoch.randomness.clone(), 1.into(), epoch.epoch_index);
let sign = keystore
.sr25519_vrf_sign(AuthorityId::ID, &public, &transcript)
.unwrap()
.unwrap();
let data = make_vrf_sign_data(&epoch.randomness.clone(), 1.into(), epoch.epoch_index);
let sign = keystore.sr25519_vrf_sign(AuthorityId::ID, &public, &data).unwrap().unwrap();
assert_eq!(pre_digest.vrf_signature.output, sign.output);

// Check that correct epoch index has been used if epochs are skipped (primary VRF)
Expand All @@ -656,11 +650,8 @@ fn claim_vrf_check() {
v => panic!("Unexpected claim variant {:?}", v),
};
let fixed_epoch = epoch.clone_for_slot(slot);
let transcript = make_transcript(&epoch.randomness.clone(), slot, fixed_epoch.epoch_index);
let sign = keystore
.sr25519_vrf_sign(AuthorityId::ID, &public, &transcript)
.unwrap()
.unwrap();
let data = make_vrf_sign_data(&epoch.randomness.clone(), slot, fixed_epoch.epoch_index);
let sign = keystore.sr25519_vrf_sign(AuthorityId::ID, &public, &data).unwrap().unwrap();
assert_eq!(fixed_epoch.epoch_index, 11);
assert_eq!(claim.vrf_signature.output, sign.output);

Expand All @@ -671,11 +662,8 @@ fn claim_vrf_check() {
v => panic!("Unexpected claim variant {:?}", v),
};
let fixed_epoch = epoch.clone_for_slot(slot);
let transcript = make_transcript(&epoch.randomness.clone(), slot, fixed_epoch.epoch_index);
let sign = keystore
.sr25519_vrf_sign(AuthorityId::ID, &public, &transcript)
.unwrap()
.unwrap();
let data = make_vrf_sign_data(&epoch.randomness.clone(), slot, fixed_epoch.epoch_index);
let sign = keystore.sr25519_vrf_sign(AuthorityId::ID, &public, &data).unwrap().unwrap();
assert_eq!(fixed_epoch.epoch_index, 11);
assert_eq!(pre_digest.vrf_signature.output, sign.output);
}
Expand Down
16 changes: 8 additions & 8 deletions client/consensus/babe/src/verification.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,11 @@ use sp_consensus_babe::{
CompatibleDigestItem, PreDigest, PrimaryPreDigest, SecondaryPlainPreDigest,
SecondaryVRFPreDigest,
},
make_transcript, AuthorityId, AuthorityPair, AuthoritySignature,
make_vrf_sign_data, AuthorityId, AuthorityPair, AuthoritySignature,
};
use sp_consensus_slots::Slot;
use sp_core::{
crypto::{VrfVerifier, Wraps},
crypto::{VrfPublic, Wraps},
Pair,
};
use sp_runtime::{traits::Header, DigestItem};
Expand Down Expand Up @@ -171,9 +171,9 @@ fn check_primary_header<B: BlockT + Sized>(
return Err(babe_err(Error::BadSignature(pre_hash)))
}

let transcript = make_transcript(&epoch.randomness, pre_digest.slot, epoch_index);
let data = make_vrf_sign_data(&epoch.randomness, pre_digest.slot, epoch_index);

if !authority_id.as_inner_ref().vrf_verify(&transcript, &pre_digest.vrf_signature) {
if !authority_id.as_inner_ref().vrf_verify(&data, &pre_digest.vrf_signature) {
return Err(babe_err(Error::VrfVerificationFailed))
}

Expand All @@ -182,9 +182,9 @@ fn check_primary_header<B: BlockT + Sized>(

let score = authority_id
.as_inner_ref()
.make_bytes::<[u8; AUTHORING_SCORE_LENGTH]>(
.make_bytes::<AUTHORING_SCORE_LENGTH>(
AUTHORING_SCORE_VRF_CONTEXT,
&transcript,
&data.as_ref(),
&pre_digest.vrf_signature.output,
)
.map(u128::from_le_bytes)
Expand Down Expand Up @@ -253,9 +253,9 @@ fn check_secondary_vrf_header<B: BlockT>(
return Err(Error::BadSignature(pre_hash))
}

let transcript = make_transcript(&epoch.randomness, pre_digest.slot, epoch_index);
let data = make_vrf_sign_data(&epoch.randomness, pre_digest.slot, epoch_index);

if !author.as_inner_ref().vrf_verify(&transcript, &pre_digest.vrf_signature) {
if !author.as_inner_ref().vrf_verify(&data, &pre_digest.vrf_signature) {
return Err(Error::VrfVerificationFailed)
}

Expand Down
35 changes: 29 additions & 6 deletions client/keystore/src/local.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
use parking_lot::RwLock;
use sp_application_crypto::{AppCrypto, AppPair, IsWrappedBy};
use sp_core::{
crypto::{ByteArray, ExposeSecret, KeyTypeId, Pair as CorePair, SecretString, VrfSigner},
crypto::{ByteArray, ExposeSecret, KeyTypeId, Pair as CorePair, SecretString, VrfSecret},
ecdsa, ed25519, sr25519,
};
use sp_keystore::{Error as TraitError, Keystore, KeystorePtr};
Expand Down Expand Up @@ -98,19 +98,33 @@ impl LocalKeystore {
Ok(signature)
}

fn vrf_sign<T: CorePair + VrfSigner>(
fn vrf_sign<T: CorePair + VrfSecret>(
&self,
key_type: KeyTypeId,
public: &T::Public,
transcript: &T::VrfInput,
data: &T::VrfSignData,
) -> std::result::Result<Option<T::VrfSignature>, TraitError> {
let sig = self
.0
.read()
.key_pair_by_type::<T>(public, key_type)?
.map(|pair| pair.vrf_sign(transcript));
.map(|pair| pair.vrf_sign(data));
Ok(sig)
}

fn vrf_output<T: CorePair + VrfSecret>(
&self,
key_type: KeyTypeId,
public: &T::Public,
input: &T::VrfInput,
) -> std::result::Result<Option<T::VrfOutput>, TraitError> {
let preout = self
.0
.read()
.key_pair_by_type::<T>(public, key_type)?
.map(|pair| pair.vrf_output(input));
Ok(preout)
}
}

impl Keystore for LocalKeystore {
Expand Down Expand Up @@ -142,9 +156,18 @@ impl Keystore for LocalKeystore {
&self,
key_type: KeyTypeId,
public: &sr25519::Public,
transcript: &sr25519::vrf::VrfTranscript,
data: &sr25519::vrf::VrfSignData,
) -> std::result::Result<Option<sr25519::vrf::VrfSignature>, TraitError> {
self.vrf_sign::<sr25519::Pair>(key_type, public, transcript)
self.vrf_sign::<sr25519::Pair>(key_type, public, data)
}

fn sr25519_vrf_output(
&self,
key_type: KeyTypeId,
public: &sr25519::Public,
input: &sr25519::vrf::VrfInput,
) -> std::result::Result<Option<sr25519::vrf::VrfOutput>, TraitError> {
self.vrf_output::<sr25519::Pair>(key_type, public, input)
}

fn ed25519_public_keys(&self, key_type: KeyTypeId) -> Vec<ed25519::Public> {
Expand Down
14 changes: 5 additions & 9 deletions frame/babe/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -357,12 +357,12 @@ pub mod pallet {
);
}

if let Some(vrf_signature) = pre_digest.vrf_signature() {
if let Some(signature) = pre_digest.vrf_signature() {
let randomness: Option<BabeRandomness> = Authorities::<T>::get()
.get(authority_index as usize)
.and_then(|(authority, _)| {
let public = authority.as_inner_ref();
let transcript = sp_consensus_babe::make_transcript(
let transcript = sp_consensus_babe::make_vrf_transcript(
&Self::randomness(),
CurrentSlot::<T>::get(),
EpochIndex::<T>::get(),
Expand All @@ -372,16 +372,12 @@ pub mod pallet {
// execution. We don't run the verification again here to avoid slowing
// down the runtime.
debug_assert!({
use sp_core::crypto::VrfVerifier;
public.vrf_verify(&transcript, &vrf_signature)
use sp_core::crypto::VrfPublic;
public.vrf_verify(&transcript.clone().into_sign_data(), &signature)
});

public
.make_bytes(
RANDOMNESS_VRF_CONTEXT,
&transcript,
&vrf_signature.output,
)
.make_bytes(RANDOMNESS_VRF_CONTEXT, &transcript, &signature.output)
.ok()
});

Expand Down
13 changes: 6 additions & 7 deletions frame/babe/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ use frame_support::{
use pallet_session::historical as pallet_session_historical;
use sp_consensus_babe::{AuthorityId, AuthorityPair, Randomness, Slot, VrfSignature};
use sp_core::{
crypto::{KeyTypeId, Pair, VrfSigner},
crypto::{KeyTypeId, Pair, VrfSecret},
H256, U256,
};
use sp_io;
Expand Down Expand Up @@ -314,17 +314,16 @@ pub fn make_secondary_vrf_pre_digest(
Digest { logs: vec![log] }
}

pub fn make_vrf_output(
pub fn make_vrf_signature_and_randomness(
slot: Slot,
pair: &sp_consensus_babe::AuthorityPair,
) -> (VrfSignature, Randomness) {
let transcript = sp_consensus_babe::make_transcript(&Babe::randomness(), slot, 0);
let transcript = sp_consensus_babe::make_vrf_transcript(&Babe::randomness(), slot, 0);

let signature = pair.as_ref().vrf_sign(&transcript);
let randomness =
pair.as_ref().make_bytes(sp_consensus_babe::RANDOMNESS_VRF_CONTEXT, &transcript);

let randomness = pair
.as_ref()
.make_bytes::<Randomness>(sp_consensus_babe::RANDOMNESS_VRF_CONTEXT, &transcript);
let signature = pair.as_ref().vrf_sign(&transcript.into());

(signature, randomness)
}
Expand Down
9 changes: 6 additions & 3 deletions frame/babe/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,8 @@ fn first_block_epoch_zero_start() {

ext.execute_with(|| {
let genesis_slot = Slot::from(100);
let (vrf_signature, vrf_randomness) = make_vrf_output(genesis_slot, &pairs[0]);
let (vrf_signature, vrf_randomness) =
make_vrf_signature_and_randomness(genesis_slot, &pairs[0]);

let pre_digest = make_primary_pre_digest(0, genesis_slot, vrf_signature);

Expand Down Expand Up @@ -111,7 +112,8 @@ fn current_slot_is_processed_on_initialization() {

ext.execute_with(|| {
let genesis_slot = Slot::from(10);
let (vrf_signature, vrf_randomness) = make_vrf_output(genesis_slot, &pairs[0]);
let (vrf_signature, vrf_randomness) =
make_vrf_signature_and_randomness(genesis_slot, &pairs[0]);
let pre_digest = make_primary_pre_digest(0, genesis_slot, vrf_signature);

System::reset_events();
Expand Down Expand Up @@ -140,7 +142,8 @@ where

ext.execute_with(|| {
let genesis_slot = Slot::from(10);
let (vrf_signature, vrf_randomness) = make_vrf_output(genesis_slot, &pairs[0]);
let (vrf_signature, vrf_randomness) =
make_vrf_signature_and_randomness(genesis_slot, &pairs[0]);
let pre_digest = make_pre_digest(0, genesis_slot, vrf_signature);

System::reset_events();
Expand Down
15 changes: 11 additions & 4 deletions primitives/consensus/babe/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@ use sp_std::vec::Vec;

use crate::digests::{NextConfigDescriptor, NextEpochDescriptor};

pub use sp_core::sr25519::vrf::{VrfOutput, VrfProof, VrfSignature, VrfTranscript};
pub use sp_core::sr25519::vrf::{
VrfInput, VrfOutput, VrfProof, VrfSignData, VrfSignature, VrfTranscript,
};

/// Key type for BABE module.
pub const KEY_TYPE: sp_core::crypto::KeyTypeId = sp_application_crypto::key_types::BABE;
Expand Down Expand Up @@ -94,9 +96,9 @@ pub type BabeAuthorityWeight = u64;
/// of 0 (regardless of whether they are plain or vrf secondary blocks).
pub type BabeBlockWeight = u32;

/// Make a VRF transcript data container
pub fn make_transcript(randomness: &Randomness, slot: Slot, epoch: u64) -> VrfTranscript {
VrfTranscript::new(
/// Make VRF input suitable for BABE's randomness generation.
pub fn make_vrf_transcript(randomness: &Randomness, slot: Slot, epoch: u64) -> VrfInput {
VrfInput::new(
&BABE_ENGINE_ID,
&[
(b"slot number", &slot.to_le_bytes()),
Expand All @@ -106,6 +108,11 @@ pub fn make_transcript(randomness: &Randomness, slot: Slot, epoch: u64) -> VrfTr
)
}

/// Make VRF signing data suitable for BABE's protocol.
pub fn make_vrf_sign_data(randomness: &Randomness, slot: Slot, epoch: u64) -> VrfSignData {
make_vrf_transcript(randomness, slot, epoch).into()
}

/// An consensus log item for BABE.
#[derive(Decode, Encode, Clone, PartialEq, Eq)]
pub enum ConsensusLog {
Expand Down
28 changes: 17 additions & 11 deletions primitives/core/src/crypto.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1094,23 +1094,29 @@ impl<'a> TryFrom<&'a str> for KeyTypeId {

/// Trait grouping types shared by a VRF signer and verifiers.
pub trait VrfCrypto {
/// Associated signature type.
type VrfSignature;

/// Vrf input data. Generally some form of transcript.
/// VRF input.
type VrfInput;
/// VRF output.
type VrfOutput;
/// VRF signing data.
type VrfSignData;
/// VRF signature.
type VrfSignature;
}

/// VRF Signer.
pub trait VrfSigner: VrfCrypto {
/// Sign input data.
fn vrf_sign(&self, data: &Self::VrfInput) -> Self::VrfSignature;
/// VRF Secret Key.
pub trait VrfSecret: VrfCrypto {
/// Get VRF-specific output .
fn vrf_output(&self, data: &Self::VrfInput) -> Self::VrfOutput;

/// Sign VRF-specific data.
fn vrf_sign(&self, input: &Self::VrfSignData) -> Self::VrfSignature;
}

/// VRF Verifier.
pub trait VrfVerifier: VrfCrypto {
/// VRF Public Key.
pub trait VrfPublic: VrfCrypto {
/// Verify input data signature.
fn vrf_verify(&self, data: &Self::VrfInput, signature: &Self::VrfSignature) -> bool;
fn vrf_verify(&self, data: &Self::VrfSignData, signature: &Self::VrfSignature) -> bool;
}

/// An identifier for a specific cryptographic algorithm used by a key pair
Expand Down
Loading

0 comments on commit e4b96dc

Please sign in to comment.