Make BlockImport and Verifier async (#8472)

* Make grandpa work

* Introduce `SharedData`

* Add test and fix bugs

* Switch to `SharedData`

* Make grandpa tests working

* More Babe work

* Make it async

* Fix fix

* Use `async_trait` in sc-consensus-slots

This makes the code a little bit easier to read and also expresses that
there can always only be one call at a time to `on_slot`.

* Make grandpa tests compile

* More Babe tests work

* Fix network test

* Start fixing service test

* Finish service-test

* Fix sc-consensus-aura

* Fix fix fix

* More fixes

* Make everything compile *yeah*

* Fix build when we have Rust 1.51

* Update client/consensus/common/src/shared_data.rs

Co-authored-by: André Silva <123550+andresilva@users.noreply.github.com>

* Update client/consensus/common/src/shared_data.rs

Co-authored-by: André Silva <123550+andresilva@users.noreply.github.com>

* Update client/consensus/common/src/shared_data.rs

Co-authored-by: André Silva <123550+andresilva@users.noreply.github.com>

* Update client/consensus/common/src/shared_data.rs

Co-authored-by: André Silva <123550+andresilva@users.noreply.github.com>

* Update client/consensus/common/src/shared_data.rs

Co-authored-by: André Silva <123550+andresilva@users.noreply.github.com>

* Update client/consensus/babe/src/tests.rs

Co-authored-by: André Silva <123550+andresilva@users.noreply.github.com>

* Update client/consensus/babe/src/tests.rs

Co-authored-by: André Silva <123550+andresilva@users.noreply.github.com>

* Fix warning

Co-authored-by: André Silva <123550+andresilva@users.noreply.github.com>
This commit is contained in:
Bastian Köcher
2021-03-30 11:19:49 +02:00
committed by GitHub
parent 2998b42311
commit 217c4be226
65 changed files with 1241 additions and 715 deletions
@@ -26,12 +26,13 @@ use substrate_test_runtime_client::{self, prelude::*};
use substrate_test_runtime_client::runtime::{Block, Hash};
use sp_runtime::generic::BlockId;
use sc_block_builder::BlockBuilderProvider;
use futures::executor::block_on;
use super::*;
fn prepare_good_block() -> (TestClient, Hash, u64, PeerId, IncomingBlock<Block>) {
let mut client = substrate_test_runtime_client::new();
let block = client.new_block(Default::default()).unwrap().build().unwrap().block;
client.import(BlockOrigin::File, block).unwrap();
block_on(client.import(BlockOrigin::File, block)).unwrap();
let (hash, number) = (client.block_hash(1).unwrap().unwrap(), 1);
let header = client.header(&BlockId::Number(1)).unwrap();
@@ -55,12 +56,12 @@ fn import_single_good_block_works() {
let mut expected_aux = ImportedAux::default();
expected_aux.is_new_best = true;
match import_single_block(
match block_on(import_single_block(
&mut substrate_test_runtime_client::new(),
BlockOrigin::File,
block,
&mut PassThroughVerifier::new(true)
) {
)) {
Ok(BlockImportResult::ImportedUnknown(ref num, ref aux, ref org))
if *num == number && *aux == expected_aux && *org == Some(peer_id) => {}
r @ _ => panic!("{:?}", r)
@@ -70,12 +71,12 @@ fn import_single_good_block_works() {
#[test]
fn import_single_good_known_block_is_ignored() {
let (mut client, _hash, number, _, block) = prepare_good_block();
match import_single_block(
match block_on(import_single_block(
&mut client,
BlockOrigin::File,
block,
&mut PassThroughVerifier::new(true)
) {
)) {
Ok(BlockImportResult::ImportedKnown(ref n, _)) if *n == number => {}
_ => panic!()
}
@@ -85,12 +86,12 @@ fn import_single_good_known_block_is_ignored() {
fn import_single_good_block_without_header_fails() {
let (_, _, _, peer_id, mut block) = prepare_good_block();
block.header = None;
match import_single_block(
match block_on(import_single_block(
&mut substrate_test_runtime_client::new(),
BlockOrigin::File,
block,
&mut PassThroughVerifier::new(true)
) {
)) {
Err(BlockImportError::IncompleteHeader(ref org)) if *org == Some(peer_id) => {}
_ => panic!()
}
+148 -111
View File
@@ -23,8 +23,7 @@ mod block_import;
mod sync;
use std::{
borrow::Cow, collections::HashMap, pin::Pin, sync::Arc, marker::PhantomData,
task::{Poll, Context as FutureContext}
borrow::Cow, collections::HashMap, pin::Pin, sync::Arc, task::{Poll, Context as FutureContext}
};
use libp2p::build_multiaddr;
@@ -64,7 +63,7 @@ use sc_network::config::ProtocolConfig;
use sp_runtime::generic::{BlockId, OpaqueDigestItemId};
use sp_runtime::traits::{Block as BlockT, Header as HeaderT, NumberFor};
use sp_runtime::{Justification, Justifications};
use substrate_test_runtime_client::{self, AccountKeyring};
use substrate_test_runtime_client::AccountKeyring;
use sc_service::client::Client;
pub use sc_network::config::EmptyTransactionPool;
pub use substrate_test_runtime_client::runtime::{Block, Extrinsic, Hash, Transfer};
@@ -104,8 +103,9 @@ impl PassThroughVerifier {
}
/// This `Verifier` accepts all data as valid.
#[async_trait::async_trait]
impl<B: BlockT> Verifier<B> for PassThroughVerifier {
fn verify(
async fn verify(
&mut self,
origin: BlockOrigin,
header: B::Header,
@@ -154,13 +154,8 @@ impl PeersClient {
}
}
pub fn as_block_import<Transaction>(&self) -> BlockImportAdapter<Transaction> {
match *self {
PeersClient::Full(ref client, ref _backend) =>
BlockImportAdapter::new_full(client.clone()),
PeersClient::Light(ref client, ref _backend) =>
BlockImportAdapter::Light(Arc::new(Mutex::new(client.clone())), PhantomData),
}
pub fn as_block_import(&self) -> BlockImportAdapter<Self> {
BlockImportAdapter::new(self.clone())
}
pub fn get_aux(&self, key: &[u8]) -> ClientResult<Option<Vec<u8>>> {
@@ -218,7 +213,36 @@ impl PeersClient {
}
}
pub struct Peer<D> {
#[async_trait::async_trait]
impl BlockImport<Block> for PeersClient {
type Error = ConsensusError;
type Transaction = ();
async fn check_block(
&mut self,
block: BlockCheckParams<Block>,
) -> Result<ImportResult, Self::Error> {
match self {
PeersClient::Full(client, _) => client.check_block(block).await,
PeersClient::Light(client, _) => client.check_block(block).await,
}
}
async fn import_block(
&mut self,
block: BlockImportParams<Block, ()>,
cache: HashMap<well_known_cache_keys::Id, Vec<u8>>,
) -> Result<ImportResult, Self::Error> {
match self {
PeersClient::Full(client, _) =>
client.import_block(block.convert_transaction(), cache).await,
PeersClient::Light(client, _) =>
client.import_block(block.convert_transaction(), cache).await,
}
}
}
pub struct Peer<D, BlockImport> {
pub data: D,
client: PeersClient,
/// We keep a copy of the verifier so that we can invoke it for locally-generated blocks,
@@ -226,7 +250,7 @@ pub struct Peer<D> {
verifier: VerifierAdapter<Block>,
/// We keep a copy of the block_import so that we can invoke it for locally-generated blocks,
/// instead of going through the import queue.
block_import: BlockImportAdapter<()>,
block_import: BlockImportAdapter<BlockImport>,
select_chain: Option<LongestChain<substrate_test_runtime_client::Backend, Block>>,
backend: Option<Arc<substrate_test_runtime_client::Backend>>,
network: NetworkWorker<Block, <Block as BlockT>::Hash>,
@@ -235,7 +259,10 @@ pub struct Peer<D> {
listen_addr: Multiaddr,
}
impl<D> Peer<D> {
impl<D, B> Peer<D, B> where
B: BlockImport<Block, Error = ConsensusError> + Send + Sync,
B::Transaction: Send,
{
/// Get this peer ID.
pub fn id(&self) -> PeerId {
self.network.service().local_peer_id().clone()
@@ -277,13 +304,24 @@ impl<D> Peer<D> {
}
/// Request explicit fork sync.
pub fn set_sync_fork_request(&self, peers: Vec<PeerId>, hash: <Block as BlockT>::Hash, number: NumberFor<Block>) {
pub fn set_sync_fork_request(
&self,
peers: Vec<PeerId>,
hash: <Block as BlockT>::Hash,
number: NumberFor<Block>,
) {
self.network.service().set_sync_fork_request(peers, hash, number);
}
/// Add blocks to the peer -- edit the block before adding
pub fn generate_blocks<F>(&mut self, count: usize, origin: BlockOrigin, edit_block: F) -> H256
where F: FnMut(BlockBuilder<Block, PeersFullClient, substrate_test_runtime_client::Backend>) -> Block
pub fn generate_blocks<F>(
&mut self,
count: usize,
origin: BlockOrigin,
edit_block: F,
) -> H256
where
F: FnMut(BlockBuilder<Block, PeersFullClient, substrate_test_runtime_client::Backend>) -> Block
{
let best_hash = self.client.info().best_hash;
self.generate_blocks_at(BlockId::Hash(best_hash), count, origin, edit_block, false, true, true)
@@ -320,19 +358,21 @@ impl<D> Peer<D> {
block.header.parent_hash,
);
let header = block.header.clone();
let (import_block, cache) = self.verifier.verify(
let (import_block, cache) = futures::executor::block_on(self.verifier.verify(
origin,
header.clone(),
None,
if headers_only { None } else { Some(block.extrinsics) },
).unwrap();
)).unwrap();
let cache = if let Some(cache) = cache {
cache.into_iter().collect()
} else {
Default::default()
};
self.block_import.import_block(import_block, cache).expect("block_import failed");
futures::executor::block_on(
self.block_import.import_block(import_block, cache)
).expect("block_import failed");
if announce_block {
self.network.service().announce_block(hash, None);
}
@@ -478,102 +518,80 @@ impl<D> Peer<D> {
}
}
pub trait BlockImportAdapterFull:
BlockImport<
Block,
Transaction = TransactionFor<substrate_test_runtime_client::Backend, Block>,
Error = ConsensusError,
> +
Send +
Sync +
Clone
{}
impl<T> BlockImportAdapterFull for T where
T: BlockImport<
Block,
Transaction = TransactionFor<substrate_test_runtime_client::Backend, Block>,
Error = ConsensusError,
> +
Send +
Sync +
Clone
{}
/// Implements `BlockImport` for any `Transaction`. Internally the transaction is
/// "converted", aka the field is set to `None`.
///
/// This is required as the `TestNetFactory` trait does not distinguish between
/// full and light nodes.
pub enum BlockImportAdapter<Transaction> {
Full(
Arc<Mutex<dyn BlockImport<
Block,
Transaction = TransactionFor<substrate_test_runtime_client::Backend, Block>,
Error = ConsensusError
> + Send>>,
PhantomData<Transaction>,
),
Light(
Arc<Mutex<dyn BlockImport<
Block,
Transaction = TransactionFor<substrate_test_runtime_client::LightBackend, Block>,
Error = ConsensusError
> + Send>>,
PhantomData<Transaction>,
),
#[derive(Clone)]
pub struct BlockImportAdapter<I> {
inner: I,
}
impl<Transaction> BlockImportAdapter<Transaction> {
impl<I> BlockImportAdapter<I> {
/// Create a new instance of `Self::Full`.
pub fn new_full(
full: impl BlockImport<
Block,
Transaction = TransactionFor<substrate_test_runtime_client::Backend, Block>,
Error = ConsensusError
>
+ 'static
+ Send
) -> Self {
Self::Full(Arc::new(Mutex::new(full)), PhantomData)
}
/// Create a new instance of `Self::Light`.
pub fn new_light(
light: impl BlockImport<
Block,
Transaction = TransactionFor<substrate_test_runtime_client::LightBackend, Block>,
Error = ConsensusError
>
+ 'static
+ Send
) -> Self {
Self::Light(Arc::new(Mutex::new(light)), PhantomData)
}
}
impl<Transaction> Clone for BlockImportAdapter<Transaction> {
fn clone(&self) -> Self {
match self {
Self::Full(full, _) => Self::Full(full.clone(), PhantomData),
Self::Light(light, _) => Self::Light(light.clone(), PhantomData),
pub fn new(inner: I) -> Self {
Self {
inner,
}
}
}
impl<Transaction> BlockImport<Block> for BlockImportAdapter<Transaction> {
#[async_trait::async_trait]
impl<I> BlockImport<Block> for BlockImportAdapter<I> where
I: BlockImport<Block, Error = ConsensusError> + Send + Sync,
I::Transaction: Send,
{
type Error = ConsensusError;
type Transaction = Transaction;
type Transaction = ();
fn check_block(
async fn check_block(
&mut self,
block: BlockCheckParams<Block>,
) -> Result<ImportResult, Self::Error> {
match self {
Self::Full(full, _) => full.lock().check_block(block),
Self::Light(light, _) => light.lock().check_block(block),
}
self.inner.check_block(block).await
}
fn import_block(
async fn import_block(
&mut self,
block: BlockImportParams<Block, Transaction>,
block: BlockImportParams<Block, ()>,
cache: HashMap<well_known_cache_keys::Id, Vec<u8>>,
) -> Result<ImportResult, Self::Error> {
match self {
Self::Full(full, _) => full.lock().import_block(block.convert_transaction(), cache),
Self::Light(light, _) => light.lock().import_block(block.convert_transaction(), cache),
}
self.inner.import_block(block.convert_transaction(), cache).await
}
}
/// Implements `Verifier` on an `Arc<Mutex<impl Verifier>>`. Used internally.
#[derive(Clone)]
/// Implements `Verifier` and keeps track of failed verifications.
struct VerifierAdapter<B: BlockT> {
verifier: Arc<Mutex<Box<dyn Verifier<B>>>>,
verifier: Arc<futures::lock::Mutex<Box<dyn Verifier<B>>>>,
failed_verifications: Arc<Mutex<HashMap<B::Hash, String>>>,
}
#[async_trait::async_trait]
impl<B: BlockT> Verifier<B> for VerifierAdapter<B> {
fn verify(
async fn verify(
&mut self,
origin: BlockOrigin,
header: B::Header,
@@ -581,17 +599,26 @@ impl<B: BlockT> Verifier<B> for VerifierAdapter<B> {
body: Option<Vec<B::Extrinsic>>
) -> Result<(BlockImportParams<B, ()>, Option<Vec<(CacheKeyId, Vec<u8>)>>), String> {
let hash = header.hash();
self.verifier.lock().verify(origin, header, justifications, body).map_err(|e| {
self.verifier.lock().await.verify(origin, header, justifications, body).await.map_err(|e| {
self.failed_verifications.lock().insert(hash, e.clone());
e
})
}
}
impl<B: BlockT> Clone for VerifierAdapter<B> {
fn clone(&self) -> Self {
Self {
verifier: self.verifier.clone(),
failed_verifications: self.failed_verifications.clone(),
}
}
}
impl<B: BlockT> VerifierAdapter<B> {
fn new(verifier: Arc<Mutex<Box<dyn Verifier<B>>>>) -> VerifierAdapter<B> {
fn new(verifier: impl Verifier<B> + 'static) -> Self {
VerifierAdapter {
verifier,
verifier: Arc::new(futures::lock::Mutex::new(Box::new(verifier))),
failed_verifications: Default::default(),
}
}
@@ -614,8 +641,9 @@ pub struct FullPeerConfig {
pub is_authority: bool,
}
pub trait TestNetFactory: Sized {
pub trait TestNetFactory: Sized where <Self::BlockImport as BlockImport<Block>>::Transaction: Send {
type Verifier: 'static + Verifier<Block>;
type BlockImport: BlockImport<Block, Error = ConsensusError> + Clone + Send + Sync + 'static;
type PeerData: Default;
/// These two need to be implemented!
@@ -628,23 +656,20 @@ pub trait TestNetFactory: Sized {
) -> Self::Verifier;
/// Get reference to peer.
fn peer(&mut self, i: usize) -> &mut Peer<Self::PeerData>;
fn peers(&self) -> &Vec<Peer<Self::PeerData>>;
fn mut_peers<F: FnOnce(&mut Vec<Peer<Self::PeerData>>)>(
fn peer(&mut self, i: usize) -> &mut Peer<Self::PeerData, Self::BlockImport>;
fn peers(&self) -> &Vec<Peer<Self::PeerData, Self::BlockImport>>;
fn mut_peers<F: FnOnce(&mut Vec<Peer<Self::PeerData, Self::BlockImport>>)>(
&mut self,
closure: F,
);
/// Get custom block import handle for fresh client, along with peer data.
fn make_block_import<Transaction>(&self, client: PeersClient)
fn make_block_import(&self, client: PeersClient)
-> (
BlockImportAdapter<Transaction>,
BlockImportAdapter<Self::BlockImport>,
Option<BoxJustificationImport<Block>>,
Self::PeerData,
)
{
(client.as_block_import(), None, Default::default())
}
);
fn default_config() -> ProtocolConfig {
ProtocolConfig::default()
@@ -688,7 +713,7 @@ pub trait TestNetFactory: Sized {
&Default::default(),
&data,
);
let verifier = VerifierAdapter::new(Arc::new(Mutex::new(Box::new(verifier) as Box<_>)));
let verifier = VerifierAdapter::new(verifier);
let import_queue = Box::new(BasicQueue::new(
verifier.clone(),
@@ -776,7 +801,7 @@ pub trait TestNetFactory: Sized {
peers.push(Peer {
data,
client: PeersClient::Full(client, backend.clone()),
client: PeersClient::Full(client.clone(), backend.clone()),
select_chain: Some(longest_chain),
backend: Some(backend),
imported_blocks_stream,
@@ -804,7 +829,7 @@ pub trait TestNetFactory: Sized {
&Default::default(),
&data,
);
let verifier = VerifierAdapter::new(Arc::new(Mutex::new(Box::new(verifier) as Box<_>)));
let verifier = VerifierAdapter::new(verifier);
let import_queue = Box::new(BasicQueue::new(
verifier.clone(),
@@ -986,7 +1011,7 @@ pub trait TestNetFactory: Sized {
}
pub struct TestNet {
peers: Vec<Peer<()>>,
peers: Vec<Peer<(), PeersClient>>,
fork_choice: ForkChoiceStrategy,
}
@@ -1003,6 +1028,7 @@ impl TestNet {
impl TestNetFactory for TestNet {
type Verifier = PassThroughVerifier;
type PeerData = ();
type BlockImport = PeersClient;
/// Create new test network with peers and given config.
fn from_config(_config: &ProtocolConfig) -> Self {
@@ -1018,15 +1044,25 @@ impl TestNetFactory for TestNet {
PassThroughVerifier::new_with_fork_choice(false, self.fork_choice.clone())
}
fn peer(&mut self, i: usize) -> &mut Peer<()> {
fn make_block_import(&self, client: PeersClient)
-> (
BlockImportAdapter<Self::BlockImport>,
Option<BoxJustificationImport<Block>>,
Self::PeerData,
)
{
(client.as_block_import(), None, ())
}
fn peer(&mut self, i: usize) -> &mut Peer<(), Self::BlockImport> {
&mut self.peers[i]
}
fn peers(&self) -> &Vec<Peer<()>> {
fn peers(&self) -> &Vec<Peer<(), Self::BlockImport>> {
&self.peers
}
fn mut_peers<F: FnOnce(&mut Vec<Peer<()>>)>(&mut self, closure: F) {
fn mut_peers<F: FnOnce(&mut Vec<Peer<(), Self::BlockImport>>)>(&mut self, closure: F) {
closure(&mut self.peers);
}
}
@@ -1052,6 +1088,7 @@ pub struct JustificationTestNet(TestNet);
impl TestNetFactory for JustificationTestNet {
type Verifier = PassThroughVerifier;
type PeerData = ();
type BlockImport = PeersClient;
fn from_config(config: &ProtocolConfig) -> Self {
JustificationTestNet(TestNet::from_config(config))
@@ -1061,23 +1098,23 @@ impl TestNetFactory for JustificationTestNet {
self.0.make_verifier(client, config, peer_data)
}
fn peer(&mut self, i: usize) -> &mut Peer<Self::PeerData> {
fn peer(&mut self, i: usize) -> &mut Peer<Self::PeerData, Self::BlockImport> {
self.0.peer(i)
}
fn peers(&self) -> &Vec<Peer<Self::PeerData>> {
fn peers(&self) -> &Vec<Peer<Self::PeerData, Self::BlockImport>> {
self.0.peers()
}
fn mut_peers<F: FnOnce(
&mut Vec<Peer<Self::PeerData>>,
&mut Vec<Peer<Self::PeerData, Self::BlockImport>>,
)>(&mut self, closure: F) {
self.0.mut_peers(closure)
}
fn make_block_import<Transaction>(&self, client: PeersClient)
fn make_block_import(&self, client: PeersClient)
-> (
BlockImportAdapter<Transaction>,
BlockImportAdapter<Self::BlockImport>,
Option<BoxJustificationImport<Block>>,
Self::PeerData,
)