use std::{
fmt::{Debug, Display, Error as FmtError, Formatter},
sync::Arc,
};
use parity_scale_codec::Encode;
use sc_client_api::HeaderBackend;
use sc_consensus_aura::{find_pre_digest, standalone::PreDigestLookupError, CompatibleDigestItem};
use sp_consensus_aura::sr25519::AuthorityPair;
use sp_consensus_slots::Slot;
use sp_core::Pair;
use sp_runtime::traits::{Header as SubstrateHeader, Zero};
use crate::{
phron_primitives::{AuthoritySignature, Block, BlockNumber, Header, MILLISECS_PER_BLOCK},
session_map::AuthorityProvider,
sync::{
substrate::{
verification::{cache::CacheError, verifier::SessionVerificationError},
InnerJustification, Justification,
},
Verifier,
},
};
mod cache;
mod verifier;
pub use cache::VerifierCache;
const HEADER_VERIFICATION_SLOT_OFFSET: u64 = 10;
pub trait FinalizationInfo {
fn finalized_number(&self) -> BlockNumber;
}
pub struct SubstrateFinalizationInfo<BE: HeaderBackend<Block>>(Arc<BE>);
impl<BE: HeaderBackend<Block>> SubstrateFinalizationInfo<BE> {
pub fn new(client: Arc<BE>) -> Self {
Self(client)
}
}
impl<BE: HeaderBackend<Block>> FinalizationInfo for SubstrateFinalizationInfo<BE> {
fn finalized_number(&self) -> BlockNumber {
self.0.info().finalized_number
}
}
#[derive(Debug)]
pub enum HeaderVerificationError {
PreDigestLookupError(PreDigestLookupError),
HeaderTooNew(Slot),
IncorrectGenesis,
MissingSeal,
IncorrectSeal,
MissingAuthorityData,
IncorrectAuthority,
}
impl Display for HeaderVerificationError {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> {
use HeaderVerificationError::*;
match self {
PreDigestLookupError(e) => write!(f, "pre digest lookup error, {e}"),
HeaderTooNew(slot) => write!(f, "slot {slot} too far in the future"),
IncorrectGenesis => write!(f, "incorrect genesis header"),
MissingSeal => write!(f, "missing seal"),
IncorrectSeal => write!(f, "incorrect seal"),
MissingAuthorityData => write!(f, "missing authority data"),
IncorrectAuthority => write!(f, "incorrect authority"),
}
}
}
#[derive(Debug)]
pub enum VerificationError {
Verification(SessionVerificationError),
Cache(CacheError),
HeaderVerification(HeaderVerificationError),
}
impl From<SessionVerificationError> for VerificationError {
fn from(e: SessionVerificationError) -> Self {
VerificationError::Verification(e)
}
}
impl From<CacheError> for VerificationError {
fn from(e: CacheError) -> Self {
VerificationError::Cache(e)
}
}
impl Display for VerificationError {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> {
use VerificationError::*;
match self {
Verification(e) => write!(f, "{e}"),
Cache(e) => write!(f, "{e}"),
HeaderVerification(e) => write!(f, "{e}"),
}
}
}
impl<AP, FS> Verifier<Justification> for VerifierCache<AP, FS, Header>
where
AP: AuthorityProvider,
FS: FinalizationInfo,
{
type Error = VerificationError;
fn verify_justification(
&mut self,
justification: Justification,
) -> Result<Justification, Self::Error> {
let header = &justification.header;
match &justification.inner_justification {
InnerJustification::PhronJustification(phron_justification) => {
let verifier = self.get(*header.number())?;
verifier.verify_bytes(phron_justification, header.hash().encode())?;
Ok(justification)
}
InnerJustification::Genesis => match header == self.genesis_header() {
true => Ok(justification),
false => Err(Self::Error::Cache(CacheError::BadGenesisHeader)),
},
}
}
fn verify_header(&mut self, mut header: Header) -> Result<Header, Self::Error> {
use HeaderVerificationError::*;
if header.number().is_zero() {
return match &header == self.genesis_header() {
true => Ok(header),
false => Err(Self::Error::HeaderVerification(IncorrectGenesis)),
};
}
let parent_number = header.number() - 1;
let slot = find_pre_digest::<Block, AuthoritySignature>(&header)
.map_err(|e| Self::Error::HeaderVerification(PreDigestLookupError(e)))?;
let slot_now = Slot::from_timestamp(
sp_timestamp::Timestamp::current(),
sp_consensus_slots::SlotDuration::from_millis(MILLISECS_PER_BLOCK),
);
if slot > slot_now + HEADER_VERIFICATION_SLOT_OFFSET {
return Err(Self::Error::HeaderVerification(HeaderTooNew(slot)));
}
let seal = header
.digest_mut()
.pop()
.ok_or(Self::Error::HeaderVerification(MissingSeal))?;
let sig = seal
.as_aura_seal()
.ok_or(Self::Error::HeaderVerification(IncorrectSeal))?;
let authorities = self
.get_aura_authorities(parent_number)
.map_err(|_| Self::Error::HeaderVerification(MissingAuthorityData))?;
let idx = *slot % (authorities.len() as u64);
let author = authorities
.get(idx as usize)
.expect("idx < authorities.len()");
if !AuthorityPair::verify(&sig, header.hash().as_ref(), author) {
return Err(Self::Error::HeaderVerification(IncorrectAuthority));
}
header.digest_mut().push(seal);
Ok(header)
}
}