mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-13 08:11:04 +00:00
Refinements to VRF types (#14036)
* 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
This commit is contained in:
@@ -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,
|
||||
@@ -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,
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
@@ -630,11 +630,8 @@ 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
|
||||
@@ -642,11 +639,8 @@ fn claim_vrf_check() {
|
||||
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)
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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};
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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};
|
||||
@@ -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 {
|
||||
@@ -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> {
|
||||
|
||||
@@ -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(),
|
||||
@@ -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()
|
||||
});
|
||||
|
||||
|
||||
@@ -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;
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
@@ -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()),
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -537,32 +537,86 @@ impl CryptoType for Pair {
|
||||
pub mod vrf {
|
||||
use super::*;
|
||||
#[cfg(feature = "full_crypto")]
|
||||
use crate::crypto::VrfSigner;
|
||||
use crate::crypto::{VrfCrypto, VrfVerifier};
|
||||
use crate::crypto::VrfSecret;
|
||||
use crate::crypto::{VrfCrypto, VrfPublic};
|
||||
use schnorrkel::{
|
||||
errors::MultiSignatureStage,
|
||||
vrf::{VRF_OUTPUT_LENGTH, VRF_PROOF_LENGTH},
|
||||
SignatureError,
|
||||
};
|
||||
|
||||
/// VRF transcript ready to be used for VRF sign/verify operations.
|
||||
const DEFAULT_EXTRA_DATA_LABEL: &[u8] = b"VRF";
|
||||
|
||||
/// Transcript ready to be used for VRF related operations.
|
||||
#[derive(Clone)]
|
||||
pub struct VrfTranscript(pub merlin::Transcript);
|
||||
|
||||
impl VrfTranscript {
|
||||
/// Build a new transcript ready to be used by a VRF signer/verifier.
|
||||
/// Build a new transcript instance.
|
||||
///
|
||||
/// Each `data` element is a tuple `(domain, message)` composing the transcipt.
|
||||
pub fn new(label: &'static [u8], data: &[(&'static [u8], &[u8])]) -> Self {
|
||||
let mut transcript = merlin::Transcript::new(label);
|
||||
data.iter().for_each(|(l, b)| transcript.append_message(l, b));
|
||||
VrfTranscript(transcript)
|
||||
}
|
||||
|
||||
/// Map transcript to `VrfSignData`.
|
||||
pub fn into_sign_data(self) -> VrfSignData {
|
||||
self.into()
|
||||
}
|
||||
}
|
||||
|
||||
/// VRF input.
|
||||
///
|
||||
/// Technically a transcript used by the Fiat-Shamir transform.
|
||||
pub type VrfInput = VrfTranscript;
|
||||
|
||||
/// VRF input ready to be used for VRF sign and verify operations.
|
||||
#[derive(Clone)]
|
||||
pub struct VrfSignData {
|
||||
/// Transcript data contributing to VRF output.
|
||||
pub(super) transcript: VrfTranscript,
|
||||
/// Extra transcript data to be signed by the VRF.
|
||||
pub(super) extra: Option<VrfTranscript>,
|
||||
}
|
||||
|
||||
impl From<VrfInput> for VrfSignData {
|
||||
fn from(transcript: VrfInput) -> Self {
|
||||
VrfSignData { transcript, extra: None }
|
||||
}
|
||||
}
|
||||
|
||||
// Get a reference to the inner VRF input.
|
||||
impl AsRef<VrfInput> for VrfSignData {
|
||||
fn as_ref(&self) -> &VrfInput {
|
||||
&self.transcript
|
||||
}
|
||||
}
|
||||
|
||||
impl VrfSignData {
|
||||
/// Build a new instance ready to be used for VRF signer and verifier.
|
||||
///
|
||||
/// `input` will contribute to the VRF output bytes.
|
||||
pub fn new(input: VrfTranscript) -> Self {
|
||||
input.into()
|
||||
}
|
||||
|
||||
/// Add some extra data to be signed.
|
||||
///
|
||||
/// `extra` will not contribute to the VRF output bytes.
|
||||
pub fn with_extra(mut self, extra: VrfTranscript) -> Self {
|
||||
self.extra = Some(extra);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// VRF signature data
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Encode, Decode, MaxEncodedLen, TypeInfo)]
|
||||
pub struct VrfSignature {
|
||||
/// The initial VRF configuration
|
||||
/// VRF output.
|
||||
pub output: VrfOutput,
|
||||
/// The calculated VRF proof
|
||||
/// VRF proof.
|
||||
pub proof: VrfProof,
|
||||
}
|
||||
|
||||
@@ -630,30 +684,58 @@ pub mod vrf {
|
||||
|
||||
#[cfg(feature = "full_crypto")]
|
||||
impl VrfCrypto for Pair {
|
||||
type VrfSignature = VrfSignature;
|
||||
type VrfInput = VrfTranscript;
|
||||
type VrfOutput = VrfOutput;
|
||||
type VrfSignData = VrfSignData;
|
||||
type VrfSignature = VrfSignature;
|
||||
}
|
||||
|
||||
#[cfg(feature = "full_crypto")]
|
||||
impl VrfSigner for Pair {
|
||||
fn vrf_sign(&self, transcript: &Self::VrfInput) -> Self::VrfSignature {
|
||||
let (inout, proof, _) = self.0.vrf_sign(transcript.0.clone());
|
||||
impl VrfSecret for Pair {
|
||||
fn vrf_sign(&self, data: &Self::VrfSignData) -> Self::VrfSignature {
|
||||
let inout = self.0.vrf_create_hash(data.transcript.0.clone());
|
||||
|
||||
let extra = data
|
||||
.extra
|
||||
.as_ref()
|
||||
.map(|e| e.0.clone())
|
||||
.unwrap_or_else(|| merlin::Transcript::new(DEFAULT_EXTRA_DATA_LABEL));
|
||||
|
||||
let proof = self.0.dleq_proove(extra, &inout, true).0;
|
||||
|
||||
VrfSignature { output: VrfOutput(inout.to_output()), proof: VrfProof(proof) }
|
||||
}
|
||||
|
||||
fn vrf_output(&self, input: &Self::VrfInput) -> Self::VrfOutput {
|
||||
let output = self.0.vrf_create_hash(input.0.clone()).to_output();
|
||||
VrfOutput(output)
|
||||
}
|
||||
}
|
||||
|
||||
impl VrfCrypto for Public {
|
||||
type VrfSignature = VrfSignature;
|
||||
type VrfInput = VrfTranscript;
|
||||
type VrfOutput = VrfOutput;
|
||||
type VrfSignData = VrfSignData;
|
||||
type VrfSignature = VrfSignature;
|
||||
}
|
||||
|
||||
impl VrfVerifier for Public {
|
||||
fn vrf_verify(&self, transcript: &Self::VrfInput, signature: &Self::VrfSignature) -> bool {
|
||||
schnorrkel::PublicKey::from_bytes(self)
|
||||
.and_then(|public| {
|
||||
public.vrf_verify(transcript.0.clone(), &signature.output.0, &signature.proof.0)
|
||||
})
|
||||
.is_ok()
|
||||
impl VrfPublic for Public {
|
||||
fn vrf_verify(&self, data: &Self::VrfSignData, signature: &Self::VrfSignature) -> bool {
|
||||
let do_verify = || {
|
||||
let public = schnorrkel::PublicKey::from_bytes(self)?;
|
||||
|
||||
let inout =
|
||||
signature.output.0.attach_input_hash(&public, data.transcript.0.clone())?;
|
||||
|
||||
let extra = data
|
||||
.extra
|
||||
.as_ref()
|
||||
.map(|e| e.0.clone())
|
||||
.unwrap_or_else(|| merlin::Transcript::new(DEFAULT_EXTRA_DATA_LABEL));
|
||||
|
||||
public.dleq_verify(extra, &inout, &signature.proof.0, true)
|
||||
};
|
||||
do_verify().is_ok()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -690,39 +772,54 @@ pub mod vrf {
|
||||
|
||||
#[cfg(feature = "full_crypto")]
|
||||
impl Pair {
|
||||
/// Generate bytes from the given VRF configuration.
|
||||
pub fn make_bytes<B: Default + AsMut<[u8]>>(
|
||||
&self,
|
||||
context: &[u8],
|
||||
transcript: &VrfTranscript,
|
||||
) -> B {
|
||||
let inout = self.0.vrf_create_hash(transcript.0.clone());
|
||||
inout.make_bytes::<B>(context)
|
||||
/// Generate output bytes from the given VRF configuration.
|
||||
pub fn make_bytes<const N: usize>(&self, context: &[u8], input: &VrfInput) -> [u8; N]
|
||||
where
|
||||
[u8; N]: Default,
|
||||
{
|
||||
let inout = self.0.vrf_create_hash(input.0.clone());
|
||||
inout.make_bytes::<[u8; N]>(context)
|
||||
}
|
||||
}
|
||||
|
||||
impl Public {
|
||||
/// Generate bytes from the given VRF configuration.
|
||||
pub fn make_bytes<B: Default + AsMut<[u8]>>(
|
||||
/// Generate output bytes from the given VRF configuration.
|
||||
pub fn make_bytes<const N: usize>(
|
||||
&self,
|
||||
context: &[u8],
|
||||
transcript: &VrfTranscript,
|
||||
input: &VrfInput,
|
||||
output: &VrfOutput,
|
||||
) -> Result<B, codec::Error> {
|
||||
) -> Result<[u8; N], codec::Error>
|
||||
where
|
||||
[u8; N]: Default,
|
||||
{
|
||||
let pubkey = schnorrkel::PublicKey::from_bytes(&self.0).map_err(convert_error)?;
|
||||
let inout = output
|
||||
.0
|
||||
.attach_input_hash(&pubkey, transcript.0.clone())
|
||||
.map_err(convert_error)?;
|
||||
Ok(inout.make_bytes::<B>(context))
|
||||
let inout =
|
||||
output.0.attach_input_hash(&pubkey, input.0.clone()).map_err(convert_error)?;
|
||||
Ok(inout.make_bytes::<[u8; N]>(context))
|
||||
}
|
||||
}
|
||||
|
||||
impl VrfOutput {
|
||||
/// Generate output bytes from the given VRF configuration.
|
||||
pub fn make_bytes<const N: usize>(
|
||||
&self,
|
||||
context: &[u8],
|
||||
input: &VrfInput,
|
||||
public: &Public,
|
||||
) -> Result<[u8; N], codec::Error>
|
||||
where
|
||||
[u8; N]: Default,
|
||||
{
|
||||
public.make_bytes(context, input, self)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::crypto::{Ss58Codec, DEV_ADDRESS, DEV_PHRASE};
|
||||
use super::{vrf::*, *};
|
||||
use crate::crypto::{Ss58Codec, VrfPublic, VrfSecret, DEV_ADDRESS, DEV_PHRASE};
|
||||
use serde_json;
|
||||
|
||||
#[test]
|
||||
@@ -952,18 +1049,85 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn vrf_make_bytes_matches() {
|
||||
use super::vrf::*;
|
||||
use crate::crypto::VrfSigner;
|
||||
fn vrf_sign_verify() {
|
||||
let pair = Pair::from_seed(b"12345678901234567890123456789012");
|
||||
let public = pair.public();
|
||||
let transcript = VrfTranscript::new(b"test", &[(b"foo", b"bar")]);
|
||||
|
||||
let signature = pair.vrf_sign(&transcript);
|
||||
let data = VrfTranscript::new(b"label", &[(b"domain1", b"data1")]).into();
|
||||
|
||||
let ctx = b"randbytes";
|
||||
let b1 = pair.make_bytes::<[u8; 32]>(ctx, &transcript);
|
||||
let b2 = public.make_bytes::<[u8; 32]>(ctx, &transcript, &signature.output).unwrap();
|
||||
assert_eq!(b1, b2);
|
||||
let signature = pair.vrf_sign(&data);
|
||||
|
||||
assert!(public.vrf_verify(&data, &signature));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn vrf_sign_verify_with_extra() {
|
||||
let pair = Pair::from_seed(b"12345678901234567890123456789012");
|
||||
let public = pair.public();
|
||||
|
||||
let extra = VrfTranscript::new(b"extra", &[(b"domain2", b"data2")]);
|
||||
let data = VrfTranscript::new(b"label", &[(b"domain1", b"data1")])
|
||||
.into_sign_data()
|
||||
.with_extra(extra);
|
||||
|
||||
let signature = pair.vrf_sign(&data);
|
||||
|
||||
assert!(public.vrf_verify(&data, &signature));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn vrf_make_bytes_matches() {
|
||||
let pair = Pair::from_seed(b"12345678901234567890123456789012");
|
||||
let public = pair.public();
|
||||
let ctx = b"vrfbytes";
|
||||
|
||||
let input = VrfTranscript::new(b"label", &[(b"domain1", b"data1")]);
|
||||
|
||||
let output = pair.vrf_output(&input);
|
||||
|
||||
let out1 = pair.make_bytes::<32>(ctx, &input);
|
||||
let out2 = output.make_bytes::<32>(ctx, &input, &public).unwrap();
|
||||
assert_eq!(out1, out2);
|
||||
|
||||
let extra = VrfTranscript::new(b"extra", &[(b"domain2", b"data2")]);
|
||||
let data = input.clone().into_sign_data().with_extra(extra);
|
||||
let signature = pair.vrf_sign(&data);
|
||||
assert!(public.vrf_verify(&data, &signature));
|
||||
|
||||
let out3 = public.make_bytes::<32>(ctx, &input, &signature.output).unwrap();
|
||||
assert_eq!(out2, out3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn vrf_backend_compat() {
|
||||
let pair = Pair::from_seed(b"12345678901234567890123456789012");
|
||||
let public = pair.public();
|
||||
let ctx = b"vrfbytes";
|
||||
|
||||
let input = VrfInput::new(b"label", &[(b"domain1", b"data1")]);
|
||||
let extra = VrfTranscript::new(b"extra", &[(b"domain2", b"data2")]);
|
||||
|
||||
let data = input.clone().into_sign_data().with_extra(extra.clone());
|
||||
let signature = pair.vrf_sign(&data);
|
||||
assert!(public.vrf_verify(&data, &signature));
|
||||
|
||||
let out1 = pair.make_bytes::<32>(ctx, &input);
|
||||
let out2 = public.make_bytes::<32>(ctx, &input, &signature.output).unwrap();
|
||||
assert_eq!(out1, out2);
|
||||
|
||||
// Direct call to backend version of sign after check with extra params
|
||||
let (inout, proof, _) = pair
|
||||
.0
|
||||
.vrf_sign_extra_after_check(input.0.clone(), |inout| {
|
||||
let out3 = inout.make_bytes::<[u8; 32]>(ctx);
|
||||
assert_eq!(out2, out3);
|
||||
Some(extra.0.clone())
|
||||
})
|
||||
.unwrap();
|
||||
let signature2 =
|
||||
VrfSignature { output: VrfOutput(inout.to_output()), proof: VrfProof(proof) };
|
||||
|
||||
assert!(public.vrf_verify(&data, &signature2));
|
||||
assert_eq!(signature.output, signature2.output);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,23 +72,34 @@ pub trait Keystore: Send + Sync {
|
||||
msg: &[u8],
|
||||
) -> Result<Option<sr25519::Signature>, Error>;
|
||||
|
||||
/// Generate an sr25519 VRF signature for a given transcript data.
|
||||
/// Generate an sr25519 VRF signature for the given data.
|
||||
///
|
||||
/// Receives [`KeyTypeId`] and an [`sr25519::Public`] key to be able to map
|
||||
/// them to a private key that exists in the keystore.
|
||||
///
|
||||
/// Returns a result containing the signature data.
|
||||
/// Namely, VRFOutput and VRFProof which are returned inside the `VRFSignature`
|
||||
/// container struct.
|
||||
/// Returns `None` if the given `key_type` and `public` combination doesn't
|
||||
/// exist in the keystore or an `Err` when something failed.
|
||||
fn sr25519_vrf_sign(
|
||||
&self,
|
||||
key_type: KeyTypeId,
|
||||
public: &sr25519::Public,
|
||||
transcript: &sr25519::vrf::VrfTranscript,
|
||||
data: &sr25519::vrf::VrfSignData,
|
||||
) -> Result<Option<sr25519::vrf::VrfSignature>, Error>;
|
||||
|
||||
/// Generate an sr25519 VRF output for a given input data.
|
||||
///
|
||||
/// Receives [`KeyTypeId`] and an [`sr25519::Public`] key to be able to map
|
||||
/// them to a private key that exists in the keystore.
|
||||
///
|
||||
/// Returns `None` if the given `key_type` and `public` combination doesn't
|
||||
/// exist in the keystore or an `Err` when something failed.
|
||||
fn sr25519_vrf_output(
|
||||
&self,
|
||||
key_type: KeyTypeId,
|
||||
public: &sr25519::Public,
|
||||
input: &sr25519::vrf::VrfInput,
|
||||
) -> Result<Option<sr25519::vrf::VrfOutput>, Error>;
|
||||
|
||||
/// Returns all ed25519 public keys for the given key type.
|
||||
fn ed25519_public_keys(&self, key_type: KeyTypeId) -> Vec<ed25519::Public>;
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
use crate::{Error, Keystore, KeystorePtr};
|
||||
|
||||
use sp_core::{
|
||||
crypto::{ByteArray, KeyTypeId, Pair, VrfSigner},
|
||||
crypto::{ByteArray, KeyTypeId, Pair, VrfSecret},
|
||||
ecdsa, ed25519, sr25519,
|
||||
};
|
||||
|
||||
@@ -99,15 +99,25 @@ impl MemoryKeystore {
|
||||
Ok(sig)
|
||||
}
|
||||
|
||||
fn vrf_sign<T: Pair + VrfSigner>(
|
||||
fn vrf_sign<T: Pair + VrfSecret>(
|
||||
&self,
|
||||
key_type: KeyTypeId,
|
||||
public: &T::Public,
|
||||
transcript: &T::VrfInput,
|
||||
data: &T::VrfSignData,
|
||||
) -> Result<Option<T::VrfSignature>, Error> {
|
||||
let sig = self.pair::<T>(key_type, public).map(|pair| pair.vrf_sign(transcript));
|
||||
let sig = self.pair::<T>(key_type, public).map(|pair| pair.vrf_sign(data));
|
||||
Ok(sig)
|
||||
}
|
||||
|
||||
fn vrf_output<T: Pair + VrfSecret>(
|
||||
&self,
|
||||
key_type: KeyTypeId,
|
||||
public: &T::Public,
|
||||
input: &T::VrfInput,
|
||||
) -> Result<Option<T::VrfOutput>, Error> {
|
||||
let preout = self.pair::<T>(key_type, public).map(|pair| pair.vrf_output(input));
|
||||
Ok(preout)
|
||||
}
|
||||
}
|
||||
|
||||
impl Keystore for MemoryKeystore {
|
||||
@@ -136,9 +146,18 @@ impl Keystore for MemoryKeystore {
|
||||
&self,
|
||||
key_type: KeyTypeId,
|
||||
public: &sr25519::Public,
|
||||
transcript: &sr25519::vrf::VrfTranscript,
|
||||
data: &sr25519::vrf::VrfSignData,
|
||||
) -> Result<Option<sr25519::vrf::VrfSignature>, Error> {
|
||||
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,
|
||||
) -> Result<Option<sr25519::vrf::VrfOutput>, Error> {
|
||||
self.vrf_output::<sr25519::Pair>(key_type, public, input)
|
||||
}
|
||||
|
||||
fn ed25519_public_keys(&self, key_type: KeyTypeId) -> Vec<ed25519::Public> {
|
||||
@@ -267,7 +286,36 @@ mod tests {
|
||||
let secret_uri = "//Alice";
|
||||
let key_pair = sr25519::Pair::from_string(secret_uri, None).expect("Generates key pair");
|
||||
|
||||
let transcript = sr25519::vrf::VrfTranscript::new(
|
||||
let data = sr25519::vrf::VrfInput::new(
|
||||
b"Test",
|
||||
&[
|
||||
(b"one", &1_u64.to_le_bytes()),
|
||||
(b"two", &2_u64.to_le_bytes()),
|
||||
(b"three", "test".as_bytes()),
|
||||
],
|
||||
)
|
||||
.into_sign_data();
|
||||
|
||||
let result = store.sr25519_vrf_sign(SR25519, &key_pair.public(), &data);
|
||||
assert!(result.unwrap().is_none());
|
||||
|
||||
store
|
||||
.insert(SR25519, secret_uri, key_pair.public().as_ref())
|
||||
.expect("Inserts unknown key");
|
||||
|
||||
let result = store.sr25519_vrf_sign(SR25519, &key_pair.public(), &data);
|
||||
|
||||
assert!(result.unwrap().is_some());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn vrf_output() {
|
||||
let store = MemoryKeystore::new();
|
||||
|
||||
let secret_uri = "//Alice";
|
||||
let pair = sr25519::Pair::from_string(secret_uri, None).expect("Generates key pair");
|
||||
|
||||
let input = sr25519::vrf::VrfInput::new(
|
||||
b"Test",
|
||||
&[
|
||||
(b"one", &1_u64.to_le_bytes()),
|
||||
@@ -276,16 +324,17 @@ mod tests {
|
||||
],
|
||||
);
|
||||
|
||||
let result = store.sr25519_vrf_sign(SR25519, &key_pair.public(), &transcript);
|
||||
let result = store.sr25519_vrf_output(SR25519, &pair.public(), &input);
|
||||
assert!(result.unwrap().is_none());
|
||||
|
||||
store
|
||||
.insert(SR25519, secret_uri, key_pair.public().as_ref())
|
||||
.insert(SR25519, secret_uri, pair.public().as_ref())
|
||||
.expect("Inserts unknown key");
|
||||
|
||||
let result = store.sr25519_vrf_sign(SR25519, &key_pair.public(), &transcript);
|
||||
let preout = store.sr25519_vrf_output(SR25519, &pair.public(), &input).unwrap().unwrap();
|
||||
|
||||
assert!(result.unwrap().is_some());
|
||||
let result = preout.make_bytes::<32>(b"rand", &input, &pair.public());
|
||||
assert!(result.is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user