// Copyright 2017-2020 Parity Technologies (UK) Ltd.
// This file is part of Substrate.
// Substrate is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Substrate is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see .
//! Import Queue primitive: something which can verify and import blocks.
//!
//! This serves as an intermediate and abstracted step between synchronization
//! and import. Each mode of consensus will have its own requirements for block
//! verification. Some algorithms can verify in parallel, while others only
//! sequentially.
//!
//! The `ImportQueue` trait allows such verification strategies to be
//! instantiated. The `BasicQueue` and `BasicVerifier` traits allow serial
//! queues to be instantiated simply.
use std::collections::HashMap;
use sp_runtime::{Justification, traits::{Block as BlockT, Header as _, NumberFor}};
use crate::error::Error as ConsensusError;
use crate::block_import::{
BlockImport, BlockOrigin, BlockImportParams, ImportedAux, JustificationImport, ImportResult,
BlockCheckParams, FinalityProofImport,
};
pub use basic_queue::BasicQueue;
mod basic_queue;
pub mod buffered_link;
/// Shared block import struct used by the queue.
pub type BoxBlockImport = Box<
dyn BlockImport + Send + Sync
>;
/// Shared justification import struct used by the queue.
pub type BoxJustificationImport = Box + Send + Sync>;
/// Shared finality proof import struct used by the queue.
pub type BoxFinalityProofImport = Box<
dyn FinalityProofImport + Send + Sync
>;
/// Maps to the Origin used by the network.
pub type Origin = libp2p::PeerId;
/// Block data used by the queue.
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct IncomingBlock {
/// Block header hash.
pub hash: ::Hash,
/// Block header if requested.
pub header: Option<::Header>,
/// Block body if requested.
pub body: Option::Extrinsic>>,
/// Justification if requested.
pub justification: Option,
/// The peer, we received this from
pub origin: Option,
/// Allow importing the block skipping state verification if parent state is missing.
pub allow_missing_state: bool,
/// Re-validate existing block.
pub import_existing: bool,
}
/// Type of keys in the blockchain cache that consensus module could use for its needs.
pub type CacheKeyId = [u8; 4];
/// Verify a justification of a block
pub trait Verifier: Send + Sync {
/// Verify the given data and return the BlockImportParams and an optional
/// new set of validators to import. If not, err with an Error-Message
/// presented to the User in the logs.
fn verify(
&mut self,
origin: BlockOrigin,
header: B::Header,
justification: Option,
body: Option>,
) -> Result<(BlockImportParams, Option)>>), String>;
}
/// Blocks import queue API.
///
/// The `import_*` methods can be called in order to send elements for the import queue to verify.
/// Afterwards, call `poll_actions` to determine how to respond to these elements.
pub trait ImportQueue: Send {
/// Import bunch of blocks.
fn import_blocks(&mut self, origin: BlockOrigin, blocks: Vec>);
/// Import a block justification.
fn import_justification(
&mut self,
who: Origin,
hash: B::Hash,
number: NumberFor,
justification: Justification
);
/// Import block finality proof.
fn import_finality_proof(
&mut self,
who: Origin,
hash: B::Hash,
number: NumberFor,
finality_proof: Vec
);
/// Polls for actions to perform on the network.
///
/// This method should behave in a way similar to `Future::poll`. It can register the current
/// task and notify later when more actions are ready to be polled. To continue the comparison,
/// it is as if this method always returned `Poll::Pending`.
fn poll_actions(&mut self, cx: &mut futures::task::Context, link: &mut dyn Link);
}
/// Hooks that the verification queue can use to influence the synchronization
/// algorithm.
pub trait Link: Send {
/// Batch of blocks imported, with or without error.
fn blocks_processed(
&mut self,
_imported: usize,
_count: usize,
_results: Vec<(Result>, BlockImportError>, B::Hash)>
) {}
/// Justification import result.
fn justification_imported(&mut self, _who: Origin, _hash: &B::Hash, _number: NumberFor, _success: bool) {}
/// Request a justification for the given block.
fn request_justification(&mut self, _hash: &B::Hash, _number: NumberFor) {}
/// Finality proof import result.
///
/// Even though we have asked for finality proof of block A, provider could return proof of
/// some earlier block B, if the proof for A was too large. The sync module should continue
/// asking for proof of A in this case.
fn finality_proof_imported(
&mut self,
_who: Origin,
_request_block: (B::Hash, NumberFor),
_finalization_result: Result<(B::Hash, NumberFor), ()>,
) {}
/// Request a finality proof for the given block.
fn request_finality_proof(&mut self, _hash: &B::Hash, _number: NumberFor) {}
}
/// Block import successful result.
#[derive(Debug, PartialEq)]
pub enum BlockImportResult {
/// Imported known block.
ImportedKnown(N),
/// Imported unknown block.
ImportedUnknown(N, ImportedAux, Option),
}
/// Block import error.
#[derive(Debug)]
pub enum BlockImportError {
/// Block missed header, can't be imported
IncompleteHeader(Option),
/// Block verification failed, can't be imported
VerificationFailed(Option, String),
/// Block is known to be Bad
BadBlock(Option),
/// Parent state is missing.
MissingState,
/// Block has an unknown parent
UnknownParent,
/// Block import has been cancelled. This can happen if the parent block fails to be imported.
Cancelled,
/// Other error.
Other(ConsensusError),
}
/// Single block import function.
pub fn import_single_block, Transaction>(
import_handle: &mut dyn BlockImport,
block_origin: BlockOrigin,
block: IncomingBlock,
verifier: &mut V,
) -> Result>, BlockImportError> {
let peer = block.origin;
let (header, justification) = match (block.header, block.justification) {
(Some(header), justification) => (header, justification),
(None, _) => {
if let Some(ref peer) = peer {
debug!(target: "sync", "Header {} was not provided by {} ", block.hash, peer);
} else {
debug!(target: "sync", "Header {} was not provided ", block.hash);
}
return Err(BlockImportError::IncompleteHeader(peer))
},
};
trace!(target: "sync", "Header {} has {:?} logs", block.hash, header.digest().logs().len());
let number = header.number().clone();
let hash = header.hash();
let parent_hash = header.parent_hash().clone();
let import_error = |e| {
match e {
Ok(ImportResult::AlreadyInChain) => {
trace!(target: "sync", "Block already in chain {}: {:?}", number, hash);
Ok(BlockImportResult::ImportedKnown(number))
},
Ok(ImportResult::Imported(aux)) => Ok(BlockImportResult::ImportedUnknown(number, aux, peer.clone())),
Ok(ImportResult::MissingState) => {
debug!(target: "sync", "Parent state is missing for {}: {:?}, parent: {:?}", number, hash, parent_hash);
Err(BlockImportError::MissingState)
},
Ok(ImportResult::UnknownParent) => {
debug!(target: "sync", "Block with unknown parent {}: {:?}, parent: {:?}", number, hash, parent_hash);
Err(BlockImportError::UnknownParent)
},
Ok(ImportResult::KnownBad) => {
debug!(target: "sync", "Peer gave us a bad block {}: {:?}", number, hash);
Err(BlockImportError::BadBlock(peer.clone()))
},
Err(e) => {
debug!(target: "sync", "Error importing block {}: {:?}: {:?}", number, hash, e);
Err(BlockImportError::Other(e))
}
}
};
match import_error(import_handle.check_block(BlockCheckParams {
hash,
number,
parent_hash,
allow_missing_state: block.allow_missing_state,
import_existing: block.import_existing,
}))? {
BlockImportResult::ImportedUnknown { .. } => (),
r => return Ok(r), // Any other successful result means that the block is already imported.
}
let (mut import_block, maybe_keys) = verifier.verify(block_origin, header, justification, block.body)
.map_err(|msg| {
if let Some(ref peer) = peer {
trace!(target: "sync", "Verifying {}({}) from {} failed: {}", number, hash, peer, msg);
} else {
trace!(target: "sync", "Verifying {}({}) failed: {}", number, hash, msg);
}
BlockImportError::VerificationFailed(peer.clone(), msg)
})?;
let mut cache = HashMap::new();
if let Some(keys) = maybe_keys {
cache.extend(keys.into_iter());
}
import_block.allow_missing_state = block.allow_missing_state;
import_error(import_handle.import_block(import_block.convert_transaction(), cache))
}