mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-13 22:11:06 +00:00
Reorganising the repository - external renames and moves (#4074)
* Adding first rough ouline of the repository structure * Remove old CI stuff * add title * formatting fixes * move node-exits job's script to scripts dir * Move docs into subdir * move to bin * move maintainence scripts, configs and helpers into its own dir * add .local to ignore * move core->client * start up 'test' area * move test client * move test runtime * make test move compile * Add dependencies rule enforcement. * Fix indexing. * Update docs to reflect latest changes * Moving /srml->/paint * update docs * move client/sr-* -> primitives/ * clean old readme * remove old broken code in rhd * update lock * Step 1. * starting to untangle client * Fix after merge. * start splitting out client interfaces * move children and blockchain interfaces * Move trie and state-machine to primitives. * Fix WASM builds. * fixing broken imports * more interface moves * move backend and light to interfaces * move CallExecutor * move cli off client * moving around more interfaces * re-add consensus crates into the mix * fix subkey path * relieve client from executor * starting to pull out client from grandpa * move is_decendent_of out of client * grandpa still depends on client directly * lemme tests pass * rename srml->paint * Make it compile. * rename interfaces->client-api * Move keyring to primitives. * fixup libp2p dep * fix broken use * allow dependency enforcement to fail * move fork-tree * Moving wasm-builder * make env * move build-script-utils * fixup broken crate depdencies and names * fix imports for authority discovery * fix typo * update cargo.lock * fixing imports * Fix paths and add missing crates * re-add missing crates
This commit is contained in:
committed by
Bastian Köcher
parent
becc3b0a4f
commit
60e5011c72
@@ -0,0 +1,284 @@
|
||||
// Copyright 2017-2019 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Block import helpers.
|
||||
|
||||
use sr_primitives::traits::{Block as BlockT, DigestItemFor, Header as HeaderT, NumberFor};
|
||||
use sr_primitives::Justification;
|
||||
use std::borrow::Cow;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::import_queue::{Verifier, CacheKeyId};
|
||||
|
||||
/// Block import result.
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum ImportResult {
|
||||
/// Block imported.
|
||||
Imported(ImportedAux),
|
||||
/// Already in the blockchain.
|
||||
AlreadyInChain,
|
||||
/// Block or parent is known to be bad.
|
||||
KnownBad,
|
||||
/// Block parent is not in the chain.
|
||||
UnknownParent,
|
||||
/// Parent state is missing.
|
||||
MissingState,
|
||||
}
|
||||
|
||||
/// Auxiliary data associated with an imported block result.
|
||||
#[derive(Debug, Default, PartialEq, Eq)]
|
||||
pub struct ImportedAux {
|
||||
/// Only the header has been imported. Block body verification was skipped.
|
||||
pub header_only: bool,
|
||||
/// Clear all pending justification requests.
|
||||
pub clear_justification_requests: bool,
|
||||
/// Request a justification for the given block.
|
||||
pub needs_justification: bool,
|
||||
/// Received a bad justification.
|
||||
pub bad_justification: bool,
|
||||
/// Request a finality proof for the given block.
|
||||
pub needs_finality_proof: bool,
|
||||
/// Whether the block that was imported is the new best block.
|
||||
pub is_new_best: bool,
|
||||
}
|
||||
|
||||
impl ImportResult {
|
||||
/// Returns default value for `ImportResult::Imported` with
|
||||
/// `clear_justification_requests`, `needs_justification`,
|
||||
/// `bad_justification` and `needs_finality_proof` set to false.
|
||||
pub fn imported(is_new_best: bool) -> ImportResult {
|
||||
let mut aux = ImportedAux::default();
|
||||
aux.is_new_best = is_new_best;
|
||||
|
||||
ImportResult::Imported(aux)
|
||||
}
|
||||
}
|
||||
|
||||
/// Block data origin.
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
pub enum BlockOrigin {
|
||||
/// Genesis block built into the client.
|
||||
Genesis,
|
||||
/// Block is part of the initial sync with the network.
|
||||
NetworkInitialSync,
|
||||
/// Block was broadcasted on the network.
|
||||
NetworkBroadcast,
|
||||
/// Block that was received from the network and validated in the consensus process.
|
||||
ConsensusBroadcast,
|
||||
/// Block that was collated by this node.
|
||||
Own,
|
||||
/// Block was imported from a file.
|
||||
File,
|
||||
}
|
||||
|
||||
/// Fork choice strategy.
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
pub enum ForkChoiceStrategy {
|
||||
/// Longest chain fork choice.
|
||||
LongestChain,
|
||||
/// Custom fork choice rule, where true indicates the new block should be the best block.
|
||||
Custom(bool),
|
||||
}
|
||||
|
||||
/// Data required to check validity of a Block.
|
||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||
pub struct BlockCheckParams<Block: BlockT> {
|
||||
/// Hash of the block that we verify.
|
||||
pub hash: Block::Hash,
|
||||
/// Block number of the block that we verify.
|
||||
pub number: NumberFor<Block>,
|
||||
/// Parent hash of the block that we verify.
|
||||
pub parent_hash: Block::Hash,
|
||||
/// Allow importing the block skipping state verification if parent state is missing.
|
||||
pub allow_missing_state: bool,
|
||||
}
|
||||
|
||||
/// Data required to import a Block.
|
||||
pub struct BlockImportParams<Block: BlockT> {
|
||||
/// Origin of the Block
|
||||
pub origin: BlockOrigin,
|
||||
/// The header, without consensus post-digests applied. This should be in the same
|
||||
/// state as it comes out of the runtime.
|
||||
///
|
||||
/// Consensus engines which alter the header (by adding post-runtime digests)
|
||||
/// should strip those off in the initial verification process and pass them
|
||||
/// via the `post_digests` field. During block authorship, they should
|
||||
/// not be pushed to the header directly.
|
||||
///
|
||||
/// The reason for this distinction is so the header can be directly
|
||||
/// re-executed in a runtime that checks digest equivalence -- the
|
||||
/// post-runtime digests are pushed back on after.
|
||||
pub header: Block::Header,
|
||||
/// Justification provided for this block from the outside.
|
||||
pub justification: Option<Justification>,
|
||||
/// Digest items that have been added after the runtime for external
|
||||
/// work, like a consensus signature.
|
||||
pub post_digests: Vec<DigestItemFor<Block>>,
|
||||
/// Block's body
|
||||
pub body: Option<Vec<Block::Extrinsic>>,
|
||||
/// Is this block finalized already?
|
||||
/// `true` implies instant finality.
|
||||
pub finalized: bool,
|
||||
/// Auxiliary consensus data produced by the block.
|
||||
/// Contains a list of key-value pairs. If values are `None`, the keys
|
||||
/// will be deleted.
|
||||
pub auxiliary: Vec<(Vec<u8>, Option<Vec<u8>>)>,
|
||||
/// Fork choice strategy of this import. This should only be set by a
|
||||
/// synchronous import, otherwise it may race against other imports.
|
||||
pub fork_choice: ForkChoiceStrategy,
|
||||
/// Allow importing the block skipping state verification if parent state is missing.
|
||||
pub allow_missing_state: bool,
|
||||
}
|
||||
|
||||
impl<Block: BlockT> BlockImportParams<Block> {
|
||||
/// Deconstruct the justified header into parts.
|
||||
pub fn into_inner(self)
|
||||
-> (
|
||||
BlockOrigin,
|
||||
<Block as BlockT>::Header,
|
||||
Option<Justification>,
|
||||
Vec<DigestItemFor<Block>>,
|
||||
Option<Vec<<Block as BlockT>::Extrinsic>>,
|
||||
bool,
|
||||
Vec<(Vec<u8>, Option<Vec<u8>>)>,
|
||||
) {
|
||||
(
|
||||
self.origin,
|
||||
self.header,
|
||||
self.justification,
|
||||
self.post_digests,
|
||||
self.body,
|
||||
self.finalized,
|
||||
self.auxiliary,
|
||||
)
|
||||
}
|
||||
|
||||
/// Get a handle to full header (with post-digests applied).
|
||||
pub fn post_header(&self) -> Cow<Block::Header> {
|
||||
if self.post_digests.is_empty() {
|
||||
Cow::Borrowed(&self.header)
|
||||
} else {
|
||||
Cow::Owned({
|
||||
let mut hdr = self.header.clone();
|
||||
for digest_item in &self.post_digests {
|
||||
hdr.digest_mut().push(digest_item.clone());
|
||||
}
|
||||
|
||||
hdr
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Block import trait.
|
||||
pub trait BlockImport<B: BlockT> {
|
||||
type Error: ::std::error::Error + Send + 'static;
|
||||
|
||||
/// Check block preconditions.
|
||||
fn check_block(
|
||||
&mut self,
|
||||
block: BlockCheckParams<B>,
|
||||
) -> Result<ImportResult, Self::Error>;
|
||||
|
||||
/// Import a block.
|
||||
///
|
||||
/// Cached data can be accessed through the blockchain cache.
|
||||
fn import_block(
|
||||
&mut self,
|
||||
block: BlockImportParams<B>,
|
||||
cache: HashMap<CacheKeyId, Vec<u8>>,
|
||||
) -> Result<ImportResult, Self::Error>;
|
||||
}
|
||||
|
||||
impl<B: BlockT> BlockImport<B> for crate::import_queue::BoxBlockImport<B> {
|
||||
type Error = crate::error::Error;
|
||||
|
||||
/// Check block preconditions.
|
||||
fn check_block(
|
||||
&mut self,
|
||||
block: BlockCheckParams<B>,
|
||||
) -> Result<ImportResult, Self::Error> {
|
||||
(**self).check_block(block)
|
||||
}
|
||||
|
||||
/// Import a block.
|
||||
///
|
||||
/// Cached data can be accessed through the blockchain cache.
|
||||
fn import_block(
|
||||
&mut self,
|
||||
block: BlockImportParams<B>,
|
||||
cache: HashMap<CacheKeyId, Vec<u8>>,
|
||||
) -> Result<ImportResult, Self::Error> {
|
||||
(**self).import_block(block, cache)
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: BlockT, T, E: std::error::Error + Send + 'static> BlockImport<B> for Arc<T>
|
||||
where for<'r> &'r T: BlockImport<B, Error = E>
|
||||
{
|
||||
type Error = E;
|
||||
|
||||
fn check_block(
|
||||
&mut self,
|
||||
block: BlockCheckParams<B>,
|
||||
) -> Result<ImportResult, Self::Error> {
|
||||
(&**self).check_block(block)
|
||||
}
|
||||
|
||||
fn import_block(
|
||||
&mut self,
|
||||
block: BlockImportParams<B>,
|
||||
cache: HashMap<CacheKeyId, Vec<u8>>,
|
||||
) -> Result<ImportResult, Self::Error> {
|
||||
(&**self).import_block(block, cache)
|
||||
}
|
||||
}
|
||||
|
||||
/// Justification import trait
|
||||
pub trait JustificationImport<B: BlockT> {
|
||||
type Error: ::std::error::Error + Send + 'static;
|
||||
|
||||
/// Called by the import queue when it is started. Returns a list of justifications to request
|
||||
/// from the network.
|
||||
fn on_start(&mut self) -> Vec<(B::Hash, NumberFor<B>)> { Vec::new() }
|
||||
|
||||
/// Import a Block justification and finalize the given block.
|
||||
fn import_justification(
|
||||
&mut self,
|
||||
hash: B::Hash,
|
||||
number: NumberFor<B>,
|
||||
justification: Justification,
|
||||
) -> Result<(), Self::Error>;
|
||||
}
|
||||
|
||||
/// Finality proof import trait.
|
||||
pub trait FinalityProofImport<B: BlockT> {
|
||||
type Error: std::error::Error + Send + 'static;
|
||||
|
||||
/// Called by the import queue when it is started. Returns a list of finality proofs to request
|
||||
/// from the network.
|
||||
fn on_start(&mut self) -> Vec<(B::Hash, NumberFor<B>)> { Vec::new() }
|
||||
|
||||
/// Import a Block justification and finalize the given block. Returns finalized block or error.
|
||||
fn import_finality_proof(
|
||||
&mut self,
|
||||
hash: B::Hash,
|
||||
number: NumberFor<B>,
|
||||
finality_proof: Vec<u8>,
|
||||
verifier: &mut dyn Verifier<B>,
|
||||
) -> Result<(B::Hash, NumberFor<B>), Self::Error>;
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
// Copyright 2019 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Block announcement validation.
|
||||
|
||||
use crate::BlockStatus;
|
||||
use sr_primitives::{generic::BlockId, traits::Block};
|
||||
use std::{error::Error, sync::Arc};
|
||||
|
||||
/// A type which provides access to chain information.
|
||||
pub trait Chain<B: Block> {
|
||||
/// Retrieve the status of the block denoted by the given [`BlockId`].
|
||||
fn block_status(&self, id: &BlockId<B>) -> Result<BlockStatus, Box<dyn Error + Send>>;
|
||||
}
|
||||
|
||||
impl<T: Chain<B>, B: Block> Chain<B> for Arc<T> {
|
||||
fn block_status(&self, id: &BlockId<B>) -> Result<BlockStatus, Box<dyn Error + Send>> {
|
||||
(&**self).block_status(id)
|
||||
}
|
||||
}
|
||||
|
||||
/// Result of `BlockAnnounceValidator::validate`.
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum Validation {
|
||||
/// Valid block announcement.
|
||||
Success,
|
||||
/// Invalid block announcement.
|
||||
Failure,
|
||||
}
|
||||
|
||||
/// Type which checks incoming block announcements.
|
||||
pub trait BlockAnnounceValidator<B: Block> {
|
||||
/// Validate the announced header and its associated data.
|
||||
fn validate(&mut self, header: &B::Header, data: &[u8]) -> Result<Validation, Box<dyn Error + Send>>;
|
||||
}
|
||||
|
||||
/// Default implementation of `BlockAnnounceValidator`.
|
||||
#[derive(Debug)]
|
||||
pub struct DefaultBlockAnnounceValidator<C> {
|
||||
chain: C
|
||||
}
|
||||
|
||||
impl<C> DefaultBlockAnnounceValidator<C> {
|
||||
pub fn new(chain: C) -> Self {
|
||||
Self { chain }
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: Block, C: Chain<B>> BlockAnnounceValidator<B> for DefaultBlockAnnounceValidator<C> {
|
||||
fn validate(&mut self, _h: &B::Header, _d: &[u8]) -> Result<Validation, Box<dyn Error + Send>> {
|
||||
Ok(Validation::Success)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
// Copyright 2017-2019 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Error types in Consensus
|
||||
use runtime_version::RuntimeVersion;
|
||||
use primitives::ed25519::{Public, Signature};
|
||||
use std::error;
|
||||
|
||||
/// Result type alias.
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
/// Error type.
|
||||
#[derive(Debug, derive_more::Display, derive_more::From)]
|
||||
pub enum Error {
|
||||
/// Missing state at block with given descriptor.
|
||||
#[display(fmt="State unavailable at block {}", _0)]
|
||||
StateUnavailable(String),
|
||||
/// I/O terminated unexpectedly
|
||||
#[display(fmt="I/O terminated unexpectedly.")]
|
||||
IoTerminated,
|
||||
/// Unable to schedule wakeup.
|
||||
#[display(fmt="Timer error: {}", _0)]
|
||||
FaultyTimer(std::io::Error),
|
||||
/// Error while working with inherent data.
|
||||
#[display(fmt="InherentData error: {}", _0)]
|
||||
InherentData(inherents::Error),
|
||||
/// Unable to propose a block.
|
||||
#[display(fmt="Unable to create block proposal.")]
|
||||
CannotPropose,
|
||||
/// Error checking signature
|
||||
#[display(fmt="Message signature {:?} by {:?} is invalid.", _0, _1)]
|
||||
InvalidSignature(Signature, Public),
|
||||
/// Invalid authorities set received from the runtime.
|
||||
#[display(fmt="Current state of blockchain has invalid authorities set")]
|
||||
InvalidAuthoritiesSet,
|
||||
/// Account is not an authority.
|
||||
#[display(fmt="Message sender {:?} is not a valid authority.", _0)]
|
||||
InvalidAuthority(Public),
|
||||
/// Authoring interface does not match the runtime.
|
||||
#[display(fmt="Authoring for current \
|
||||
runtime is not supported. Native ({}) cannot author for on-chain ({}).", native, on_chain)]
|
||||
IncompatibleAuthoringRuntime { native: RuntimeVersion, on_chain: RuntimeVersion },
|
||||
/// Authoring interface does not match the runtime.
|
||||
#[display(fmt="Authoring for current runtime is not supported since it has no version.")]
|
||||
RuntimeVersionMissing,
|
||||
/// Authoring interface does not match the runtime.
|
||||
#[display(fmt="Authoring in current build is not supported since it has no runtime.")]
|
||||
NativeRuntimeMissing,
|
||||
/// Justification requirements not met.
|
||||
#[display(fmt="Invalid justification.")]
|
||||
InvalidJustification,
|
||||
/// Some other error.
|
||||
#[display(fmt="Other error: {}", _0)]
|
||||
Other(Box<dyn error::Error + Send>),
|
||||
/// Error from the client while importing
|
||||
#[display(fmt="Import failed: {}", _0)]
|
||||
ClientImport(String),
|
||||
/// Error from the client while importing
|
||||
#[display(fmt="Chain lookup failed: {}", _0)]
|
||||
ChainLookup(String),
|
||||
}
|
||||
|
||||
impl error::Error for Error {
|
||||
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
|
||||
match self {
|
||||
Error::FaultyTimer(ref err) => Some(err),
|
||||
Error::Other(ref err) => Some(&**err),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
// Copyright 2018-2019 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Block evaluation and evaluation errors.
|
||||
|
||||
use super::MAX_BLOCK_SIZE;
|
||||
|
||||
use codec::Encode;
|
||||
use sr_primitives::traits::{Block as BlockT, Header as HeaderT, One, CheckedConversion};
|
||||
|
||||
// This is just a best effort to encode the number. None indicated that it's too big to encode
|
||||
// in a u128.
|
||||
type BlockNumber = Option<u128>;
|
||||
|
||||
/// Result type alias.
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
/// Error type.
|
||||
#[derive(Debug, derive_more::Display)]
|
||||
pub enum Error {
|
||||
/// Proposal provided not a block.
|
||||
#[display(fmt="Proposal provided not a block: decoding error: {}", _0)]
|
||||
BadProposalFormat(codec::Error),
|
||||
/// Proposal had wrong parent hash.
|
||||
#[display(fmt="Proposal had wrong parent hash. Expected {:?}, got {:?}", expected, got)]
|
||||
WrongParentHash { expected: String, got: String },
|
||||
/// Proposal had wrong number.
|
||||
#[display(fmt="Proposal had wrong number. Expected {:?}, got {:?}", expected, got)]
|
||||
WrongNumber { expected: BlockNumber, got: BlockNumber },
|
||||
/// Proposal exceeded the maximum size.
|
||||
#[display(
|
||||
fmt="Proposal exceeded the maximum size of {} by {} bytes.",
|
||||
"MAX_BLOCK_SIZE", "_0.saturating_sub(MAX_BLOCK_SIZE)"
|
||||
)]
|
||||
ProposalTooLarge(usize),
|
||||
}
|
||||
|
||||
impl std::error::Error for Error {}
|
||||
|
||||
/// Attempt to evaluate a substrate block as a node block, returning error
|
||||
/// upon any initial validity checks failing.
|
||||
pub fn evaluate_initial<Block: BlockT>(
|
||||
proposal: &Block,
|
||||
parent_hash: &<Block as BlockT>::Hash,
|
||||
parent_number: <<Block as BlockT>::Header as HeaderT>::Number,
|
||||
) -> Result<()> {
|
||||
|
||||
let encoded = Encode::encode(proposal);
|
||||
let proposal = Block::decode(&mut &encoded[..])
|
||||
.map_err(|e| Error::BadProposalFormat(e))?;
|
||||
|
||||
if encoded.len() > MAX_BLOCK_SIZE {
|
||||
return Err(Error::ProposalTooLarge(encoded.len()))
|
||||
}
|
||||
|
||||
if *parent_hash != *proposal.header().parent_hash() {
|
||||
return Err(Error::WrongParentHash {
|
||||
expected: format!("{:?}", *parent_hash),
|
||||
got: format!("{:?}", proposal.header().parent_hash())
|
||||
});
|
||||
}
|
||||
|
||||
if parent_number + One::one() != *proposal.header().number() {
|
||||
return Err(Error::WrongNumber {
|
||||
expected: parent_number.checked_into::<u128>().map(|x| x + 1),
|
||||
got: (*proposal.header().number()).checked_into::<u128>(),
|
||||
});
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,255 @@
|
||||
// Copyright 2017-2019 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! 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 sr_primitives::{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<B> = Box<dyn BlockImport<B, Error = ConsensusError> + Send + Sync>;
|
||||
|
||||
/// Shared justification import struct used by the queue.
|
||||
pub type BoxJustificationImport<B> = Box<dyn JustificationImport<B, Error=ConsensusError> + Send + Sync>;
|
||||
|
||||
/// Shared finality proof import struct used by the queue.
|
||||
pub type BoxFinalityProofImport<B> = Box<dyn FinalityProofImport<B, Error=ConsensusError> + 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<B: BlockT> {
|
||||
/// Block header hash.
|
||||
pub hash: <B as BlockT>::Hash,
|
||||
/// Block header if requested.
|
||||
pub header: Option<<B as BlockT>::Header>,
|
||||
/// Block body if requested.
|
||||
pub body: Option<Vec<<B as BlockT>::Extrinsic>>,
|
||||
/// Justification if requested.
|
||||
pub justification: Option<Justification>,
|
||||
/// The peer, we received this from
|
||||
pub origin: Option<Origin>,
|
||||
/// Allow importing the block skipping state verification if parent state is missing.
|
||||
pub allow_missing_state: 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<B: BlockT>: 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<Justification>,
|
||||
body: Option<Vec<B::Extrinsic>>,
|
||||
) -> Result<(BlockImportParams<B>, Option<Vec<(CacheKeyId, Vec<u8>)>>), 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<B: BlockT>: Send {
|
||||
/// Import bunch of blocks.
|
||||
fn import_blocks(&mut self, origin: BlockOrigin, blocks: Vec<IncomingBlock<B>>);
|
||||
/// Import a block justification.
|
||||
fn import_justification(
|
||||
&mut self,
|
||||
who: Origin,
|
||||
hash: B::Hash,
|
||||
number: NumberFor<B>,
|
||||
justification: Justification
|
||||
);
|
||||
/// Import block finality proof.
|
||||
fn import_finality_proof(
|
||||
&mut self,
|
||||
who: Origin,
|
||||
hash: B::Hash,
|
||||
number: NumberFor<B>,
|
||||
finality_proof: Vec<u8>
|
||||
);
|
||||
|
||||
/// 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<B>);
|
||||
}
|
||||
|
||||
/// Hooks that the verification queue can use to influence the synchronization
|
||||
/// algorithm.
|
||||
pub trait Link<B: BlockT>: Send {
|
||||
/// Batch of blocks imported, with or without error.
|
||||
fn blocks_processed(
|
||||
&mut self,
|
||||
_imported: usize,
|
||||
_count: usize,
|
||||
_results: Vec<(Result<BlockImportResult<NumberFor<B>>, BlockImportError>, B::Hash)>
|
||||
) {}
|
||||
/// Justification import result.
|
||||
fn justification_imported(&mut self, _who: Origin, _hash: &B::Hash, _number: NumberFor<B>, _success: bool) {}
|
||||
/// Request a justification for the given block.
|
||||
fn request_justification(&mut self, _hash: &B::Hash, _number: NumberFor<B>) {}
|
||||
/// 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<B>),
|
||||
_finalization_result: Result<(B::Hash, NumberFor<B>), ()>,
|
||||
) {}
|
||||
/// Request a finality proof for the given block.
|
||||
fn request_finality_proof(&mut self, _hash: &B::Hash, _number: NumberFor<B>) {}
|
||||
}
|
||||
|
||||
/// Block import successful result.
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum BlockImportResult<N: ::std::fmt::Debug + PartialEq> {
|
||||
/// Imported known block.
|
||||
ImportedKnown(N),
|
||||
/// Imported unknown block.
|
||||
ImportedUnknown(N, ImportedAux, Option<Origin>),
|
||||
}
|
||||
|
||||
/// Block import error.
|
||||
#[derive(Debug)]
|
||||
pub enum BlockImportError {
|
||||
/// Block missed header, can't be imported
|
||||
IncompleteHeader(Option<Origin>),
|
||||
/// Block verification failed, can't be imported
|
||||
VerificationFailed(Option<Origin>, String),
|
||||
/// Block is known to be Bad
|
||||
BadBlock(Option<Origin>),
|
||||
/// 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<B: BlockT, V: Verifier<B>>(
|
||||
import_handle: &mut dyn BlockImport<B, Error = ConsensusError>,
|
||||
block_origin: BlockOrigin,
|
||||
block: IncomingBlock<B>,
|
||||
verifier: &mut V,
|
||||
) -> Result<BlockImportResult<NumberFor<B>>, 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,
|
||||
}))? {
|
||||
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, cache))
|
||||
}
|
||||
@@ -0,0 +1,408 @@
|
||||
// Copyright 2017-2019 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::{mem, pin::Pin, time::Duration};
|
||||
use futures::{prelude::*, channel::mpsc, task::Context, task::Poll};
|
||||
use futures_timer::Delay;
|
||||
use sr_primitives::{Justification, traits::{Block as BlockT, Header as HeaderT, NumberFor}};
|
||||
|
||||
use crate::block_import::BlockOrigin;
|
||||
use crate::import_queue::{
|
||||
BlockImportResult, BlockImportError, Verifier, BoxBlockImport, BoxFinalityProofImport,
|
||||
BoxJustificationImport, ImportQueue, Link, Origin,
|
||||
IncomingBlock, import_single_block,
|
||||
buffered_link::{self, BufferedLinkSender, BufferedLinkReceiver}
|
||||
};
|
||||
|
||||
/// Interface to a basic block import queue that is importing blocks sequentially in a separate
|
||||
/// task, with pluggable verification.
|
||||
pub struct BasicQueue<B: BlockT> {
|
||||
/// Channel to send messages to the background task.
|
||||
sender: mpsc::UnboundedSender<ToWorkerMsg<B>>,
|
||||
/// Results coming from the worker task.
|
||||
result_port: BufferedLinkReceiver<B>,
|
||||
/// If it isn't possible to spawn the future in `future_to_spawn` (which is notably the case in
|
||||
/// "no std" environment), we instead put it in `manual_poll`. It is then polled manually from
|
||||
/// `poll_actions`.
|
||||
manual_poll: Option<Pin<Box<dyn Future<Output = ()> + Send>>>,
|
||||
/// A thread pool where the background worker is being run.
|
||||
pool: Option<futures::executor::ThreadPool>,
|
||||
}
|
||||
|
||||
impl<B: BlockT> BasicQueue<B> {
|
||||
/// Instantiate a new basic queue, with given verifier.
|
||||
///
|
||||
/// This creates a background task, and calls `on_start` on the justification importer and
|
||||
/// finality proof importer.
|
||||
pub fn new<V: 'static + Verifier<B>>(
|
||||
verifier: V,
|
||||
block_import: BoxBlockImport<B>,
|
||||
justification_import: Option<BoxJustificationImport<B>>,
|
||||
finality_proof_import: Option<BoxFinalityProofImport<B>>,
|
||||
) -> Self {
|
||||
let (result_sender, result_port) = buffered_link::buffered_link();
|
||||
let (future, worker_sender) = BlockImportWorker::new(
|
||||
result_sender,
|
||||
verifier,
|
||||
block_import,
|
||||
justification_import,
|
||||
finality_proof_import,
|
||||
);
|
||||
|
||||
let mut pool = futures::executor::ThreadPool::builder()
|
||||
.name_prefix("import-queue-worker-")
|
||||
.pool_size(1)
|
||||
.create()
|
||||
.ok();
|
||||
|
||||
let manual_poll;
|
||||
if let Some(pool) = &mut pool {
|
||||
pool.spawn_ok(future);
|
||||
manual_poll = None;
|
||||
} else {
|
||||
manual_poll = Some(Box::pin(future) as Pin<Box<_>>);
|
||||
}
|
||||
|
||||
Self {
|
||||
sender: worker_sender,
|
||||
result_port,
|
||||
manual_poll,
|
||||
pool,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: BlockT> ImportQueue<B> for BasicQueue<B> {
|
||||
fn import_blocks(&mut self, origin: BlockOrigin, blocks: Vec<IncomingBlock<B>>) {
|
||||
if blocks.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
trace!(target: "sync", "Scheduling {} blocks for import", blocks.len());
|
||||
let _ = self.sender.unbounded_send(ToWorkerMsg::ImportBlocks(origin, blocks));
|
||||
}
|
||||
|
||||
fn import_justification(
|
||||
&mut self,
|
||||
who: Origin,
|
||||
hash: B::Hash,
|
||||
number: NumberFor<B>,
|
||||
justification: Justification
|
||||
) {
|
||||
let _ = self.sender.unbounded_send(ToWorkerMsg::ImportJustification(who.clone(), hash, number, justification));
|
||||
}
|
||||
|
||||
fn import_finality_proof(&mut self, who: Origin, hash: B::Hash, number: NumberFor<B>, finality_proof: Vec<u8>) {
|
||||
trace!(target: "sync", "Scheduling finality proof of {}/{} for import", number, hash);
|
||||
let _ = self.sender.unbounded_send(ToWorkerMsg::ImportFinalityProof(who, hash, number, finality_proof));
|
||||
}
|
||||
|
||||
fn poll_actions(&mut self, cx: &mut Context, link: &mut dyn Link<B>) {
|
||||
// As a backup mechanism, if we failed to spawn the `future_to_spawn`, we instead poll
|
||||
// manually here.
|
||||
if let Some(manual_poll) = self.manual_poll.as_mut() {
|
||||
match Future::poll(Pin::new(manual_poll), cx) {
|
||||
Poll::Pending => {}
|
||||
_ => self.manual_poll = None,
|
||||
}
|
||||
}
|
||||
|
||||
self.result_port.poll_actions(cx, link);
|
||||
}
|
||||
}
|
||||
|
||||
/// Message destinated to the background worker.
|
||||
#[derive(Debug)]
|
||||
enum ToWorkerMsg<B: BlockT> {
|
||||
ImportBlocks(BlockOrigin, Vec<IncomingBlock<B>>),
|
||||
ImportJustification(Origin, B::Hash, NumberFor<B>, Justification),
|
||||
ImportFinalityProof(Origin, B::Hash, NumberFor<B>, Vec<u8>),
|
||||
}
|
||||
|
||||
struct BlockImportWorker<B: BlockT> {
|
||||
result_sender: BufferedLinkSender<B>,
|
||||
justification_import: Option<BoxJustificationImport<B>>,
|
||||
finality_proof_import: Option<BoxFinalityProofImport<B>>,
|
||||
delay_between_blocks: Duration,
|
||||
}
|
||||
|
||||
impl<B: BlockT> BlockImportWorker<B> {
|
||||
fn new<V: 'static + Verifier<B>>(
|
||||
result_sender: BufferedLinkSender<B>,
|
||||
verifier: V,
|
||||
block_import: BoxBlockImport<B>,
|
||||
justification_import: Option<BoxJustificationImport<B>>,
|
||||
finality_proof_import: Option<BoxFinalityProofImport<B>>,
|
||||
) -> (impl Future<Output = ()> + Send, mpsc::UnboundedSender<ToWorkerMsg<B>>) {
|
||||
let (sender, mut port) = mpsc::unbounded();
|
||||
|
||||
let mut worker = BlockImportWorker {
|
||||
result_sender,
|
||||
justification_import,
|
||||
finality_proof_import,
|
||||
delay_between_blocks: Duration::new(0, 0),
|
||||
};
|
||||
|
||||
// Let's initialize `justification_import` and `finality_proof_import`.
|
||||
if let Some(justification_import) = worker.justification_import.as_mut() {
|
||||
for (hash, number) in justification_import.on_start() {
|
||||
worker.result_sender.request_justification(&hash, number);
|
||||
}
|
||||
}
|
||||
if let Some(finality_proof_import) = worker.finality_proof_import.as_mut() {
|
||||
for (hash, number) in finality_proof_import.on_start() {
|
||||
worker.result_sender.request_finality_proof(&hash, number);
|
||||
}
|
||||
}
|
||||
|
||||
// The future below has two possible states:
|
||||
//
|
||||
// - Currently importing many blocks, in which case `importing` is `Some` and contains a
|
||||
// `Future`, and `block_import` is `None`.
|
||||
// - Something else, in which case `block_import` is `Some` and `importing` is None.
|
||||
//
|
||||
let mut block_import_verifier = Some((block_import, verifier));
|
||||
let mut importing = None;
|
||||
|
||||
let future = futures::future::poll_fn(move |cx| {
|
||||
loop {
|
||||
// If the results sender is closed, that means that the import queue is shutting
|
||||
// down and we should end this future.
|
||||
if worker.result_sender.is_closed() {
|
||||
return Poll::Ready(())
|
||||
}
|
||||
|
||||
// If we are in the process of importing a bunch of block, let's resume this
|
||||
// process before doing anything more.
|
||||
if let Some(imp_fut) = importing.as_mut() {
|
||||
match Future::poll(Pin::new(imp_fut), cx) {
|
||||
Poll::Pending => return Poll::Pending,
|
||||
Poll::Ready((bi, verif)) => {
|
||||
block_import_verifier = Some((bi, verif));
|
||||
importing = None;
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
debug_assert!(importing.is_none());
|
||||
debug_assert!(block_import_verifier.is_some());
|
||||
|
||||
// Grab the next action request sent to the import queue.
|
||||
let msg = match Stream::poll_next(Pin::new(&mut port), cx) {
|
||||
Poll::Ready(Some(msg)) => msg,
|
||||
Poll::Ready(None) => return Poll::Ready(()),
|
||||
Poll::Pending => return Poll::Pending,
|
||||
};
|
||||
|
||||
match msg {
|
||||
ToWorkerMsg::ImportBlocks(origin, blocks) => {
|
||||
// On blocks import request, we merely *start* the process and store
|
||||
// a `Future` into `importing`.
|
||||
let (bi, verif) = block_import_verifier.take()
|
||||
.expect("block_import_verifier is always Some; qed");
|
||||
importing = Some(worker.import_a_batch_of_blocks(bi, verif, origin, blocks));
|
||||
},
|
||||
ToWorkerMsg::ImportFinalityProof(who, hash, number, proof) => {
|
||||
let (_, verif) = block_import_verifier.as_mut()
|
||||
.expect("block_import_verifier is always Some; qed");
|
||||
worker.import_finality_proof(verif, who, hash, number, proof);
|
||||
},
|
||||
ToWorkerMsg::ImportJustification(who, hash, number, justification) => {
|
||||
worker.import_justification(who, hash, number, justification);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
(future, sender)
|
||||
}
|
||||
|
||||
/// Returns a `Future` that imports the given blocks and sends the results on
|
||||
/// `self.result_sender`.
|
||||
///
|
||||
/// For lifetime reasons, the `BlockImport` implementation must be passed by value, and is
|
||||
/// yielded back in the output once the import is finished.
|
||||
fn import_a_batch_of_blocks<V: 'static + Verifier<B>>(
|
||||
&mut self,
|
||||
block_import: BoxBlockImport<B>,
|
||||
verifier: V,
|
||||
origin: BlockOrigin,
|
||||
blocks: Vec<IncomingBlock<B>>
|
||||
) -> impl Future<Output = (BoxBlockImport<B>, V)> {
|
||||
let mut result_sender = self.result_sender.clone();
|
||||
|
||||
import_many_blocks(block_import, origin, blocks, verifier, self.delay_between_blocks)
|
||||
.then(move |(imported, count, results, block_import, verifier)| {
|
||||
result_sender.blocks_processed(imported, count, results);
|
||||
future::ready((block_import, verifier))
|
||||
})
|
||||
}
|
||||
|
||||
fn import_finality_proof<V: 'static + Verifier<B>>(
|
||||
&mut self,
|
||||
verifier: &mut V,
|
||||
who: Origin,
|
||||
hash: B::Hash,
|
||||
number: NumberFor<B>,
|
||||
finality_proof: Vec<u8>
|
||||
) {
|
||||
let result = self.finality_proof_import.as_mut().map(|finality_proof_import| {
|
||||
finality_proof_import.import_finality_proof(hash, number, finality_proof, verifier)
|
||||
.map_err(|e| {
|
||||
debug!(
|
||||
"Finality proof import failed with {:?} for hash: {:?} number: {:?} coming from node: {:?}",
|
||||
e,
|
||||
hash,
|
||||
number,
|
||||
who,
|
||||
);
|
||||
})
|
||||
}).unwrap_or(Err(()));
|
||||
|
||||
trace!(target: "sync", "Imported finality proof for {}/{}", number, hash);
|
||||
self.result_sender.finality_proof_imported(who, (hash, number), result);
|
||||
}
|
||||
|
||||
fn import_justification(
|
||||
&mut self,
|
||||
who: Origin,
|
||||
hash: B::Hash,
|
||||
number: NumberFor<B>,
|
||||
justification: Justification
|
||||
) {
|
||||
let success = self.justification_import.as_mut().map(|justification_import| {
|
||||
justification_import.import_justification(hash, number, justification)
|
||||
.map_err(|e| {
|
||||
debug!(
|
||||
target: "sync",
|
||||
"Justification import failed with {:?} for hash: {:?} number: {:?} coming from node: {:?}",
|
||||
e,
|
||||
hash,
|
||||
number,
|
||||
who,
|
||||
);
|
||||
e
|
||||
}).is_ok()
|
||||
}).unwrap_or(false);
|
||||
|
||||
self.result_sender.justification_imported(who, &hash, number, success);
|
||||
}
|
||||
}
|
||||
|
||||
/// Import several blocks at once, returning import result for each block.
|
||||
///
|
||||
/// For lifetime reasons, the `BlockImport` implementation must be passed by value, and is yielded
|
||||
/// back in the output once the import is finished.
|
||||
///
|
||||
/// The returned `Future` yields at every imported block, which makes the execution more
|
||||
/// fine-grained and making it possible to interrupt the process.
|
||||
fn import_many_blocks<B: BlockT, V: Verifier<B>>(
|
||||
import_handle: BoxBlockImport<B>,
|
||||
blocks_origin: BlockOrigin,
|
||||
blocks: Vec<IncomingBlock<B>>,
|
||||
verifier: V,
|
||||
delay_between_blocks: Duration,
|
||||
) -> impl Future<Output = (usize, usize, Vec<(
|
||||
Result<BlockImportResult<NumberFor<B>>, BlockImportError>,
|
||||
B::Hash,
|
||||
)>, BoxBlockImport<B>, V)> {
|
||||
let count = blocks.len();
|
||||
|
||||
let blocks_range = match (
|
||||
blocks.first().and_then(|b| b.header.as_ref().map(|h| h.number())),
|
||||
blocks.last().and_then(|b| b.header.as_ref().map(|h| h.number())),
|
||||
) {
|
||||
(Some(first), Some(last)) if first != last => format!(" ({}..{})", first, last),
|
||||
(Some(first), Some(_)) => format!(" ({})", first),
|
||||
_ => Default::default(),
|
||||
};
|
||||
|
||||
trace!(target: "sync", "Starting import of {} blocks {}", count, blocks_range);
|
||||
|
||||
let mut imported = 0;
|
||||
let mut results = vec![];
|
||||
let mut has_error = false;
|
||||
let mut blocks = blocks.into_iter();
|
||||
let mut import_handle = Some(import_handle);
|
||||
let mut waiting = None;
|
||||
let mut verifier = Some(verifier);
|
||||
|
||||
// Blocks in the response/drain should be in ascending order.
|
||||
|
||||
future::poll_fn(move |cx| {
|
||||
// Handle the optional timer that makes us wait before the next import.
|
||||
if let Some(waiting) = &mut waiting {
|
||||
match Future::poll(Pin::new(waiting), cx) {
|
||||
Poll::Ready(_) => {},
|
||||
Poll::Pending => return Poll::Pending,
|
||||
}
|
||||
}
|
||||
waiting = None;
|
||||
|
||||
// Is there any block left to import?
|
||||
let block = match blocks.next() {
|
||||
Some(b) => b,
|
||||
None => {
|
||||
// No block left to import, success!
|
||||
let import_handle = import_handle.take()
|
||||
.expect("Future polled again after it has finished");
|
||||
let verifier = verifier.take()
|
||||
.expect("Future polled again after it has finished");
|
||||
let results = mem::replace(&mut results, Vec::new());
|
||||
return Poll::Ready((imported, count, results, import_handle, verifier));
|
||||
},
|
||||
};
|
||||
|
||||
// We extract the content of `import_handle` and `verifier` only when the future ends,
|
||||
// therefore `import_handle` and `verifier` are always `Some` here. It is illegal to poll
|
||||
// a `Future` again after it has ended.
|
||||
let import_handle = import_handle.as_mut()
|
||||
.expect("Future polled again after it has finished");
|
||||
let verifier = verifier.as_mut()
|
||||
.expect("Future polled again after it has finished");
|
||||
|
||||
let block_number = block.header.as_ref().map(|h| h.number().clone());
|
||||
let block_hash = block.hash;
|
||||
let import_result = if has_error {
|
||||
Err(BlockImportError::Cancelled)
|
||||
} else {
|
||||
// The actual import.
|
||||
import_single_block(
|
||||
&mut **import_handle,
|
||||
blocks_origin.clone(),
|
||||
block,
|
||||
verifier,
|
||||
)
|
||||
};
|
||||
|
||||
if import_result.is_ok() {
|
||||
trace!(target: "sync", "Block imported successfully {:?} ({})", block_number, block_hash);
|
||||
imported += 1;
|
||||
} else {
|
||||
has_error = true;
|
||||
}
|
||||
|
||||
results.push((import_result, block_hash));
|
||||
|
||||
// Notifies the current task again so that we re-execute this closure again for the next
|
||||
// block.
|
||||
if delay_between_blocks != Duration::new(0, 0) {
|
||||
waiting = Some(Delay::new(delay_between_blocks));
|
||||
}
|
||||
cx.waker().wake_by_ref();
|
||||
Poll::Pending
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,173 @@
|
||||
// Copyright 2017-2019 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Provides the `buffered_link` utility.
|
||||
//!
|
||||
//! The buffered link is a channel that allows buffering the method calls on `Link`.
|
||||
//!
|
||||
//! # Example
|
||||
//!
|
||||
//! ```
|
||||
//! use substrate_consensus_common::import_queue::Link;
|
||||
//! # use substrate_consensus_common::import_queue::buffered_link::buffered_link;
|
||||
//! # use test_client::runtime::Block;
|
||||
//! # struct DummyLink; impl Link<Block> for DummyLink {}
|
||||
//! # let mut my_link = DummyLink;
|
||||
//! let (mut tx, mut rx) = buffered_link::<Block>();
|
||||
//! tx.blocks_processed(0, 0, vec![]);
|
||||
//!
|
||||
//! // Calls `my_link.blocks_processed(0, 0, vec![])` when polled.
|
||||
//! let _fut = futures::future::poll_fn(move |cx| {
|
||||
//! rx.poll_actions(cx, &mut my_link);
|
||||
//! std::task::Poll::Pending::<()>
|
||||
//! });
|
||||
//! ```
|
||||
//!
|
||||
|
||||
use futures::{prelude::*, channel::mpsc};
|
||||
use sr_primitives::traits::{Block as BlockT, NumberFor};
|
||||
use std::{pin::Pin, task::Context, task::Poll};
|
||||
use crate::import_queue::{Origin, Link, BlockImportResult, BlockImportError};
|
||||
|
||||
/// Wraps around an unbounded channel from the `futures` crate. The sender implements `Link` and
|
||||
/// can be used to buffer commands, and the receiver can be used to poll said commands and transfer
|
||||
/// them to another link.
|
||||
pub fn buffered_link<B: BlockT>() -> (BufferedLinkSender<B>, BufferedLinkReceiver<B>) {
|
||||
let (tx, rx) = mpsc::unbounded();
|
||||
let tx = BufferedLinkSender { tx };
|
||||
let rx = BufferedLinkReceiver { rx };
|
||||
(tx, rx)
|
||||
}
|
||||
|
||||
/// See [`buffered_link`].
|
||||
pub struct BufferedLinkSender<B: BlockT> {
|
||||
tx: mpsc::UnboundedSender<BlockImportWorkerMsg<B>>,
|
||||
}
|
||||
|
||||
impl<B: BlockT> BufferedLinkSender<B> {
|
||||
/// Returns true if the sender points to nowhere.
|
||||
///
|
||||
/// Once `true` is returned, it is pointless to use the sender anymore.
|
||||
pub fn is_closed(&self) -> bool {
|
||||
self.tx.is_closed()
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: BlockT> Clone for BufferedLinkSender<B> {
|
||||
fn clone(&self) -> Self {
|
||||
BufferedLinkSender {
|
||||
tx: self.tx.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Internal buffered message.
|
||||
enum BlockImportWorkerMsg<B: BlockT> {
|
||||
BlocksProcessed(usize, usize, Vec<(Result<BlockImportResult<NumberFor<B>>, BlockImportError>, B::Hash)>),
|
||||
JustificationImported(Origin, B::Hash, NumberFor<B>, bool),
|
||||
RequestJustification(B::Hash, NumberFor<B>),
|
||||
FinalityProofImported(Origin, (B::Hash, NumberFor<B>), Result<(B::Hash, NumberFor<B>), ()>),
|
||||
RequestFinalityProof(B::Hash, NumberFor<B>),
|
||||
}
|
||||
|
||||
impl<B: BlockT> Link<B> for BufferedLinkSender<B> {
|
||||
fn blocks_processed(
|
||||
&mut self,
|
||||
imported: usize,
|
||||
count: usize,
|
||||
results: Vec<(Result<BlockImportResult<NumberFor<B>>, BlockImportError>, B::Hash)>
|
||||
) {
|
||||
let _ = self.tx.unbounded_send(BlockImportWorkerMsg::BlocksProcessed(imported, count, results));
|
||||
}
|
||||
|
||||
fn justification_imported(
|
||||
&mut self,
|
||||
who: Origin,
|
||||
hash: &B::Hash,
|
||||
number: NumberFor<B>,
|
||||
success: bool
|
||||
) {
|
||||
let msg = BlockImportWorkerMsg::JustificationImported(who, hash.clone(), number, success);
|
||||
let _ = self.tx.unbounded_send(msg);
|
||||
}
|
||||
|
||||
fn request_justification(&mut self, hash: &B::Hash, number: NumberFor<B>) {
|
||||
let _ = self.tx.unbounded_send(BlockImportWorkerMsg::RequestJustification(hash.clone(), number));
|
||||
}
|
||||
|
||||
fn finality_proof_imported(
|
||||
&mut self,
|
||||
who: Origin,
|
||||
request_block: (B::Hash, NumberFor<B>),
|
||||
finalization_result: Result<(B::Hash, NumberFor<B>), ()>,
|
||||
) {
|
||||
let msg = BlockImportWorkerMsg::FinalityProofImported(who, request_block, finalization_result);
|
||||
let _ = self.tx.unbounded_send(msg);
|
||||
}
|
||||
|
||||
fn request_finality_proof(&mut self, hash: &B::Hash, number: NumberFor<B>) {
|
||||
let _ = self.tx.unbounded_send(BlockImportWorkerMsg::RequestFinalityProof(hash.clone(), number));
|
||||
}
|
||||
}
|
||||
|
||||
/// See [`buffered_link`].
|
||||
pub struct BufferedLinkReceiver<B: BlockT> {
|
||||
rx: mpsc::UnboundedReceiver<BlockImportWorkerMsg<B>>,
|
||||
}
|
||||
|
||||
impl<B: BlockT> BufferedLinkReceiver<B> {
|
||||
/// Polls for the buffered link actions. Any enqueued action will be propagated to the link
|
||||
/// passed as parameter.
|
||||
///
|
||||
/// 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`.
|
||||
pub fn poll_actions(&mut self, cx: &mut Context, link: &mut dyn Link<B>) {
|
||||
loop {
|
||||
let msg = if let Poll::Ready(Some(msg)) = Stream::poll_next(Pin::new(&mut self.rx), cx) {
|
||||
msg
|
||||
} else {
|
||||
break
|
||||
};
|
||||
|
||||
match msg {
|
||||
BlockImportWorkerMsg::BlocksProcessed(imported, count, results) =>
|
||||
link.blocks_processed(imported, count, results),
|
||||
BlockImportWorkerMsg::JustificationImported(who, hash, number, success) =>
|
||||
link.justification_imported(who, &hash, number, success),
|
||||
BlockImportWorkerMsg::RequestJustification(hash, number) =>
|
||||
link.request_justification(&hash, number),
|
||||
BlockImportWorkerMsg::FinalityProofImported(who, block, result) =>
|
||||
link.finality_proof_imported(who, block, result),
|
||||
BlockImportWorkerMsg::RequestFinalityProof(hash, number) =>
|
||||
link.request_finality_proof(&hash, number),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use test_client::runtime::Block;
|
||||
|
||||
#[test]
|
||||
fn is_closed() {
|
||||
let (tx, rx) = super::buffered_link::<Block>();
|
||||
assert!(!tx.is_closed());
|
||||
drop(rx);
|
||||
assert!(tx.is_closed());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
// Copyright 2018-2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate Consensus Common.
|
||||
|
||||
// Substrate Demo 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 Consensus Common 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 Consensus Common. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Common utilities for building and using consensus engines in substrate.
|
||||
//!
|
||||
//! Much of this crate is _unstable_ and thus the API is likely to undergo
|
||||
//! change. Implementors of traits should not rely on the interfaces to remain
|
||||
//! the same.
|
||||
|
||||
// This provides "unused" building blocks to other crates
|
||||
#![allow(dead_code)]
|
||||
|
||||
// our error-chain could potentially blow up otherwise
|
||||
#![recursion_limit="128"]
|
||||
|
||||
#[macro_use] extern crate log;
|
||||
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
|
||||
use sr_primitives::traits::{Block as BlockT, DigestFor};
|
||||
use futures::prelude::*;
|
||||
pub use inherents::InherentData;
|
||||
|
||||
pub mod block_validation;
|
||||
pub mod offline_tracker;
|
||||
pub mod error;
|
||||
pub mod block_import;
|
||||
mod select_chain;
|
||||
pub mod import_queue;
|
||||
pub mod evaluation;
|
||||
|
||||
// block size limit.
|
||||
const MAX_BLOCK_SIZE: usize = 4 * 1024 * 1024 + 512;
|
||||
|
||||
pub use self::error::Error;
|
||||
pub use block_import::{
|
||||
BlockImport, BlockOrigin, ForkChoiceStrategy, ImportedAux, BlockImportParams, BlockCheckParams, ImportResult,
|
||||
JustificationImport, FinalityProofImport,
|
||||
};
|
||||
pub use select_chain::SelectChain;
|
||||
|
||||
/// Block status.
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum BlockStatus {
|
||||
/// Added to the import queue.
|
||||
Queued,
|
||||
/// Already in the blockchain and the state is available.
|
||||
InChainWithState,
|
||||
/// In the blockchain, but the state is not available.
|
||||
InChainPruned,
|
||||
/// Block or parent is known to be bad.
|
||||
KnownBad,
|
||||
/// Not in the queue or the blockchain.
|
||||
Unknown,
|
||||
}
|
||||
|
||||
/// Environment producer for a Consensus instance. Creates proposer instance and communication streams.
|
||||
pub trait Environment<B: BlockT> {
|
||||
/// The proposer type this creates.
|
||||
type Proposer: Proposer<B>;
|
||||
/// Error which can occur upon creation.
|
||||
type Error: From<Error>;
|
||||
|
||||
/// Initialize the proposal logic on top of a specific header. Provide
|
||||
/// the authorities at that header.
|
||||
fn init(&mut self, parent_header: &B::Header)
|
||||
-> Result<Self::Proposer, Self::Error>;
|
||||
}
|
||||
|
||||
/// Logic for a proposer.
|
||||
///
|
||||
/// This will encapsulate creation and evaluation of proposals at a specific
|
||||
/// block.
|
||||
///
|
||||
/// Proposers are generic over bits of "consensus data" which are engine-specific.
|
||||
pub trait Proposer<B: BlockT> {
|
||||
/// Error type which can occur when proposing or evaluating.
|
||||
type Error: From<Error> + ::std::fmt::Debug + 'static;
|
||||
/// Future that resolves to a committed proposal.
|
||||
type Create: Future<Output = Result<B, Self::Error>>;
|
||||
/// Create a proposal.
|
||||
fn propose(
|
||||
&mut self,
|
||||
inherent_data: InherentData,
|
||||
inherent_digests: DigestFor<B>,
|
||||
max_duration: Duration,
|
||||
) -> Self::Create;
|
||||
}
|
||||
|
||||
/// An oracle for when major synchronization work is being undertaken.
|
||||
///
|
||||
/// Generally, consensus authoring work isn't undertaken while well behind
|
||||
/// the head of the chain.
|
||||
pub trait SyncOracle {
|
||||
/// Whether the synchronization service is undergoing major sync.
|
||||
/// Returns true if so.
|
||||
fn is_major_syncing(&mut self) -> bool;
|
||||
/// Whether the synchronization service is offline.
|
||||
/// Returns true if so.
|
||||
fn is_offline(&mut self) -> bool;
|
||||
}
|
||||
|
||||
/// A synchronization oracle for when there is no network.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct NoNetwork;
|
||||
|
||||
impl SyncOracle for NoNetwork {
|
||||
fn is_major_syncing(&mut self) -> bool { false }
|
||||
fn is_offline(&mut self) -> bool { false }
|
||||
}
|
||||
|
||||
impl<T> SyncOracle for Arc<T>
|
||||
where T: ?Sized, for<'r> &'r T: SyncOracle
|
||||
{
|
||||
fn is_major_syncing(&mut self) -> bool {
|
||||
<&T>::is_major_syncing(&mut &**self)
|
||||
}
|
||||
fn is_offline(&mut self) -> bool {
|
||||
<&T>::is_offline(&mut &**self)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
// Copyright 2018-2019 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Tracks offline validators.
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::time::{Instant, Duration};
|
||||
|
||||
// time before we report a validator.
|
||||
const REPORT_TIME: Duration = Duration::from_secs(60 * 5);
|
||||
|
||||
struct Observed {
|
||||
last_round_end: Instant,
|
||||
offline_since: Instant,
|
||||
}
|
||||
|
||||
impl Observed {
|
||||
fn new() -> Observed {
|
||||
let now = Instant::now();
|
||||
Observed {
|
||||
last_round_end: now,
|
||||
offline_since: now,
|
||||
}
|
||||
}
|
||||
|
||||
fn note_round_end(&mut self, was_online: bool) {
|
||||
let now = Instant::now();
|
||||
|
||||
self.last_round_end = now;
|
||||
if was_online {
|
||||
self.offline_since = now;
|
||||
}
|
||||
}
|
||||
|
||||
fn is_active(&self) -> bool {
|
||||
// can happen if clocks are not monotonic
|
||||
if self.offline_since > self.last_round_end { return true }
|
||||
self.last_round_end.duration_since(self.offline_since) < REPORT_TIME
|
||||
}
|
||||
}
|
||||
|
||||
/// Tracks offline validators and can issue a report for those offline.
|
||||
pub struct OfflineTracker<AuthorityId> {
|
||||
observed: HashMap<AuthorityId, Observed>,
|
||||
}
|
||||
|
||||
impl<AuthorityId: Eq + Clone + std::hash::Hash> OfflineTracker<AuthorityId> {
|
||||
/// Create a new tracker.
|
||||
pub fn new() -> Self {
|
||||
OfflineTracker { observed: HashMap::new() }
|
||||
}
|
||||
|
||||
/// Note new consensus is starting with the given set of validators.
|
||||
pub fn note_new_block(&mut self, validators: &[AuthorityId]) {
|
||||
use std::collections::HashSet;
|
||||
|
||||
let set: HashSet<_> = validators.iter().cloned().collect();
|
||||
self.observed.retain(|k, _| set.contains(k));
|
||||
}
|
||||
|
||||
/// Note that a round has ended.
|
||||
pub fn note_round_end(&mut self, validator: AuthorityId, was_online: bool) {
|
||||
self.observed.entry(validator)
|
||||
.or_insert_with(Observed::new)
|
||||
.note_round_end(was_online);
|
||||
}
|
||||
|
||||
/// Generate a vector of indices for offline account IDs.
|
||||
pub fn reports(&self, validators: &[AuthorityId]) -> Vec<u32> {
|
||||
validators.iter()
|
||||
.enumerate()
|
||||
.filter_map(|(i, v)| if self.is_online(v) {
|
||||
None
|
||||
} else {
|
||||
Some(i as u32)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Whether reports on a validator set are consistent with our view of things.
|
||||
pub fn check_consistency(&self, validators: &[AuthorityId], reports: &[u32]) -> bool {
|
||||
reports.iter().cloned().all(|r| {
|
||||
let v = match validators.get(r as usize) {
|
||||
Some(v) => v,
|
||||
None => return false,
|
||||
};
|
||||
|
||||
// we must think all validators reported externally are offline.
|
||||
let thinks_online = self.is_online(v);
|
||||
!thinks_online
|
||||
})
|
||||
}
|
||||
|
||||
fn is_online(&self, v: &AuthorityId) -> bool {
|
||||
self.observed.get(v).map(Observed::is_active).unwrap_or(true)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn validator_offline() {
|
||||
let mut tracker = OfflineTracker::<u64>::new();
|
||||
let v1 = 1;
|
||||
let v2 = 2;
|
||||
let v3 = 3;
|
||||
tracker.note_round_end(v1, true);
|
||||
tracker.note_round_end(v2, true);
|
||||
tracker.note_round_end(v3, true);
|
||||
|
||||
let slash_time = REPORT_TIME + Duration::from_secs(5);
|
||||
tracker.observed.get_mut(&v1).unwrap().offline_since -= slash_time;
|
||||
tracker.observed.get_mut(&v2).unwrap().offline_since -= slash_time;
|
||||
|
||||
assert_eq!(tracker.reports(&[v1, v2, v3]), vec![0, 1]);
|
||||
|
||||
tracker.note_new_block(&[v1, v3]);
|
||||
assert_eq!(tracker.reports(&[v1, v2, v3]), vec![0]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
// Copyright 2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate Consensus Common.
|
||||
|
||||
// Substrate Demo 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 Consensus Common 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 Consensus Common. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::error::Error;
|
||||
use sr_primitives::traits::{Block as BlockT, NumberFor};
|
||||
|
||||
|
||||
/// The SelectChain trait defines the strategy upon which the head is chosen
|
||||
/// if multiple forks are present for an opaque definition of "best" in the
|
||||
/// specific chain build.
|
||||
///
|
||||
/// The Strategy can be customised for the two use cases of authoring new blocks
|
||||
/// upon the best chain or which fork to finalise. Unless implemented differently
|
||||
/// by default finalisation methods fall back to use authoring, so as a minimum
|
||||
/// `_authoring`-functions must be implemented.
|
||||
///
|
||||
/// Any particular user must make explicit, however, whether they intend to finalise
|
||||
/// or author through the using the right function call, as these might differ in
|
||||
/// some implementations.
|
||||
///
|
||||
/// Non-deterministicly finalising chains may only use the `_authoring` functions.
|
||||
pub trait SelectChain<Block: BlockT>: Sync + Send + Clone {
|
||||
|
||||
/// Get all leaves of the chain: block hashes that have no children currently.
|
||||
/// Leaves that can never be finalized will not be returned.
|
||||
fn leaves(&self) -> Result<Vec<<Block as BlockT>::Hash>, Error>;
|
||||
|
||||
/// Among those `leaves` deterministically pick one chain as the generally
|
||||
/// best chain to author new blocks upon and probably finalize.
|
||||
fn best_chain(&self) -> Result<<Block as BlockT>::Header, Error>;
|
||||
|
||||
/// Get the best descendent of `target_hash` that we should attempt to
|
||||
/// finalize next, if any. It is valid to return the given `target_hash`
|
||||
/// itself if no better descendent exists.
|
||||
fn finality_target(
|
||||
&self,
|
||||
target_hash: <Block as BlockT>::Hash,
|
||||
_maybe_max_number: Option<NumberFor<Block>>
|
||||
) -> Result<Option<<Block as BlockT>::Hash>, Error> {
|
||||
Ok(Some(target_hash))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user