ckb_verification/
block_verifier.rsuse crate::{
transaction_verifier::NonContextualTransactionVerifier, BlockErrorKind, CellbaseError,
};
use ckb_chain_spec::consensus::Consensus;
use ckb_error::Error;
use ckb_types::{
core::{BlockView, ScriptHashType},
packed::{CellInput, CellbaseWitness},
prelude::*,
};
use ckb_verification_traits::Verifier;
use std::collections::HashSet;
#[derive(Clone)]
pub struct BlockVerifier<'a> {
consensus: &'a Consensus,
}
impl<'a> BlockVerifier<'a> {
pub fn new(consensus: &'a Consensus) -> Self {
BlockVerifier { consensus }
}
}
impl<'a> Verifier for BlockVerifier<'a> {
type Target = BlockView;
fn verify(&self, target: &BlockView) -> Result<(), Error> {
let max_block_proposals_limit = self.consensus.max_block_proposals_limit();
let max_block_bytes = self.consensus.max_block_bytes();
BlockProposalsLimitVerifier::new(max_block_proposals_limit).verify(target)?;
BlockBytesVerifier::new(max_block_bytes).verify(target)?;
CellbaseVerifier::new().verify(target)?;
DuplicateVerifier::new().verify(target)?;
MerkleRootVerifier::new().verify(target)
}
}
#[derive(Clone)]
pub struct CellbaseVerifier {}
impl CellbaseVerifier {
pub fn new() -> Self {
CellbaseVerifier {}
}
pub fn verify(&self, block: &BlockView) -> Result<(), Error> {
if block.is_genesis() {
return Ok(());
}
let cellbase_len = block
.transactions()
.iter()
.filter(|tx| tx.is_cellbase())
.count();
if cellbase_len != 1 {
return Err((CellbaseError::InvalidQuantity).into());
}
let cellbase_transaction = &block.transactions()[0];
if !cellbase_transaction.is_cellbase() {
return Err((CellbaseError::InvalidPosition).into());
}
if cellbase_transaction.outputs().len() > 1
|| cellbase_transaction.outputs_data().len() > 1
|| cellbase_transaction.outputs().len() != cellbase_transaction.outputs_data().len()
{
return Err((CellbaseError::InvalidOutputQuantity).into());
}
if !cellbase_transaction
.outputs_data()
.get(0)
.map(|data| data.is_empty())
.unwrap_or(true)
{
return Err((CellbaseError::InvalidOutputData).into());
}
if cellbase_transaction
.witnesses()
.get(0)
.and_then(|witness| {
CellbaseWitness::from_slice(&witness.raw_data())
.ok()
.and_then(|cellbase_witness| {
ScriptHashType::try_from(cellbase_witness.lock().hash_type()).ok()
})
})
.is_none()
{
return Err((CellbaseError::InvalidWitness).into());
}
if cellbase_transaction
.outputs()
.into_iter()
.any(|output| output.type_().is_some())
{
return Err((CellbaseError::InvalidTypeScript).into());
}
let cellbase_input = &cellbase_transaction
.inputs()
.get(0)
.expect("cellbase should have input");
if cellbase_input != &CellInput::new_cellbase_input(block.header().number()) {
return Err((CellbaseError::InvalidInput).into());
}
Ok(())
}
}
#[derive(Clone)]
pub struct DuplicateVerifier {}
impl DuplicateVerifier {
pub fn new() -> Self {
DuplicateVerifier {}
}
pub fn verify(&self, block: &BlockView) -> Result<(), Error> {
let mut seen = HashSet::with_capacity(block.transactions().len());
if !block.transactions().iter().all(|tx| seen.insert(tx.hash())) {
return Err((BlockErrorKind::CommitTransactionDuplicate).into());
}
let mut seen = HashSet::with_capacity(block.data().proposals().len());
if !block
.data()
.proposals()
.into_iter()
.all(|id| seen.insert(id))
{
return Err((BlockErrorKind::ProposalTransactionDuplicate).into());
}
Ok(())
}
}
#[derive(Clone, Default)]
pub struct MerkleRootVerifier {}
impl MerkleRootVerifier {
pub fn new() -> Self {
MerkleRootVerifier::default()
}
pub fn verify(&self, block: &BlockView) -> Result<(), Error> {
if block.transactions_root() != block.calc_transactions_root() {
return Err(BlockErrorKind::TransactionsRoot.into());
}
if block.proposals_hash() != block.calc_proposals_hash() {
return Err(BlockErrorKind::ProposalTransactionsHash.into());
}
Ok(())
}
}
#[derive(Clone)]
pub struct BlockProposalsLimitVerifier {
block_proposals_limit: u64,
}
impl BlockProposalsLimitVerifier {
pub fn new(block_proposals_limit: u64) -> Self {
BlockProposalsLimitVerifier {
block_proposals_limit,
}
}
pub fn verify(&self, block: &BlockView) -> Result<(), Error> {
let proposals_len = block.data().proposals().len() as u64;
if proposals_len <= self.block_proposals_limit {
Ok(())
} else {
Err(BlockErrorKind::ExceededMaximumProposalsLimit.into())
}
}
}
#[derive(Clone)]
pub struct BlockBytesVerifier {
block_bytes_limit: u64,
}
impl BlockBytesVerifier {
pub fn new(block_bytes_limit: u64) -> Self {
BlockBytesVerifier { block_bytes_limit }
}
pub fn verify(&self, block: &BlockView) -> Result<(), Error> {
if block.is_genesis() {
return Ok(());
}
let block_bytes = block.data().serialized_size_without_uncle_proposals() as u64;
if block_bytes <= self.block_bytes_limit {
Ok(())
} else {
Err(BlockErrorKind::ExceededMaximumBlockBytes.into())
}
}
}
pub struct NonContextualBlockTxsVerifier<'a> {
consensus: &'a Consensus,
}
impl<'a> NonContextualBlockTxsVerifier<'a> {
pub fn new(consensus: &'a Consensus) -> Self {
NonContextualBlockTxsVerifier { consensus }
}
pub fn verify(&self, block: &BlockView) -> Result<Vec<()>, Error> {
block
.transactions()
.iter()
.map(|tx| NonContextualTransactionVerifier::new(tx, self.consensus).verify())
.collect()
}
}