// 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)) }