use crate::header;
use alloc::vec::Vec;
use core::{num::NonZero, time::Duration};
pub struct VerifyConfig<'a, TAuthList> {
pub header: header::HeaderRef<'a>,
pub block_number_bytes: usize,
pub parent_block_header: header::HeaderRef<'a>,
pub now_from_unix_epoch: Duration,
pub current_authorities: TAuthList,
pub slot_duration: NonZero<u64>,
pub allow_equal_slot_number: bool,
}
#[derive(Debug)]
pub struct VerifySuccess {
pub authorities_change: Option<Vec<header::AuraAuthority>>,
}
#[derive(Debug, derive_more::Display, derive_more::Error)]
pub enum VerifyError {
MissingSeal,
MissingPreRuntimeDigest,
ParentIsntAuraConsensus,
SlotNumberNotIncreasing,
TooFarInFuture,
BadSignature,
BadPublicKey,
EmptyAuthorities,
MultipleAuthoritiesChangeDigestItems,
}
pub fn verify_header<'a>(
mut config: VerifyConfig<'a, impl ExactSizeIterator<Item = header::AuraAuthorityRef<'a>>>,
) -> Result<VerifySuccess, VerifyError> {
let slot_number = config
.header
.digest
.aura_pre_runtime()
.ok_or(VerifyError::MissingPreRuntimeDigest)?
.slot_number;
if config.parent_block_header.number != 0 {
let parent_slot_number = match config.parent_block_header.digest.aura_pre_runtime() {
Some(pr) => pr.slot_number,
None => return Err(VerifyError::ParentIsntAuraConsensus),
};
if config.allow_equal_slot_number {
if slot_number < parent_slot_number {
return Err(VerifyError::SlotNumberNotIncreasing);
}
} else if slot_number <= parent_slot_number {
return Err(VerifyError::SlotNumberNotIncreasing);
}
}
{
const TOLERANCE: Duration = Duration::from_secs(30);
let current_slot =
(config.now_from_unix_epoch + TOLERANCE).as_secs() * 1000 / config.slot_duration.get();
if slot_number > current_slot {
return Err(VerifyError::TooFarInFuture);
}
};
let mut authorities_change = None;
for digest_item in config.header.digest.logs() {
if let header::DigestItemRef::AuraConsensus(
header::AuraConsensusLogRef::AuthoritiesChange(new_list),
) = digest_item
{
if authorities_change.is_some() {
return Err(VerifyError::MultipleAuthoritiesChangeDigestItems);
}
authorities_change = Some(new_list.map(Into::into).collect());
}
}
let (seal_bytes, pre_seal_hash) = {
let mut unsealed_header = config.header;
let seal_bytes: [u8; 64] = match unsealed_header.digest.pop_seal() {
Some(header::Seal::Aura(seal)) => *seal,
_ => return Err(VerifyError::MissingSeal),
};
(seal_bytes, unsealed_header.hash(config.block_number_bytes))
};
if config.current_authorities.len() == 0 {
return Err(VerifyError::EmptyAuthorities);
}
let signing_authority = usize::try_from(
slot_number % u64::try_from(config.current_authorities.len()).unwrap_or(u64::MAX),
)
.unwrap_or_else(|_| unreachable!());
let authority_public_key = config
.current_authorities
.nth(signing_authority)
.unwrap()
.public_key;
let sr25519_ok = schnorrkel::PublicKey::from_bytes(authority_public_key)
.and_then(|pk| {
pk.verify_simple(
b"substrate",
&pre_seal_hash,
&schnorrkel::Signature::from_bytes(&seal_bytes)?,
)
})
.is_ok();
if !sr25519_ok {
ed25519_zebra::VerificationKey::try_from(*authority_public_key)
.map_err(|_| VerifyError::BadSignature)?
.verify(&ed25519_zebra::Signature::from(seal_bytes), &pre_seal_hash)
.map_err(|_| VerifyError::BadSignature)?;
}
Ok(VerifySuccess { authorities_change })
}