VRF refactory (#13889)

* First iteration to encapsulate schnorrkel and merlin usage

* Remove schnorkel direct dependency from BABE pallet

* Remove schnorrkel direct dependency from BABE client

* Trivial renaming for VrfTranscript data and value

* Better errors

* Expose a function to get a schnorrkel friendly transcript

* Keep the vrf signature stuff together (preventing some clones around)

* Fix tests

* Remove vrf agnostic transcript and define it as an associated type for VrfSigner and VrfVerifier

* Fix babe pallet mock

* Inner types are required to be public for polkadot

* Update client/consensus/babe/src/verification.rs

Co-authored-by: Koute <koute@users.noreply.github.com>

* Nit

* Remove Deref implementations

* make_bytes as a method

* Trigger CI

---------

Co-authored-by: Koute <koute@users.noreply.github.com>
This commit is contained in:
Davide Galassi
2023-04-19 11:11:47 +02:00
committed by GitHub
parent d9ad6feac0
commit bb394e08ac
28 changed files with 473 additions and 717 deletions
@@ -18,8 +18,9 @@
//! Verification for BABE headers.
use crate::{
authorship::{calculate_primary_threshold, check_primary_threshold, secondary_slot_author},
babe_err, find_pre_digest, BlockT, Epoch, Error, LOG_TARGET,
authorship::{calculate_primary_threshold, secondary_slot_author},
babe_err, find_pre_digest, BlockT, Epoch, Error, AUTHORING_SCORE_LENGTH,
AUTHORING_SCORE_VRF_CONTEXT, LOG_TARGET,
};
use log::{debug, trace};
use sc_consensus_epochs::Epoch as EpochT;
@@ -32,7 +33,10 @@ use sp_consensus_babe::{
make_transcript, AuthorityId, AuthorityPair, AuthoritySignature,
};
use sp_consensus_slots::Slot;
use sp_core::{ByteArray, Pair};
use sp_core::{
crypto::{VrfVerifier, Wraps},
Pair,
};
use sp_runtime::{traits::Header, DigestItem};
/// BABE verification parameters
@@ -155,7 +159,7 @@ fn check_primary_header<B: BlockT + Sized>(
epoch: &Epoch,
c: (u64, u64),
) -> Result<(), Error<B>> {
let author = &epoch.authorities[pre_digest.authority_index as usize].0;
let authority_id = &epoch.authorities[pre_digest.authority_index as usize].0;
let mut epoch_index = epoch.epoch_index;
if epoch.end_slot() <= pre_digest.slot {
@@ -163,28 +167,34 @@ fn check_primary_header<B: BlockT + Sized>(
epoch_index = epoch.clone_for_slot(pre_digest.slot).epoch_index;
}
if AuthorityPair::verify(&signature, pre_hash, author) {
let (inout, _) = {
let transcript = make_transcript(&epoch.randomness, pre_digest.slot, epoch_index);
schnorrkel::PublicKey::from_bytes(author.as_slice())
.and_then(|p| {
p.vrf_verify(transcript, &pre_digest.vrf_output, &pre_digest.vrf_proof)
})
.map_err(|s| babe_err(Error::VRFVerificationFailed(s)))?
};
let threshold =
calculate_primary_threshold(c, &epoch.authorities, pre_digest.authority_index as usize);
if !check_primary_threshold(&inout, threshold) {
return Err(babe_err(Error::VRFVerificationOfBlockFailed(author.clone(), threshold)))
}
Ok(())
} else {
Err(babe_err(Error::BadSignature(pre_hash)))
if !AuthorityPair::verify(&signature, pre_hash, authority_id) {
return Err(babe_err(Error::BadSignature(pre_hash)))
}
let transcript = make_transcript(&epoch.randomness, pre_digest.slot, epoch_index);
if !authority_id.as_inner_ref().vrf_verify(&transcript, &pre_digest.vrf_signature) {
return Err(babe_err(Error::VrfVerificationFailed))
}
let threshold =
calculate_primary_threshold(c, &epoch.authorities, pre_digest.authority_index as usize);
let score = authority_id
.as_inner_ref()
.make_bytes::<[u8; AUTHORING_SCORE_LENGTH]>(
AUTHORING_SCORE_VRF_CONTEXT,
&transcript,
&pre_digest.vrf_signature.output,
)
.map(u128::from_le_bytes)
.map_err(|_| babe_err(Error::VrfVerificationFailed))?;
if score >= threshold {
return Err(babe_err(Error::VrfThresholdExceeded(threshold)))
}
Ok(())
}
/// Check a secondary slot proposal header. We validate that the given header is
@@ -197,8 +207,7 @@ fn check_secondary_plain_header<B: BlockT>(
signature: AuthoritySignature,
epoch: &Epoch,
) -> Result<(), Error<B>> {
// check the signature is valid under the expected authority and
// chain state.
// check the signature is valid under the expected authority and chain state.
let expected_author =
secondary_slot_author(pre_digest.slot, &epoch.authorities, epoch.randomness)
.ok_or(Error::NoSecondaryAuthorExpected)?;
@@ -209,11 +218,11 @@ fn check_secondary_plain_header<B: BlockT>(
return Err(Error::InvalidAuthor(expected_author.clone(), author.clone()))
}
if AuthorityPair::verify(&signature, pre_hash.as_ref(), author) {
Ok(())
} else {
Err(Error::BadSignature(pre_hash))
if !AuthorityPair::verify(&signature, pre_hash.as_ref(), author) {
return Err(Error::BadSignature(pre_hash))
}
Ok(())
}
/// Check a secondary VRF slot proposal header.
@@ -223,8 +232,7 @@ fn check_secondary_vrf_header<B: BlockT>(
signature: AuthoritySignature,
epoch: &Epoch,
) -> Result<(), Error<B>> {
// check the signature is valid under the expected authority and
// chain state.
// check the signature is valid under the expected authority and chain state.
let expected_author =
secondary_slot_author(pre_digest.slot, &epoch.authorities, epoch.randomness)
.ok_or(Error::NoSecondaryAuthorExpected)?;
@@ -241,15 +249,15 @@ fn check_secondary_vrf_header<B: BlockT>(
epoch_index = epoch.clone_for_slot(pre_digest.slot).epoch_index;
}
if AuthorityPair::verify(&signature, pre_hash.as_ref(), author) {
let transcript = make_transcript(&epoch.randomness, pre_digest.slot, epoch_index);
schnorrkel::PublicKey::from_bytes(author.as_slice())
.and_then(|p| p.vrf_verify(transcript, &pre_digest.vrf_output, &pre_digest.vrf_proof))
.map_err(|s| babe_err(Error::VRFVerificationFailed(s)))?;
Ok(())
} else {
Err(Error::BadSignature(pre_hash))
if !AuthorityPair::verify(&signature, pre_hash.as_ref(), author) {
return Err(Error::BadSignature(pre_hash))
}
let transcript = make_transcript(&epoch.randomness, pre_digest.slot, epoch_index);
if !author.as_inner_ref().vrf_verify(&transcript, &pre_digest.vrf_signature) {
return Err(Error::VrfVerificationFailed)
}
Ok(())
}