use crate::finality::decode;
use alloc::vec::Vec;
use core::{cmp, iter, mem};
use rand_chacha::{
ChaCha20Rng,
rand_core::{RngCore as _, SeedableRng as _},
};
#[derive(Debug)]
pub struct CommitVerifyConfig<C> {
pub commit: C,
pub block_number_bytes: usize,
pub expected_authorities_set_id: u64,
pub num_authorities: u32,
pub randomness_seed: [u8; 32],
}
#[must_use]
pub enum CommitVerify<C> {
IsAuthority(CommitVerifyIsAuthority<C>),
IsParent(CommitVerifyIsParent<C>),
Finished(Result<(), CommitVerifyError>),
FinishedUnknown,
}
pub fn verify_commit<C: AsRef<[u8]>>(config: CommitVerifyConfig<C>) -> CommitVerify<C> {
let decoded_commit =
match decode::decode_grandpa_commit(config.commit.as_ref(), config.block_number_bytes) {
Ok(c) => c,
Err(_) => return CommitVerify::Finished(Err(CommitVerifyError::InvalidFormat)),
};
if decoded_commit.set_id != config.expected_authorities_set_id {
return CommitVerify::Finished(Err(CommitVerifyError::BadSetId));
}
if decoded_commit.auth_data.len() != decoded_commit.precommits.len() {
return CommitVerify::Finished(Err(CommitVerifyError::InvalidFormat));
}
let mut randomness = ChaCha20Rng::from_seed(config.randomness_seed);
{
let mut unique = hashbrown::HashSet::with_capacity_and_hasher(
decoded_commit.auth_data.len(),
crate::util::SipHasherBuild::new({
let mut seed = [0; 16];
randomness.fill_bytes(&mut seed);
seed
}),
);
if let Some((_, faulty_pub_key)) = decoded_commit
.auth_data
.iter()
.find(|(_, pubkey)| !unique.insert(pubkey))
{
return CommitVerify::Finished(Err(CommitVerifyError::DuplicateSignature {
authority_key: **faulty_pub_key,
}));
}
}
CommitVerification {
commit: config.commit,
block_number_bytes: config.block_number_bytes,
next_precommit_index: 0,
next_precommit_author_verified: false,
next_precommit_block_verified: false,
num_verified_signatures: 0,
num_authorities: config.num_authorities,
signatures_batch: ed25519_zebra::batch::Verifier::new(),
randomness,
}
.resume()
}
#[must_use]
pub struct CommitVerifyIsAuthority<C> {
inner: CommitVerification<C>,
}
impl<C: AsRef<[u8]>> CommitVerifyIsAuthority<C> {
pub fn authority_public_key(&self) -> &[u8; 32] {
debug_assert!(!self.inner.next_precommit_author_verified);
let decoded_commit = decode::decode_grandpa_commit(
self.inner.commit.as_ref(),
self.inner.block_number_bytes,
)
.unwrap();
decoded_commit.auth_data[self.inner.next_precommit_index].1
}
pub fn resume(mut self, is_authority: bool) -> CommitVerify<C> {
if !is_authority {
let key = *self.authority_public_key();
return CommitVerify::Finished(Err(CommitVerifyError::NotAuthority {
authority_key: key,
}));
}
self.inner.next_precommit_author_verified = true;
self.inner.resume()
}
}
#[must_use]
pub struct CommitVerifyIsParent<C> {
inner: CommitVerification<C>,
block_number: u64,
}
impl<C: AsRef<[u8]>> CommitVerifyIsParent<C> {
pub fn block_number(&self) -> u64 {
self.block_number
}
pub fn block_hash(&self) -> &[u8; 32] {
debug_assert!(!self.inner.next_precommit_block_verified);
let decoded_commit = decode::decode_grandpa_commit(
self.inner.commit.as_ref(),
self.inner.block_number_bytes,
)
.unwrap();
decoded_commit.precommits[self.inner.next_precommit_index].target_hash
}
pub fn target_block_number(&self) -> u64 {
let decoded_commit = decode::decode_grandpa_commit(
self.inner.commit.as_ref(),
self.inner.block_number_bytes,
)
.unwrap();
decoded_commit.target_number
}
pub fn target_block_hash(&self) -> &[u8; 32] {
let decoded_commit = decode::decode_grandpa_commit(
self.inner.commit.as_ref(),
self.inner.block_number_bytes,
)
.unwrap();
decoded_commit.target_hash
}
pub fn resume(mut self, is_parent: Option<bool>) -> CommitVerify<C> {
match is_parent {
None => {}
Some(true) => self.inner.num_verified_signatures += 1,
Some(false) => {
return CommitVerify::Finished(Err(CommitVerifyError::BadAncestry));
}
}
self.inner.next_precommit_block_verified = true;
self.inner.resume()
}
}
struct CommitVerification<C> {
commit: C,
block_number_bytes: usize,
next_precommit_index: usize,
next_precommit_author_verified: bool,
next_precommit_block_verified: bool,
num_verified_signatures: usize,
num_authorities: u32,
signatures_batch: ed25519_zebra::batch::Verifier,
randomness: ChaCha20Rng,
}
impl<C: AsRef<[u8]>> CommitVerification<C> {
fn resume(mut self) -> CommitVerify<C> {
let decoded_commit =
decode::decode_grandpa_commit(self.commit.as_ref(), self.block_number_bytes).unwrap();
loop {
if let Some(precommit) = decoded_commit.precommits.get(self.next_precommit_index) {
if !self.next_precommit_author_verified {
return CommitVerify::IsAuthority(CommitVerifyIsAuthority { inner: self });
}
if !self.next_precommit_block_verified {
if precommit.target_hash == decoded_commit.target_hash
&& precommit.target_number == decoded_commit.target_number
{
self.next_precommit_block_verified = true;
} else {
return CommitVerify::IsParent(CommitVerifyIsParent {
block_number: precommit.target_number,
inner: self,
});
}
}
let authority_public_key = decoded_commit.auth_data[self.next_precommit_index].1;
let signature = decoded_commit.auth_data[self.next_precommit_index].0;
let mut msg = Vec::with_capacity(1 + 32 + self.block_number_bytes + 8 + 8);
msg.push(1u8); msg.extend_from_slice(&precommit.target_hash[..]);
msg.extend_from_slice(
&precommit.target_number.to_le_bytes()[..cmp::min(
mem::size_of_val(&precommit.target_number),
self.block_number_bytes,
)],
);
msg.extend(
iter::repeat(0).take(
self.block_number_bytes
.saturating_sub(mem::size_of_val(&precommit.target_number)),
),
);
msg.extend_from_slice(&u64::to_le_bytes(decoded_commit.round_number)[..]);
msg.extend_from_slice(&u64::to_le_bytes(decoded_commit.set_id)[..]);
debug_assert_eq!(msg.len(), msg.capacity());
self.signatures_batch
.queue(ed25519_zebra::batch::Item::from((
ed25519_zebra::VerificationKeyBytes::from(*authority_public_key),
ed25519_zebra::Signature::from(*signature),
&msg,
)));
self.next_precommit_index += 1;
self.next_precommit_author_verified = false;
self.next_precommit_block_verified = false;
} else {
debug_assert!(!self.next_precommit_author_verified);
debug_assert!(!self.next_precommit_block_verified);
if decoded_commit.precommits.len()
< (usize::try_from(self.num_authorities).unwrap() * 2 / 3) + 1
{
return CommitVerify::FinishedUnknown;
}
match self.signatures_batch.verify(&mut self.randomness) {
Ok(()) => {}
Err(_) => return CommitVerify::Finished(Err(CommitVerifyError::BadSignature)),
}
return CommitVerify::Finished(Ok(()));
}
}
}
}
#[derive(Debug, derive_more::Display, derive_more::Error)]
pub enum CommitVerifyError {
InvalidFormat,
BadSetId,
BadPublicKey,
BadSignature,
#[display("One authority has produced two signatures")]
DuplicateSignature { authority_key: [u8; 32] },
#[display("One of the public keys isn't in the list of authorities")]
NotAuthority { authority_key: [u8; 32] },
BadAncestry,
}
#[derive(Debug)]
pub struct JustificationVerifyConfig<J, I> {
pub justification: J,
pub block_number_bytes: usize,
pub authorities_set_id: u64,
pub authorities_list: I,
pub randomness_seed: [u8; 32],
}
pub fn verify_justification<'a>(
config: JustificationVerifyConfig<impl AsRef<[u8]>, impl Iterator<Item = &'a [u8]>>,
) -> Result<(), JustificationVerifyError> {
let decoded_justification = match decode::decode_grandpa_justification(
config.justification.as_ref(),
config.block_number_bytes,
) {
Ok(c) => c,
Err(_) => return Err(JustificationVerifyError::InvalidFormat),
};
let num_precommits = decoded_justification.precommits.iter().count();
let mut randomness = ChaCha20Rng::from_seed(config.randomness_seed);
let mut authorities_list = {
let mut list = hashbrown::HashMap::<&[u8], _, _>::with_capacity_and_hasher(
0,
crate::util::SipHasherBuild::new({
let mut seed = [0; 16];
randomness.fill_bytes(&mut seed);
seed
}),
);
for authority in config.authorities_list {
list.insert(authority, false);
}
list
};
if num_precommits < (authorities_list.len() * 2 / 3) + 1 {
return Err(JustificationVerifyError::NotEnoughSignatures);
}
let mut batch = ed25519_zebra::batch::Verifier::new();
for precommit in decoded_justification.precommits.iter() {
match authorities_list.entry(precommit.authority_public_key) {
hashbrown::hash_map::Entry::Occupied(mut entry) => {
if entry.insert(true) {
return Err(JustificationVerifyError::DuplicateSignature {
authority_key: *precommit.authority_public_key,
});
}
}
hashbrown::hash_map::Entry::Vacant(_) => {
return Err(JustificationVerifyError::NotAuthority {
authority_key: *precommit.authority_public_key,
});
}
}
let mut msg = Vec::with_capacity(1 + 32 + 4 + 8 + 8);
msg.push(1u8); msg.extend_from_slice(&precommit.target_hash[..]);
msg.extend_from_slice(
&precommit.target_number.to_le_bytes()[..cmp::min(
mem::size_of_val(&precommit.target_number),
config.block_number_bytes,
)],
);
msg.extend(
iter::repeat(0).take(
config
.block_number_bytes
.saturating_sub(mem::size_of_val(&precommit.target_number)),
),
);
msg.extend_from_slice(&u64::to_le_bytes(decoded_justification.round)[..]);
msg.extend_from_slice(&u64::to_le_bytes(config.authorities_set_id)[..]);
debug_assert_eq!(msg.len(), msg.capacity());
batch.queue(ed25519_zebra::batch::Item::from((
ed25519_zebra::VerificationKeyBytes::from(*precommit.authority_public_key),
ed25519_zebra::Signature::from(*precommit.signature),
&msg,
)));
}
batch
.verify(&mut randomness)
.map_err(|_| JustificationVerifyError::BadSignature)?;
Ok(())
}
#[derive(Debug, derive_more::Display, derive_more::Error)]
pub enum JustificationVerifyError {
InvalidFormat,
BadPublicKey,
BadSignature,
#[display("One authority has produced two signatures")]
DuplicateSignature { authority_key: [u8; 32] },
#[display("One of the public keys isn't in the list of authorities")]
NotAuthority { authority_key: [u8; 32] },
NotEnoughSignatures,
}