Generalize the Consensus Infrastructure (#883)

* Split out Consensus
* Supply ImportQueue through network-service
  - simplify ImportQueue.import_blocks
  - remove Deadlock on import_block
  - Adding Verifier-Trait
  - Implement import_queue provisioning in service; allow cli to import
* Allow to actually customize import queue
* Consensus Gossip: Cache Message hash per Topic
This commit is contained in:
Benjamin Kampmann
2018-10-16 13:40:33 +02:00
committed by GitHub
parent a24e61cb29
commit ac4bcf879f
61 changed files with 1937 additions and 3306 deletions
+2 -3
View File
@@ -18,8 +18,7 @@
use error;
use primitives::AuthorityId;
use runtime_primitives::bft::Justification;
use runtime_primitives::generic::BlockId;
use runtime_primitives::{generic::BlockId, Justification};
use runtime_primitives::traits::{Block as BlockT, NumberFor};
use state_machine::backend::Backend as StateBackend;
use state_machine::ChangesTrieStorage as StateChangesTrieStorage;
@@ -64,7 +63,7 @@ where
&mut self,
header: Block::Header,
body: Option<Vec<Block::Extrinsic>>,
justification: Option<Justification<Block::Hash>>,
justification: Option<Justification>,
state: NewBlockState,
) -> error::Result<()>;
+2 -3
View File
@@ -19,7 +19,7 @@
use primitives::AuthorityId;
use runtime_primitives::traits::{Block as BlockT, Header as HeaderT, NumberFor};
use runtime_primitives::generic::BlockId;
use runtime_primitives::bft::Justification;
use runtime_primitives::Justification;
use error::{ErrorKind, Result};
@@ -47,10 +47,9 @@ pub trait Backend<Block: BlockT>: HeaderBackend<Block> {
/// Get block body. Returns `None` if block is not found.
fn body(&self, id: BlockId<Block>) -> Result<Option<Vec<<Block as BlockT>::Extrinsic>>>;
/// Get block justification. Returns `None` if justification does not exist.
fn justification(&self, id: BlockId<Block>) -> Result<Option<Justification<Block::Hash>>>;
fn justification(&self, id: BlockId<Block>) -> Result<Option<Justification>>;
/// Get last finalized block hash.
fn last_finalized(&self) -> Result<Block::Hash>;
/// Returns data cache reference, if it is enabled on this backend.
fn cache(&self) -> Option<&Cache<Block>>;
+76 -105
View File
@@ -22,7 +22,7 @@ use futures::sync::mpsc;
use parking_lot::{Mutex, RwLock};
use primitives::AuthorityId;
use runtime_primitives::{
bft::Justification,
Justification,
generic::{BlockId, SignedBlock, Block as RuntimeBlock},
transaction_validity::{TransactionValidity, TransactionTag},
};
@@ -44,7 +44,7 @@ use blockchain::{self, Info as ChainInfo, Backend as ChainBackend, HeaderBackend
use call_executor::{CallExecutor, LocalCallExecutor};
use executor::{RuntimeVersion, RuntimeInfo};
use notifications::{StorageNotifications, StorageEventStream};
use {cht, error, in_mem, block_builder, bft, genesis};
use {cht, error, in_mem, block_builder, genesis};
/// Type that implements `futures::Stream` of block import events.
pub type ImportNotifications<Block> = mpsc::UnboundedReceiver<BlockImportNotification<Block>>;
@@ -151,6 +151,53 @@ pub enum BlockOrigin {
File,
}
/// Data required to import a Block
pub struct ImportBlock<Block: BlockT> {
/// Origin of the Block
pub origin: BlockOrigin,
/// Header
pub header: Block::Header,
/// Justification provided for this block from the outside
pub external_justification: Justification,
/// Internal Justification for the block
pub internal_justification: Vec<u8>, // Block::Digest::DigestItem?
/// 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>>)>,
}
impl<Block: BlockT> ImportBlock<Block> {
/// Deconstruct the justified header into parts.
pub fn into_inner(self)
-> (
BlockOrigin,
<Block as BlockT>::Header,
Justification,
Justification,
Option<Vec<<Block as BlockT>::Extrinsic>>,
bool,
Vec<(Vec<u8>, Option<Vec<u8>>)>,
) {
(
self.origin,
self.header,
self.external_justification,
self.internal_justification,
self.body,
self.finalized,
self.auxiliary,
)
}
}
/// Summary of an imported block
#[derive(Clone, Debug)]
pub struct BlockImportNotification<Block: BlockT> {
@@ -175,21 +222,6 @@ pub struct FinalityNotification<Block: BlockT> {
pub header: Block::Header,
}
/// A header paired with a justification which has already been checked.
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct JustifiedHeader<Block: BlockT> {
header: <Block as BlockT>::Header,
justification: ::bft::Justification<Block::Hash>,
authorities: Vec<AuthorityId>,
}
impl<Block: BlockT> JustifiedHeader<Block> {
/// Deconstruct the justified header into parts.
pub fn into_inner(self) -> (<Block as BlockT>::Header, ::bft::Justification<Block::Hash>, Vec<AuthorityId>) {
(self.header, self.justification, self.authorities)
}
}
/// Create an instance of in-memory client.
pub fn new_in_mem<E, Block, S>(
executor: E,
@@ -486,37 +518,24 @@ impl<B, E, Block> Client<B, E, Block> where
)
}
/// Check a header's justification.
pub fn check_justification(
&self,
header: <Block as BlockT>::Header,
justification: ::bft::UncheckedJustification<Block::Hash>,
) -> error::Result<JustifiedHeader<Block>> {
let parent_hash = header.parent_hash().clone();
let authorities = self.authorities_at(&BlockId::Hash(parent_hash))?;
let just = ::bft::check_justification::<Block>(&authorities[..], parent_hash, justification)
.map_err(|_|
error::ErrorKind::BadJustification(
format!("{}", header.hash())
)
)?;
Ok(JustifiedHeader {
header,
justification: just,
authorities,
})
}
/// Queue a block for import.
/// Import a checked and validated block
pub fn import_block(
&self,
origin: BlockOrigin,
header: JustifiedHeader<Block>,
body: Option<Vec<<Block as BlockT>::Extrinsic>>,
finalized: bool,
import_block: ImportBlock<Block>,
new_authorities: Option<Vec<AuthorityId>>,
) -> error::Result<ImportResult> {
let (header, justification, authorities) = header.into_inner();
let (
origin,
header,
_,
justification,
body,
finalized,
_aux, // TODO: write this to DB also
) = import_block.into_inner();
let parent_hash = header.parent_hash().clone();
match self.backend.blockchain().status(BlockId::Hash(parent_hash))? {
blockchain::BlockStatus::InChain => {},
blockchain::BlockStatus::Unknown => return Ok(ImportResult::UnknownParent),
@@ -532,8 +551,8 @@ impl<B, E, Block> Client<B, E, Block> where
header,
justification,
body,
authorities,
finalized
new_authorities,
finalized,
);
*self.importing_block.write() = None;
@@ -574,9 +593,9 @@ impl<B, E, Block> Client<B, E, Block> where
origin: BlockOrigin,
hash: Block::Hash,
header: Block::Header,
justification: bft::Justification<Block::Hash>,
justification: Justification,
body: Option<Vec<Block::Extrinsic>>,
authorities: Vec<AuthorityId>,
authorities: Option<Vec<AuthorityId>>,
finalized: bool,
) -> error::Result<ImportResult> {
let parent_hash = header.parent_hash().clone();
@@ -650,16 +669,17 @@ impl<B, E, Block> Client<B, E, Block> where
};
trace!("Imported {}, (#{}), best={}, origin={:?}", hash, header.number(), is_new_best, origin);
let unchecked: bft::UncheckedJustification<_> = justification.uncheck().into();
transaction.set_block_data(
header.clone(),
body,
Some(unchecked.into()),
Some(justification),
leaf_state,
)?;
transaction.update_authorities(authorities);
if let Some(authorities) = authorities {
transaction.update_authorities(authorities);
}
if let Some(storage_update) = storage_update {
transaction.update_storage(storage_update)?;
}
@@ -843,12 +863,14 @@ impl<B, E, Block> Client<B, E, Block> where
}
/// Get block justification set by id.
pub fn justification(&self, id: &BlockId<Block>) -> error::Result<Option<Justification<Block::Hash>>> {
pub fn justification(&self, id: &BlockId<Block>) -> error::Result<Option<Justification>> {
self.backend.blockchain().justification(*id)
}
/// Get full block by id.
pub fn block(&self, id: &BlockId<Block>) -> error::Result<Option<SignedBlock<Block::Header, Block::Extrinsic, Block::Hash>>> {
pub fn block(&self, id: &BlockId<Block>)
-> error::Result<Option<SignedBlock<Block::Header, Block::Extrinsic>>>
{
Ok(match (self.header(id)?, self.body(id)?, self.justification(id)?) {
(Some(header), Some(extrinsics), Some(justification)) =>
Some(SignedBlock { block: RuntimeBlock { header, extrinsics }, justification }),
@@ -985,57 +1007,6 @@ impl<B, E, Block> BlockNumberToHash for Client<B, E, Block> where
}
}
impl<B, E, Block> bft::BlockImport<Block> for Client<B, E, Block>
where
B: backend::Backend<Block, Blake2Hasher>,
E: CallExecutor<Block, Blake2Hasher>,
Block: BlockT,
{
fn import_block(
&self,
block: Block,
justification: ::bft::Justification<Block::Hash>,
authorities: &[AuthorityId],
) -> bool {
let (header, extrinsics) = block.deconstruct();
let justified_header = JustifiedHeader {
header: header,
justification,
authorities: authorities.to_vec(),
};
// TODO [rob]: non-instant finality.
self.import_block(
BlockOrigin::ConsensusBroadcast,
justified_header,
Some(extrinsics),
true
).is_ok()
}
}
impl<B, E, Block> bft::Authorities<Block> for Client<B, E, Block>
where
B: backend::Backend<Block, Blake2Hasher>,
E: CallExecutor<Block, Blake2Hasher>,
Block: BlockT,
{
fn authorities(&self, at: &BlockId<Block>) -> Result<Vec<AuthorityId>, bft::Error> {
let on_chain_version: Result<_, bft::Error> = self.runtime_version_at(at)
.map_err(|e| { trace!("Error getting runtime version {:?}", e); bft::ErrorKind::RuntimeVersionMissing.into() });
let on_chain_version = on_chain_version?;
let native_version: Result<_, bft::Error> = self.executor.native_runtime_version()
.ok_or_else(|| bft::ErrorKind::NativeRuntimeMissing.into());
let native_version = native_version?;
if !native_version.can_author_with(&on_chain_version) {
return Err(bft::ErrorKind::IncompatibleAuthoringRuntime(on_chain_version, native_version.runtime_version.clone()).into())
}
self.authorities_at(at).map_err(|_| {
let descriptor = format!("{:?}", at);
bft::ErrorKind::StateUnavailable(descriptor).into()
})
}
}
impl<B, E, Block> BlockchainEvents<Block> for Client<B, E, Block>
where
@@ -1095,7 +1066,7 @@ impl<B, E, Block> api::Core<Block, AuthorityId> for Client<B, E, Block> where
}
fn authorities(&self, at: &BlockId<Block>) -> Result<Vec<AuthorityId>, Self::Error> {
bft::Authorities::authorities(self, at).map_err(Into::into)
self.authorities_at(at)
}
fn execute_block(&self, at: &BlockId<Block>, block: &Block) -> Result<(), Self::Error> {
-5
View File
@@ -19,13 +19,8 @@
use std;
use state_machine;
use runtime_primitives::ApplyError;
use bft;
error_chain! {
links {
BFT(bft::error::Error, bft::error::ErrorKind) #[doc="BFT error"];
}
errors {
/// Backend error.
Backend(s: String) {
+9 -9
View File
@@ -26,7 +26,7 @@ use primitives::AuthorityId;
use runtime_primitives::generic::BlockId;
use runtime_primitives::traits::{Block as BlockT, Header as HeaderT, Zero,
NumberFor, As, Digest, DigestItem};
use runtime_primitives::bft::Justification;
use runtime_primitives::Justification;
use blockchain::{self, BlockStatus, HeaderBackend};
use state_machine::backend::{Backend as StateBackend, InMemory};
use state_machine::InMemoryChangesTrieStorage;
@@ -42,12 +42,12 @@ struct PendingBlock<B: BlockT> {
#[derive(PartialEq, Eq, Clone)]
enum StoredBlock<B: BlockT> {
Header(B::Header, Option<Justification<B::Hash>>),
Full(B, Option<Justification<B::Hash>>),
Header(B::Header, Option<Justification>),
Full(B, Option<Justification>),
}
impl<B: BlockT> StoredBlock<B> {
fn new(header: B::Header, body: Option<Vec<B::Extrinsic>>, just: Option<Justification<B::Hash>>) -> Self {
fn new(header: B::Header, body: Option<Vec<B::Extrinsic>>, just: Option<Justification>) -> Self {
match body {
Some(body) => StoredBlock::Full(B::new(header, body), just),
None => StoredBlock::Header(header, just),
@@ -61,7 +61,7 @@ impl<B: BlockT> StoredBlock<B> {
}
}
fn justification(&self) -> Option<&Justification<B::Hash>> {
fn justification(&self) -> Option<&Justification> {
match *self {
StoredBlock::Header(_, ref j) | StoredBlock::Full(_, ref j) => j.as_ref()
}
@@ -74,7 +74,7 @@ impl<B: BlockT> StoredBlock<B> {
}
}
fn into_inner(self) -> (B::Header, Option<Vec<B::Extrinsic>>, Option<Justification<B::Hash>>) {
fn into_inner(self) -> (B::Header, Option<Vec<B::Extrinsic>>, Option<Justification>) {
match self {
StoredBlock::Header(header, just) => (header, None, just),
StoredBlock::Full(block, just) => {
@@ -159,7 +159,7 @@ impl<Block: BlockT> Blockchain<Block> {
&self,
hash: Block::Hash,
header: <Block as BlockT>::Header,
justification: Option<Justification<Block::Hash>>,
justification: Option<Justification>,
body: Option<Vec<<Block as BlockT>::Extrinsic>>,
new_state: NewBlockState,
) -> ::error::Result<()> {
@@ -292,7 +292,7 @@ impl<Block: BlockT> blockchain::Backend<Block> for Blockchain<Block> {
}))
}
fn justification(&self, id: BlockId<Block>) -> error::Result<Option<Justification<Block::Hash>>> {
fn justification(&self, id: BlockId<Block>) -> error::Result<Option<Justification>> {
Ok(self.id(id).and_then(|hash| self.storage.read().blocks.get(&hash).and_then(|b|
b.justification().map(|x| x.clone()))
))
@@ -375,7 +375,7 @@ where
&mut self,
header: <Block as BlockT>::Header,
body: Option<Vec<<Block as BlockT>::Extrinsic>>,
justification: Option<Justification<Block::Hash>>,
justification: Option<Justification>,
state: NewBlockState,
) -> error::Result<()> {
assert!(self.pending_block.is_none(), "Only one block per operation is allowed");
+1 -2
View File
@@ -19,7 +19,6 @@
#![warn(missing_docs)]
#![recursion_limit="128"]
extern crate substrate_bft as bft;
extern crate substrate_trie as trie;
extern crate parity_codec as codec;
extern crate substrate_primitives as primitives;
@@ -63,7 +62,7 @@ pub use client::{
new_with_backend,
new_in_mem,
BlockBody, BlockStatus, BlockOrigin, ImportNotifications, FinalityNotifications, BlockchainEvents,
Client, ClientInfo, ChainHead, ImportResult, JustifiedHeader,
Client, ClientInfo, ChainHead, ImportResult, ImportBlock,
};
pub use notifications::{StorageEventStream, StorageChangeSet};
pub use state_machine::ExecutionStrategy;
+3 -3
View File
@@ -22,9 +22,9 @@ use futures::{Future, IntoFuture};
use parking_lot::RwLock;
use primitives::AuthorityId;
use runtime_primitives::{bft::Justification, generic::BlockId};
use runtime_primitives::traits::{Block as BlockT, NumberFor};
use runtime_primitives::{generic::BlockId, Justification};
use state_machine::{Backend as StateBackend, InMemoryChangesTrieStorage, TrieBackend};
use runtime_primitives::traits::{Block as BlockT, NumberFor};
use backend::{Backend as ClientBackend, BlockImportOperation, RemoteBackend, NewBlockState};
use blockchain::HeaderBackend as BlockchainHeaderBackend;
@@ -167,7 +167,7 @@ where
&mut self,
header: Block::Header,
_body: Option<Vec<Block::Extrinsic>>,
_justification: Option<Justification<Block::Hash>>,
_justification: Option<Justification>,
state: NewBlockState,
) -> ClientResult<()> {
self.leaf_state = state;
@@ -22,8 +22,8 @@ use futures::{Future, IntoFuture};
use parking_lot::Mutex;
use primitives::AuthorityId;
use runtime_primitives::{bft::Justification, generic::BlockId};
use runtime_primitives::traits::{Block as BlockT, Header as HeaderT, NumberFor, Zero};
use runtime_primitives::{Justification, generic::BlockId};
use runtime_primitives::traits::{Block as BlockT, Header as HeaderT,NumberFor, Zero};
use backend::NewBlockState;
use blockchain::{Backend as BlockchainBackend, BlockStatus, Cache as BlockchainCache,
@@ -139,7 +139,7 @@ impl<S, F, Block> BlockchainBackend<Block> for Blockchain<S, F> where Block: Blo
Ok(None)
}
fn justification(&self, _id: BlockId<Block>) -> ClientResult<Option<Justification<Block::Hash>>> {
fn justification(&self, _id: BlockId<Block>) -> ClientResult<Option<Justification>> {
Ok(None)
}