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:
Davide Galassi
2023-05-04 15:41:59 +02:00
committed by GitHub
parent 93165bc4d2
commit 3a90728de0
12 changed files with 384 additions and 138 deletions
@@ -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)
+9 -21
View File
@@ -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)
}
+29 -6
View File
@@ -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> {
+5 -9
View File
@@ -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()
});
+6 -7
View File
@@ -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)
}
+6 -3
View File
@@ -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();
+11 -4
View File
@@ -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 {
+17 -11
View File
@@ -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
+210 -46
View File
@@ -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);
}
}
+16 -5
View File
@@ -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>;
+60 -11
View File
@@ -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]