use super::Verifier;
use crate::{
BlockVersionError, NumberError, PowError, TimestampError, UnknownParentError,
ALLOWED_FUTURE_BLOCKTIME,
};
use ckb_chain_spec::consensus::Consensus;
use ckb_error::Error;
use ckb_pow::PowEngine;
use ckb_traits::BlockMedianTimeContext;
use ckb_types::core::{HeaderView, Version};
use faketime::unix_time_as_millis;
use std::marker::PhantomData;
pub trait HeaderResolver {
fn header(&self) -> &HeaderView;
fn parent(&self) -> Option<&HeaderView>;
}
pub struct HeaderVerifier<'a, T, M> {
block_median_time_context: &'a M,
consensus: &'a Consensus,
_phantom: PhantomData<T>,
}
impl<'a, T, M: BlockMedianTimeContext> HeaderVerifier<'a, T, M> {
pub fn new(block_median_time_context: &'a M, consensus: &'a Consensus) -> Self {
HeaderVerifier {
consensus,
block_median_time_context,
_phantom: PhantomData,
}
}
}
impl<'a, T: HeaderResolver, M: BlockMedianTimeContext> Verifier for HeaderVerifier<'a, T, M> {
type Target = T;
fn verify(&self, target: &T) -> Result<(), Error> {
let header = target.header();
VersionVerifier::new(header, self.consensus.block_version()).verify()?;
PowVerifier::new(header, self.consensus.pow_engine().as_ref()).verify()?;
let parent = target.parent().ok_or_else(|| UnknownParentError {
parent_hash: header.parent_hash(),
})?;
NumberVerifier::new(parent, header).verify()?;
TimestampVerifier::new(self.block_median_time_context, header).verify()?;
Ok(())
}
}
pub struct VersionVerifier<'a> {
header: &'a HeaderView,
block_version: Version,
}
impl<'a> VersionVerifier<'a> {
pub fn new(header: &'a HeaderView, block_version: Version) -> Self {
VersionVerifier {
header,
block_version,
}
}
pub fn verify(&self) -> Result<(), Error> {
if self.header.version() != self.block_version {
return Err(BlockVersionError {
expected: self.block_version,
actual: self.header.version(),
}
.into());
}
Ok(())
}
}
pub struct TimestampVerifier<'a, M> {
header: &'a HeaderView,
block_median_time_context: &'a M,
now: u64,
}
impl<'a, M: BlockMedianTimeContext> TimestampVerifier<'a, M> {
pub fn new(block_median_time_context: &'a M, header: &'a HeaderView) -> Self {
TimestampVerifier {
block_median_time_context,
header,
now: unix_time_as_millis(),
}
}
pub fn verify(&self) -> Result<(), Error> {
if self.header.is_genesis() {
return Ok(());
}
let min = self
.block_median_time_context
.block_median_time(&self.header.data().raw().parent_hash());
if self.header.timestamp() <= min {
return Err(TimestampError::BlockTimeTooOld {
min,
actual: self.header.timestamp(),
}
.into());
}
let max = self.now + ALLOWED_FUTURE_BLOCKTIME;
if self.header.timestamp() > max {
return Err(TimestampError::BlockTimeTooNew {
max,
actual: self.header.timestamp(),
}
.into());
}
Ok(())
}
}
pub struct NumberVerifier<'a> {
parent: &'a HeaderView,
header: &'a HeaderView,
}
impl<'a> NumberVerifier<'a> {
pub fn new(parent: &'a HeaderView, header: &'a HeaderView) -> Self {
NumberVerifier { parent, header }
}
pub fn verify(&self) -> Result<(), Error> {
if self.header.number() != self.parent.number() + 1 {
return Err(NumberError {
expected: self.parent.number() + 1,
actual: self.header.number(),
}
.into());
}
Ok(())
}
}
pub struct PowVerifier<'a> {
header: &'a HeaderView,
pow: &'a dyn PowEngine,
}
impl<'a> PowVerifier<'a> {
pub fn new(header: &'a HeaderView, pow: &'a dyn PowEngine) -> Self {
PowVerifier { header, pow }
}
pub fn verify(&self) -> Result<(), Error> {
if self.pow.verify(&self.header.data()) {
Ok(())
} else {
Err(PowError::InvalidNonce.into())
}
}
}