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
+2 -2
View File
@@ -336,7 +336,7 @@ check-web-wasm:
# Note: we don't need to test crates imported in `bin/node/cli`
- time cargo build --manifest-path=client/consensus/aura/Cargo.toml --target=wasm32-unknown-unknown --features getrandom
# Note: the command below is a bit weird because several Cargo issues prevent us from compiling the node in a more straight-forward way.
- time cargo +nightly build --manifest-path=bin/node/cli/Cargo.toml --no-default-features --features browser --target=wasm32-unknown-unknown -Z features=itarget
- time cargo +nightly build --manifest-path=bin/node/cli/Cargo.toml --no-default-features --features browser --target=wasm32-unknown-unknown
# with-tracing must be explicitly activated, we run a test to ensure this works as expected in both cases
- time cargo +nightly test --manifest-path primitives/tracing/Cargo.toml --no-default-features
- time cargo +nightly test --manifest-path primitives/tracing/Cargo.toml --no-default-features --features=with-tracing
@@ -407,7 +407,7 @@ test-browser-node:
CARGO_TARGET_WASM32_UNKNOWN_UNKNOWN_RUNNER: "wasm-bindgen-test-runner"
WASM_BINDGEN_TEST_TIMEOUT: 120
script:
- cargo +nightly test --target wasm32-unknown-unknown -p node-browser-testing -Z features=itarget
- cargo +nightly test --target wasm32-unknown-unknown -p node-browser-testing
build-linux-substrate: &build-binary
stage: build
+13 -1
View File
@@ -4171,6 +4171,7 @@ dependencies = [
"frame-benchmarking",
"frame-support",
"frame-system",
"futures 0.3.13",
"node-primitives",
"node-runtime",
"node-testing",
@@ -7106,6 +7107,7 @@ dependencies = [
name = "sc-consensus"
version = "0.9.0"
dependencies = [
"parking_lot 0.11.1",
"sc-client-api",
"sp-blockchain",
"sp-consensus",
@@ -7116,6 +7118,7 @@ dependencies = [
name = "sc-consensus-aura"
version = "0.9.0"
dependencies = [
"async-trait",
"derive_more",
"futures 0.3.13",
"futures-timer 3.0.2",
@@ -7157,6 +7160,7 @@ dependencies = [
name = "sc-consensus-babe"
version = "0.9.0"
dependencies = [
"async-trait",
"derive_more",
"fork-tree",
"futures 0.3.13",
@@ -7243,8 +7247,8 @@ version = "0.9.0"
dependencies = [
"fork-tree",
"parity-scale-codec 2.0.1",
"parking_lot 0.11.1",
"sc-client-api",
"sc-consensus",
"sp-blockchain",
"sp-runtime",
]
@@ -7254,6 +7258,7 @@ name = "sc-consensus-manual-seal"
version = "0.9.0"
dependencies = [
"assert_matches",
"async-trait",
"derive_more",
"futures 0.3.13",
"jsonrpc-core",
@@ -7291,6 +7296,7 @@ dependencies = [
name = "sc-consensus-pow"
version = "0.9.0"
dependencies = [
"async-trait",
"derive_more",
"futures 0.3.13",
"futures-timer 3.0.2",
@@ -7442,6 +7448,7 @@ name = "sc-finality-grandpa"
version = "0.9.0"
dependencies = [
"assert_matches",
"async-trait",
"derive_more",
"dyn-clone",
"finality-grandpa",
@@ -7680,6 +7687,7 @@ name = "sc-network-test"
version = "0.8.0"
dependencies = [
"async-std",
"async-trait",
"futures 0.3.13",
"futures-timer 3.0.2",
"libp2p",
@@ -7860,6 +7868,7 @@ name = "sc-service"
version = "0.9.0"
dependencies = [
"async-std",
"async-trait",
"directories",
"exit-future",
"futures 0.1.31",
@@ -8665,6 +8674,7 @@ dependencies = [
name = "sp-consensus"
version = "0.9.0"
dependencies = [
"async-trait",
"futures 0.3.13",
"futures-timer 3.0.2",
"libp2p",
@@ -9488,6 +9498,7 @@ dependencies = [
name = "substrate-test-client"
version = "2.0.1"
dependencies = [
"async-trait",
"futures 0.1.31",
"futures 0.3.13",
"hash-db",
@@ -9519,6 +9530,7 @@ dependencies = [
"frame-support",
"frame-system",
"frame-system-rpc-runtime-api",
"futures 0.3.13",
"log",
"memory-db",
"pallet-babe",
+2
View File
@@ -1,4 +1,6 @@
[workspace]
resolver = "2"
members = [
"bin/node-template/node",
"bin/node-template/pallets/template",
@@ -5,7 +5,7 @@ use super::*;
use frame_system::RawOrigin;
use frame_benchmarking::{benchmarks, whitelisted_caller, impl_benchmark_test_suite};
#[allow(unused)]
use crate::Module as Template;
use crate::Pallet as Template;
benchmarks! {
do_something {
+5 -5
View File
@@ -534,7 +534,7 @@ pub fn new_light(
#[cfg(test)]
mod tests {
use std::{sync::Arc, borrow::Cow, any::Any, convert::TryInto};
use std::{sync::Arc, borrow::Cow, convert::TryInto};
use sc_consensus_babe::{CompatibleDigestItem, BabeIntermediate, INTERMEDIATE_KEY};
use sc_consensus_epochs::descendent_query;
use sp_consensus::{
@@ -648,14 +648,14 @@ mod tests {
&(slot * SLOT_DURATION),
);
let epoch_descriptor = babe_link.epoch_changes().lock().epoch_descriptor_for_child_of(
let epoch_descriptor = babe_link.epoch_changes().shared_data().epoch_descriptor_for_child_of(
descendent_query(&*service.client()),
&parent_hash,
parent_number,
slot.into(),
).unwrap().unwrap();
let epoch = babe_link.epoch_changes().lock().epoch_data(
let epoch = babe_link.epoch_changes().shared_data().epoch_data(
&epoch_descriptor,
|slot| sc_consensus_babe::Epoch::genesis(&babe_link.config(), slot),
).unwrap();
@@ -703,11 +703,11 @@ mod tests {
params.body = Some(new_body);
params.intermediates.insert(
Cow::from(INTERMEDIATE_KEY),
Box::new(BabeIntermediate::<Block> { epoch_descriptor }) as Box<dyn Any>,
Box::new(BabeIntermediate::<Block> { epoch_descriptor }) as Box<_>,
);
params.fork_choice = Some(ForkChoiceStrategy::LongestChain);
block_import.import_block(params, Default::default())
futures::executor::block_on(block_import.import_block(params, Default::default()))
.expect("error importing test block");
},
|service, _| {
+1
View File
@@ -44,6 +44,7 @@ sp-runtime = { version = "3.0.0", path = "../../../primitives/runtime" }
sp-externalities = { version = "0.9.0", path = "../../../primitives/externalities" }
substrate-test-client = { version = "2.0.0", path = "../../../test-utils/client" }
wat = "1.0"
futures = "0.3.9"
[features]
wasmtime = [
+1 -1
View File
@@ -841,5 +841,5 @@ fn should_import_block_with_test_client() {
let block_data = block1.0;
let block = node_primitives::Block::decode(&mut &block_data[..]).unwrap();
client.import(BlockOrigin::Own, block).unwrap();
futures::executor::block_on(client.import(BlockOrigin::Own, block)).unwrap();
}
+1 -1
View File
@@ -691,7 +691,7 @@ impl BenchContext {
assert_eq!(self.client.chain_info().best_number, 0);
assert_eq!(
self.client.import_block(import_params, Default::default())
futures::executor::block_on(self.client.import_block(import_params, Default::default()))
.expect("Failed to import block"),
ImportResult::Imported(
ImportedAux {
@@ -420,6 +420,7 @@ mod tests {
use sp_blockchain::HeaderBackend;
use sp_runtime::traits::NumberFor;
use sc_client_api::Backend;
use futures::executor::block_on;
const SOURCE: TransactionSource = TransactionSource::External;
@@ -454,11 +455,11 @@ mod tests {
client.clone(),
);
futures::executor::block_on(
block_on(
txpool.submit_at(&BlockId::number(0), SOURCE, vec![extrinsic(0), extrinsic(1)])
).unwrap();
futures::executor::block_on(
block_on(
txpool.maintain(chain_event(
client.header(&BlockId::Number(0u64))
.expect("header get error")
@@ -492,7 +493,7 @@ mod tests {
// when
let deadline = time::Duration::from_secs(3);
let block = futures::executor::block_on(
let block = block_on(
proposer.propose(Default::default(), Default::default(), deadline)
).map(|r| r.block).unwrap();
@@ -538,7 +539,7 @@ mod tests {
);
let deadline = time::Duration::from_secs(1);
futures::executor::block_on(
block_on(
proposer.propose(Default::default(), Default::default(), deadline)
).map(|r| r.block).unwrap();
}
@@ -559,11 +560,11 @@ mod tests {
let genesis_hash = client.info().best_hash;
let block_id = BlockId::Hash(genesis_hash);
futures::executor::block_on(
block_on(
txpool.submit_at(&BlockId::number(0), SOURCE, vec![extrinsic(0)]),
).unwrap();
futures::executor::block_on(
block_on(
txpool.maintain(chain_event(
client.header(&BlockId::Number(0u64))
.expect("header get error")
@@ -585,7 +586,7 @@ mod tests {
);
let deadline = time::Duration::from_secs(9);
let proposal = futures::executor::block_on(
let proposal = block_on(
proposer.propose(Default::default(), Default::default(), deadline),
).unwrap();
@@ -625,7 +626,7 @@ mod tests {
client.clone(),
);
futures::executor::block_on(
block_on(
txpool.submit_at(&BlockId::number(0), SOURCE, vec![
extrinsic(0),
extrinsic(1),
@@ -667,7 +668,7 @@ mod tests {
// when
let deadline = time::Duration::from_secs(9);
let block = futures::executor::block_on(
let block = block_on(
proposer.propose(Default::default(), Default::default(), deadline)
).map(|r| r.block).unwrap();
@@ -679,7 +680,7 @@ mod tests {
block
};
futures::executor::block_on(
block_on(
txpool.maintain(chain_event(
client.header(&BlockId::Number(0u64))
.expect("header get error")
@@ -689,9 +690,9 @@ mod tests {
// let's create one block and import it
let block = propose_block(&client, 0, 2, 7);
client.import(BlockOrigin::Own, block).unwrap();
block_on(client.import(BlockOrigin::Own, block)).unwrap();
futures::executor::block_on(
block_on(
txpool.maintain(chain_event(
client.header(&BlockId::Number(1))
.expect("header get error")
@@ -701,6 +702,6 @@ mod tests {
// now let's make sure that we can still make some progress
let block = propose_block(&client, 1, 2, 5);
client.import(BlockOrigin::Own, block).unwrap();
block_on(client.import(BlockOrigin::Own, block)).unwrap();
}
}
+2 -1
View File
@@ -26,7 +26,6 @@ futures = "0.3.9"
futures-timer = "3.0.1"
sp-inherents = { version = "3.0.0", path = "../../../primitives/inherents" }
log = "0.4.8"
parking_lot = "0.11.1"
sp-core = { version = "3.0.0", path = "../../../primitives/core" }
sp-blockchain = { version = "3.0.0", path = "../../../primitives/blockchain" }
sp-io = { version = "3.0.0", path = "../../../primitives/io" }
@@ -38,6 +37,7 @@ sp-timestamp = { version = "3.0.0", path = "../../../primitives/timestamp" }
sp-keystore = { version = "0.9.0", path = "../../../primitives/keystore" }
sc-telemetry = { version = "3.0.0", path = "../../telemetry" }
prometheus-endpoint = { package = "substrate-prometheus-endpoint", path = "../../../utils/prometheus", version = "0.9.0"}
async-trait = "0.1.42"
# We enable it only for web-wasm check
# See https://docs.rs/getrandom/0.2.1/getrandom/#webassembly-support
getrandom = { version = "0.2", features = ["js"], optional = true }
@@ -52,3 +52,4 @@ sc-network-test = { version = "0.8.0", path = "../../network/test" }
sc-service = { version = "0.9.0", default-features = false, path = "../../service" }
substrate-test-runtime-client = { version = "2.0.0", path = "../../../test-utils/runtime/client" }
tempfile = "3.1.0"
parking_lot = "0.11.1"
@@ -220,6 +220,7 @@ impl<C, P, CAW> AuraVerifier<C, P, CAW> where
}
}
#[async_trait::async_trait]
impl<B: BlockT, C, P, CAW> Verifier<B> for AuraVerifier<C, P, CAW> where
C: ProvideRuntimeApi<B> +
Send +
@@ -234,7 +235,7 @@ impl<B: BlockT, C, P, CAW> Verifier<B> for AuraVerifier<C, P, CAW> where
P::Signature: Encode + Decode,
CAW: CanAuthorWith<B> + Send + Sync + 'static,
{
fn verify(
async fn verify(
&mut self,
origin: BlockOrigin,
header: B::Header,
@@ -405,6 +406,7 @@ impl<Block: BlockT, C, I: BlockImport<Block>, P> AuraBlockImport<Block, C, I, P>
}
}
#[async_trait::async_trait]
impl<Block: BlockT, C, I, P> BlockImport<Block> for AuraBlockImport<Block, C, I, P> where
I: BlockImport<Block, Transaction = sp_api::TransactionFor<C, Block>> + Send + Sync,
I::Error: Into<ConsensusError>,
@@ -412,18 +414,19 @@ impl<Block: BlockT, C, I, P> BlockImport<Block> for AuraBlockImport<Block, C, I,
P: Pair + Send + Sync + 'static,
P::Public: Clone + Eq + Send + Sync + Hash + Debug + Encode + Decode,
P::Signature: Encode + Decode,
sp_api::TransactionFor<C, Block>: Send + 'static,
{
type Error = ConsensusError;
type Transaction = sp_api::TransactionFor<C, Block>;
fn check_block(
async fn check_block(
&mut self,
block: BlockCheckParams<Block>,
) -> Result<ImportResult, Self::Error> {
self.inner.check_block(block).map_err(Into::into)
self.inner.check_block(block).await.map_err(Into::into)
}
fn import_block(
async fn import_block(
&mut self,
block: BlockImportParams<Block, Self::Transaction>,
new_cache: HashMap<CacheKeyId, Vec<u8>>,
@@ -453,7 +456,7 @@ impl<Block: BlockT, C, I, P> BlockImport<Block> for AuraBlockImport<Block, C, I,
);
}
self.inner.import_block(block, new_cache).map_err(Into::into)
self.inner.import_block(block, new_cache).await.map_err(Into::into)
}
}
+18 -5
View File
@@ -580,6 +580,7 @@ mod tests {
use super::*;
use sp_consensus::{
NoNetwork as DummyOracle, Proposal, AlwaysCanAuthor, DisableProofRecording,
import_queue::BoxJustificationImport,
};
use sc_network_test::{Block as TestBlock, *};
use sp_runtime::traits::{Block as BlockT, DigestFor};
@@ -641,13 +642,17 @@ mod tests {
const SLOT_DURATION: u64 = 1000;
type AuraVerifier = import_queue::AuraVerifier<PeersFullClient, AuthorityPair, AlwaysCanAuthor>;
type AuraPeer = Peer<(), PeersClient>;
pub struct AuraTestNet {
peers: Vec<Peer<()>>,
peers: Vec<AuraPeer>,
}
impl TestNetFactory for AuraTestNet {
type Verifier = import_queue::AuraVerifier<PeersFullClient, AuthorityPair, AlwaysCanAuthor>;
type Verifier = AuraVerifier;
type PeerData = ();
type BlockImport = PeersClient;
/// Create new test network with peers and given config.
fn from_config(_config: &ProtocolConfig) -> Self {
@@ -681,14 +686,22 @@ mod tests {
}
}
fn peer(&mut self, i: usize) -> &mut Peer<Self::PeerData> {
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 AuraPeer {
&mut self.peers[i]
}
fn peers(&self) -> &Vec<Peer<Self::PeerData>> {
fn peers(&self) -> &Vec<AuraPeer> {
&self.peers
}
fn mut_peers<F: FnOnce(&mut Vec<Peer<Self::PeerData>>)>(&mut self, closure: F) {
fn mut_peers<F: FnOnce(&mut Vec<AuraPeer>)>(&mut self, closure: F) {
closure(&mut self.peers);
}
}
@@ -53,6 +53,7 @@ merlin = "2.0"
pdqselect = "0.1.0"
derive_more = "0.99.2"
retain_mut = "0.1.2"
async-trait = "0.1.42"
[dev-dependencies]
sp-keyring = { version = "3.0.0", path = "../../../primitives/keyring" }
@@ -217,7 +217,7 @@ fn epoch_data<B, C, SC>(
SC: SelectChain<B>,
{
let parent = select_chain.best_chain()?;
epoch_changes.lock().epoch_data_for_child_of(
epoch_changes.shared_data().epoch_data_for_child_of(
descendent_query(&**client),
&parent.hash(),
parent.number().clone(),
@@ -18,8 +18,6 @@
//! Schema for BABE epoch changes in the aux-db.
use std::sync::Arc;
use parking_lot::Mutex;
use log::info;
use codec::{Decode, Encode};
@@ -79,18 +77,19 @@ pub fn load_epoch_changes<Block: BlockT, B: AuxStore>(
},
};
let epoch_changes = Arc::new(Mutex::new(maybe_epoch_changes.unwrap_or_else(|| {
info!(target: "babe",
"👶 Creating empty BABE epoch changes on what appears to be first startup."
let epoch_changes = SharedEpochChanges::<Block, Epoch>::new(maybe_epoch_changes.unwrap_or_else(|| {
info!(
target: "babe",
"👶 Creating empty BABE epoch changes on what appears to be first startup.",
);
EpochChangesFor::<Block, Epoch>::default()
})));
}));
// rebalance the tree after deserialization. this isn't strictly necessary
// since the tree is now rebalanced on every update operation. but since the
// tree wasn't rebalanced initially it's useful to temporarily leave it here
// to avoid having to wait until an import for rebalancing.
epoch_changes.lock().rebalance();
epoch_changes.shared_data().rebalance();
Ok(epoch_changes)
}
@@ -189,7 +188,7 @@ mod test {
).unwrap();
assert!(
epoch_changes.lock()
epoch_changes.shared_data()
.tree()
.iter()
.map(|(_, _, epoch)| epoch.clone())
@@ -201,7 +200,7 @@ mod test {
); // PersistedEpochHeader does not implement Debug, so we use assert! directly.
write_epoch_changes::<TestBlock, _, _>(
&epoch_changes.lock(),
&epoch_changes.shared_data(),
|values| {
client.insert_aux(values, &[]).unwrap();
},
+184 -173
View File
@@ -76,8 +76,8 @@ pub use sp_consensus_babe::{
pub use sp_consensus::SyncOracle;
pub use sc_consensus_slots::SlotProportion;
use std::{
collections::HashMap, sync::Arc, u64, pin::Pin, time::{Instant, Duration},
any::Any, borrow::Cow, convert::TryInto,
collections::HashMap, sync::Arc, u64, pin::Pin, borrow::Cow, convert::TryInto,
time::{Duration, Instant},
};
use sp_consensus::{ImportResult, CanAuthorWith, import_queue::BoxJustificationImport};
use sp_core::crypto::Public;
@@ -502,7 +502,7 @@ async fn answer_requests<B: BlockT, C>(
match request {
BabeRequest::EpochForChild(parent_hash, parent_number, slot_number, response) => {
let lookup = || {
let epoch_changes = epoch_changes.lock();
let epoch_changes = epoch_changes.shared_data();
let epoch_descriptor = epoch_changes.epoch_descriptor_for_child_of(
descendent_query(&*client),
&parent_hash,
@@ -656,7 +656,7 @@ where
parent: &B::Header,
slot: Slot,
) -> Result<Self::EpochData, ConsensusError> {
self.epoch_changes.lock().epoch_descriptor_for_child_of(
self.epoch_changes.shared_data().epoch_descriptor_for_child_of(
descendent_query(&*self.client),
&parent.hash(),
parent.number().clone(),
@@ -667,7 +667,8 @@ where
}
fn authorities_len(&self, epoch_descriptor: &Self::EpochData) -> Option<usize> {
self.epoch_changes.lock()
self.epoch_changes
.shared_data()
.viable_epoch(&epoch_descriptor, |slot| Epoch::genesis(&self.config, slot))
.map(|epoch| epoch.as_ref().authorities.len())
}
@@ -681,7 +682,7 @@ where
debug!(target: "babe", "Attempting to claim slot {}", slot);
let s = authorship::claim_slot(
slot,
self.epoch_changes.lock().viable_epoch(
self.epoch_changes.shared_data().viable_epoch(
&epoch_descriptor,
|slot| Epoch::genesis(&self.config, slot)
)?.as_ref(),
@@ -768,7 +769,7 @@ where
import_block.storage_changes = Some(storage_changes);
import_block.intermediates.insert(
Cow::from(INTERMEDIATE_KEY),
Box::new(BabeIntermediate::<B> { epoch_descriptor }) as Box<dyn Any>,
Box::new(BabeIntermediate::<B> { epoch_descriptor }) as Box<_>,
);
Ok(import_block)
@@ -1083,6 +1084,7 @@ where
}
}
#[async_trait::async_trait]
impl<Block, Client, SelectChain, CAW> Verifier<Block>
for BabeVerifier<Block, Client, SelectChain, CAW>
where
@@ -1093,7 +1095,7 @@ where
SelectChain: sp_consensus::SelectChain<Block>,
CAW: CanAuthorWith<Block> + Send + Sync,
{
fn verify(
async fn verify(
&mut self,
origin: BlockOrigin,
header: Block::Header,
@@ -1125,7 +1127,7 @@ where
.map_err(Error::<Block>::FetchParentHeader)?;
let pre_digest = find_pre_digest::<Block>(&header)?;
let epoch_changes = self.epoch_changes.lock();
let epoch_changes = self.epoch_changes.shared_data();
let epoch_descriptor = epoch_changes.epoch_descriptor_for_child_of(
descendent_query(&*self.client),
&parent_hash,
@@ -1189,7 +1191,8 @@ where
self.telemetry;
CONSENSUS_TRACE;
"babe.checked_and_importing";
"pre_header" => ?pre_header);
"pre_header" => ?pre_header,
);
let mut import_block = BlockImportParams::new(origin, pre_header);
import_block.post_digests.push(verified_info.seal);
@@ -1197,7 +1200,7 @@ where
import_block.justifications = justifications;
import_block.intermediates.insert(
Cow::from(INTERMEDIATE_KEY),
Box::new(BabeIntermediate::<Block> { epoch_descriptor }) as Box<dyn Any>,
Box::new(BabeIntermediate::<Block> { epoch_descriptor }) as Box<_>,
);
import_block.post_hash = Some(hash);
@@ -1275,6 +1278,7 @@ impl<Block: BlockT, Client, I> BabeBlockImport<Block, Client, I> {
}
}
#[async_trait::async_trait]
impl<Block, Client, Inner> BlockImport<Block> for BabeBlockImport<Block, Client, Inner> where
Block: BlockT,
Inner: BlockImport<Block, Transaction = sp_api::TransactionFor<Client, Block>> + Send + Sync,
@@ -1286,7 +1290,7 @@ impl<Block, Client, Inner> BlockImport<Block> for BabeBlockImport<Block, Client,
type Error = ConsensusError;
type Transaction = sp_api::TransactionFor<Client, Block>;
fn import_block(
async fn import_block(
&mut self,
mut block: BlockImportParams<Block, Self::Transaction>,
new_cache: HashMap<CacheKeyId, Vec<u8>>,
@@ -1328,202 +1332,209 @@ impl<Block, Client, Inner> BlockImport<Block> for BabeBlockImport<Block, Client,
);
}
let mut epoch_changes = self.epoch_changes.lock();
// check if there's any epoch change expected to happen at this slot.
// `epoch` is the epoch to verify the block under, and `first_in_epoch` is true
// if this is the first block in its chain for that epoch.
//
// also provides the total weight of the chain, including the imported block.
let (epoch_descriptor, first_in_epoch, parent_weight) = {
let parent_weight = if *parent_header.number() == Zero::zero() {
0
} else {
aux_schema::load_block_weight(&*self.client, parent_hash)
.map_err(|e| ConsensusError::ClientImport(e.to_string()))?
.ok_or_else(|| ConsensusError::ClientImport(
babe_err(Error::<Block>::ParentBlockNoAssociatedWeight(hash)).into()
))?
};
let intermediate = block.take_intermediate::<BabeIntermediate<Block>>(
INTERMEDIATE_KEY
)?;
let epoch_descriptor = intermediate.epoch_descriptor;
let first_in_epoch = parent_slot < epoch_descriptor.start_slot();
(epoch_descriptor, first_in_epoch, parent_weight)
};
let total_weight = parent_weight + pre_digest.added_weight();
// search for this all the time so we can reject unexpected announcements.
let next_epoch_digest = find_next_epoch_digest::<Block>(&block.header)
.map_err(|e| ConsensusError::ClientImport(e.to_string()))?;
let next_config_digest = find_next_config_digest::<Block>(&block.header)
.map_err(|e| ConsensusError::ClientImport(e.to_string()))?;
match (first_in_epoch, next_epoch_digest.is_some(), next_config_digest.is_some()) {
(true, true, _) => {},
(false, false, false) => {},
(false, false, true) => {
return Err(
ConsensusError::ClientImport(
babe_err(Error::<Block>::UnexpectedConfigChange).into(),
)
)
},
(true, false, _) => {
return Err(
ConsensusError::ClientImport(
babe_err(Error::<Block>::ExpectedEpochChange(hash, slot)).into(),
)
)
},
(false, true, _) => {
return Err(
ConsensusError::ClientImport(
babe_err(Error::<Block>::UnexpectedEpochChange).into(),
)
)
},
}
// if there's a pending epoch we'll save the previous epoch changes here
// this way we can revert it if there's any error
let mut old_epoch_changes = None;
let info = self.client.info();
// Use an extra scope to make the compiler happy, because otherwise he complains about the
// mutex, even if we dropped it...
let mut epoch_changes = {
let mut epoch_changes = self.epoch_changes.shared_data_locked();
if let Some(next_epoch_descriptor) = next_epoch_digest {
old_epoch_changes = Some(epoch_changes.clone());
// check if there's any epoch change expected to happen at this slot.
// `epoch` is the epoch to verify the block under, and `first_in_epoch` is true
// if this is the first block in its chain for that epoch.
//
// also provides the total weight of the chain, including the imported block.
let (epoch_descriptor, first_in_epoch, parent_weight) = {
let parent_weight = if *parent_header.number() == Zero::zero() {
0
} else {
aux_schema::load_block_weight(&*self.client, parent_hash)
.map_err(|e| ConsensusError::ClientImport(e.to_string()))?
.ok_or_else(|| ConsensusError::ClientImport(
babe_err(Error::<Block>::ParentBlockNoAssociatedWeight(hash)).into()
))?
};
let viable_epoch = epoch_changes.viable_epoch(
&epoch_descriptor,
|slot| Epoch::genesis(&self.config, slot)
).ok_or_else(|| {
ConsensusError::ClientImport(Error::<Block>::FetchEpoch(parent_hash).into())
})?;
let epoch_config = next_config_digest.map(Into::into).unwrap_or_else(
|| viable_epoch.as_ref().config.clone()
);
// restrict info logging during initial sync to avoid spam
let log_level = if block.origin == BlockOrigin::NetworkInitialSync {
log::Level::Debug
} else {
log::Level::Info
};
log!(target: "babe",
log_level,
"👶 New epoch {} launching at block {} (block slot {} >= start slot {}).",
viable_epoch.as_ref().epoch_index,
hash,
slot,
viable_epoch.as_ref().start_slot,
);
let next_epoch = viable_epoch.increment((next_epoch_descriptor, epoch_config));
log!(target: "babe",
log_level,
"👶 Next epoch starts at slot {}",
next_epoch.as_ref().start_slot,
);
// prune the tree of epochs not part of the finalized chain or
// that are not live anymore, and then track the given epoch change
// in the tree.
// NOTE: it is important that these operations are done in this
// order, otherwise if pruning after import the `is_descendent_of`
// used by pruning may not know about the block that is being
// imported.
let prune_and_import = || {
prune_finalized(
self.client.clone(),
&mut epoch_changes,
let intermediate = block.take_intermediate::<BabeIntermediate<Block>>(
INTERMEDIATE_KEY
)?;
epoch_changes.import(
descendent_query(&*self.client),
hash,
number,
*block.header.parent_hash(),
next_epoch,
).map_err(|e| ConsensusError::ClientImport(format!("{:?}", e)))?;
Ok(())
let epoch_descriptor = intermediate.epoch_descriptor;
let first_in_epoch = parent_slot < epoch_descriptor.start_slot();
(epoch_descriptor, first_in_epoch, parent_weight)
};
if let Err(e) = prune_and_import() {
debug!(target: "babe", "Failed to launch next epoch: {:?}", e);
*epoch_changes = old_epoch_changes.expect("set `Some` above and not taken; qed");
return Err(e);
let total_weight = parent_weight + pre_digest.added_weight();
// search for this all the time so we can reject unexpected announcements.
let next_epoch_digest = find_next_epoch_digest::<Block>(&block.header)
.map_err(|e| ConsensusError::ClientImport(e.to_string()))?;
let next_config_digest = find_next_config_digest::<Block>(&block.header)
.map_err(|e| ConsensusError::ClientImport(e.to_string()))?;
match (first_in_epoch, next_epoch_digest.is_some(), next_config_digest.is_some()) {
(true, true, _) => {},
(false, false, false) => {},
(false, false, true) => {
return Err(
ConsensusError::ClientImport(
babe_err(Error::<Block>::UnexpectedConfigChange).into(),
)
)
},
(true, false, _) => {
return Err(
ConsensusError::ClientImport(
babe_err(Error::<Block>::ExpectedEpochChange(hash, slot)).into(),
)
)
},
(false, true, _) => {
return Err(
ConsensusError::ClientImport(
babe_err(Error::<Block>::UnexpectedEpochChange).into(),
)
)
},
}
crate::aux_schema::write_epoch_changes::<Block, _, _>(
&*epoch_changes,
|insert| block.auxiliary.extend(
insert.iter().map(|(k, v)| (k.to_vec(), Some(v.to_vec())))
)
let info = self.client.info();
if let Some(next_epoch_descriptor) = next_epoch_digest {
old_epoch_changes = Some((*epoch_changes).clone());
let viable_epoch = epoch_changes.viable_epoch(
&epoch_descriptor,
|slot| Epoch::genesis(&self.config, slot)
).ok_or_else(|| {
ConsensusError::ClientImport(Error::<Block>::FetchEpoch(parent_hash).into())
})?;
let epoch_config = next_config_digest.map(Into::into).unwrap_or_else(
|| viable_epoch.as_ref().config.clone()
);
// restrict info logging during initial sync to avoid spam
let log_level = if block.origin == BlockOrigin::NetworkInitialSync {
log::Level::Debug
} else {
log::Level::Info
};
log!(target: "babe",
log_level,
"👶 New epoch {} launching at block {} (block slot {} >= start slot {}).",
viable_epoch.as_ref().epoch_index,
hash,
slot,
viable_epoch.as_ref().start_slot,
);
let next_epoch = viable_epoch.increment((next_epoch_descriptor, epoch_config));
log!(target: "babe",
log_level,
"👶 Next epoch starts at slot {}",
next_epoch.as_ref().start_slot,
);
// prune the tree of epochs not part of the finalized chain or
// that are not live anymore, and then track the given epoch change
// in the tree.
// NOTE: it is important that these operations are done in this
// order, otherwise if pruning after import the `is_descendent_of`
// used by pruning may not know about the block that is being
// imported.
let prune_and_import = || {
prune_finalized(
self.client.clone(),
&mut epoch_changes,
)?;
epoch_changes.import(
descendent_query(&*self.client),
hash,
number,
*block.header.parent_hash(),
next_epoch,
).map_err(|e| ConsensusError::ClientImport(format!("{:?}", e)))?;
Ok(())
};
if let Err(e) = prune_and_import() {
debug!(target: "babe", "Failed to launch next epoch: {:?}", e);
*epoch_changes = old_epoch_changes.expect("set `Some` above and not taken; qed");
return Err(e);
}
crate::aux_schema::write_epoch_changes::<Block, _, _>(
&*epoch_changes,
|insert| block.auxiliary.extend(
insert.iter().map(|(k, v)| (k.to_vec(), Some(v.to_vec())))
)
);
}
aux_schema::write_block_weight(
hash,
total_weight,
|values| block.auxiliary.extend(
values.iter().map(|(k, v)| (k.to_vec(), Some(v.to_vec())))
),
);
}
aux_schema::write_block_weight(
hash,
total_weight,
|values| block.auxiliary.extend(
values.iter().map(|(k, v)| (k.to_vec(), Some(v.to_vec())))
),
);
// The fork choice rule is that we pick the heaviest chain (i.e.
// more primary blocks), if there's a tie we go with the longest
// chain.
block.fork_choice = {
let (last_best, last_best_number) = (info.best_hash, info.best_number);
// The fork choice rule is that we pick the heaviest chain (i.e.
// more primary blocks), if there's a tie we go with the longest
// chain.
block.fork_choice = {
let (last_best, last_best_number) = (info.best_hash, info.best_number);
let last_best_weight = if &last_best == block.header.parent_hash() {
// the parent=genesis case is already covered for loading parent weight,
// so we don't need to cover again here.
parent_weight
} else {
aux_schema::load_block_weight(&*self.client, last_best)
.map_err(|e| ConsensusError::ChainLookup(format!("{:?}", e)))?
let last_best_weight = if &last_best == block.header.parent_hash() {
// the parent=genesis case is already covered for loading parent weight,
// so we don't need to cover again here.
parent_weight
} else {
aux_schema::load_block_weight(&*self.client, last_best)
.map_err(|e| ConsensusError::ChainLookup(format!("{:?}", e)))?
.ok_or_else(
|| ConsensusError::ChainLookup("No block weight for parent header.".to_string())
)?
};
Some(ForkChoiceStrategy::Custom(if total_weight > last_best_weight {
true
} else if total_weight == last_best_weight {
number > last_best_number
} else {
false
}))
};
Some(ForkChoiceStrategy::Custom(if total_weight > last_best_weight {
true
} else if total_weight == last_best_weight {
number > last_best_number
} else {
false
}))
// Release the mutex, but it stays locked
epoch_changes.release_mutex()
};
let import_result = self.inner.import_block(block, new_cache);
let import_result = self.inner.import_block(block, new_cache).await;
// revert to the original epoch changes in case there's an error
// importing the block
if import_result.is_err() {
if let Some(old_epoch_changes) = old_epoch_changes {
*epoch_changes = old_epoch_changes;
*epoch_changes.upgrade() = old_epoch_changes;
}
}
import_result.map_err(Into::into)
}
fn check_block(
async fn check_block(
&mut self,
block: BlockCheckParams<Block>,
) -> Result<ImportResult, Self::Error> {
self.inner.check_block(block).map_err(Into::into)
self.inner.check_block(block).await.map_err(Into::into)
}
}
@@ -1583,7 +1594,7 @@ pub fn block_import<Client, Block: BlockT, I>(
// startup rather than waiting until importing the next epoch change block.
prune_finalized(
client.clone(),
&mut epoch_changes.lock(),
&mut epoch_changes.shared_data(),
)?;
let import = BabeBlockImport::new(
+42 -29
View File
@@ -47,6 +47,7 @@ use rand_chacha::{
};
use sc_keystore::LocalKeystore;
use sp_application_crypto::key_types::BABE;
use futures::executor::block_on;
type Item = DigestItem<Hash>;
@@ -67,6 +68,9 @@ enum Stage {
type Mutator = Arc<dyn Fn(&mut TestHeader, Stage) + Send + Sync>;
type BabeBlockImport =
PanickingBlockImport<crate::BabeBlockImport<TestBlock, TestClient, Arc<TestClient>>>;
#[derive(Clone)]
struct DummyFactory {
client: Arc<TestClient>,
@@ -134,7 +138,7 @@ impl DummyProposer {
// figure out if we should add a consensus digest, since the test runtime
// doesn't.
let epoch_changes = self.factory.epoch_changes.lock();
let epoch_changes = self.factory.epoch_changes.shared_data();
let epoch = epoch_changes.epoch_data_for_child_of(
descendent_query(&*self.factory.client),
&self.parent_hash,
@@ -188,30 +192,37 @@ thread_local! {
}
#[derive(Clone)]
struct PanickingBlockImport<B>(B);
pub struct PanickingBlockImport<B>(B);
impl<B: BlockImport<TestBlock>> BlockImport<TestBlock> for PanickingBlockImport<B> {
#[async_trait::async_trait]
impl<B: BlockImport<TestBlock>> BlockImport<TestBlock> for PanickingBlockImport<B>
where
B::Transaction: Send,
B: Send,
{
type Error = B::Error;
type Transaction = B::Transaction;
fn import_block(
async fn import_block(
&mut self,
block: BlockImportParams<TestBlock, Self::Transaction>,
new_cache: HashMap<CacheKeyId, Vec<u8>>,
) -> Result<ImportResult, Self::Error> {
Ok(self.0.import_block(block, new_cache).expect("importing block failed"))
Ok(self.0.import_block(block, new_cache).await.expect("importing block failed"))
}
fn check_block(
async fn check_block(
&mut self,
block: BlockCheckParams<TestBlock>,
) -> Result<ImportResult, Self::Error> {
Ok(self.0.check_block(block).expect("checking block failed"))
Ok(self.0.check_block(block).await.expect("checking block failed"))
}
}
type BabePeer = Peer<Option<PeerData>, BabeBlockImport>;
pub struct BabeTestNet {
peers: Vec<Peer<Option<PeerData>>>,
peers: Vec<BabePeer>,
}
type TestHeader = <TestBlock as BlockT>::Header;
@@ -227,11 +238,12 @@ pub struct TestVerifier {
mutator: Mutator,
}
#[async_trait::async_trait]
impl Verifier<TestBlock> for TestVerifier {
/// 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(
async fn verify(
&mut self,
origin: BlockOrigin,
mut header: TestHeader,
@@ -240,7 +252,7 @@ impl Verifier<TestBlock> for TestVerifier {
) -> Result<(BlockImportParams<TestBlock, ()>, Option<Vec<(CacheKeyId, Vec<u8>)>>), String> {
// apply post-sealing mutations (i.e. stripping seal, if desired).
(self.mutator)(&mut header, Stage::PostSeal);
self.inner.verify(origin, header, justifications, body)
self.inner.verify(dbg!(origin), header, justifications, body).await
}
}
@@ -255,6 +267,7 @@ pub struct PeerData {
impl TestNetFactory for BabeTestNet {
type Verifier = TestVerifier;
type PeerData = Option<PeerData>;
type BlockImport = BabeBlockImport;
/// Create new test network with peers and given config.
fn from_config(_config: &ProtocolConfig) -> Self {
@@ -264,9 +277,9 @@ impl TestNetFactory for BabeTestNet {
}
}
fn make_block_import<Transaction>(&self, client: PeersClient)
fn make_block_import(&self, client: PeersClient)
-> (
BlockImportAdapter<Transaction>,
BlockImportAdapter<Self::BlockImport>,
Option<BoxJustificationImport<Block>>,
Option<PeerData>,
)
@@ -287,7 +300,7 @@ impl TestNetFactory for BabeTestNet {
Some(Box::new(block_import.clone()) as BoxBlockImport<_, _>)
);
(
BlockImportAdapter::new_full(block_import),
BlockImportAdapter::new(block_import),
None,
Some(PeerData { link, inherent_data_providers, block_import: data_block_import }),
)
@@ -326,17 +339,17 @@ impl TestNetFactory for BabeTestNet {
}
}
fn peer(&mut self, i: usize) -> &mut Peer<Self::PeerData> {
fn peer(&mut self, i: usize) -> &mut BabePeer {
trace!(target: "babe", "Retrieving a peer");
&mut self.peers[i]
}
fn peers(&self) -> &Vec<Peer<Self::PeerData>> {
fn peers(&self) -> &Vec<BabePeer> {
trace!(target: "babe", "Retrieving peers");
&self.peers
}
fn mut_peers<F: FnOnce(&mut Vec<Peer<Self::PeerData>>)>(
fn mut_peers<F: FnOnce(&mut Vec<BabePeer>)>(
&mut self,
closure: F,
) {
@@ -436,7 +449,7 @@ fn run_one_test(
telemetry: None,
}).expect("Starts babe"));
}
futures::executor::block_on(future::select(
block_on(future::select(
futures::future::poll_fn(move |cx| {
let mut net = net.lock();
net.poll(cx);
@@ -567,7 +580,7 @@ fn can_author_block() {
}
// Propose and import a new BABE block on top of the given parent.
fn propose_and_import_block<Transaction>(
fn propose_and_import_block<Transaction: Send + 'static>(
parent: &TestHeader,
slot: Option<Slot>,
proposer_factory: &mut DummyFactory,
@@ -595,7 +608,7 @@ fn propose_and_import_block<Transaction>(
let mut block = futures::executor::block_on(proposer.propose_with(pre_digest)).unwrap().block;
let epoch_descriptor = proposer_factory.epoch_changes.lock().epoch_descriptor_for_child_of(
let epoch_descriptor = proposer_factory.epoch_changes.shared_data().epoch_descriptor_for_child_of(
descendent_query(&*proposer_factory.client),
&parent_hash,
*parent.number(),
@@ -623,10 +636,10 @@ fn propose_and_import_block<Transaction>(
import.body = Some(block.extrinsics);
import.intermediates.insert(
Cow::from(INTERMEDIATE_KEY),
Box::new(BabeIntermediate::<TestBlock> { epoch_descriptor }) as Box<dyn Any>,
Box::new(BabeIntermediate::<TestBlock> { epoch_descriptor }) as Box<_>,
);
import.fork_choice = Some(ForkChoiceStrategy::LongestChain);
let import_result = block_import.import_block(import, Default::default()).unwrap();
let import_result = block_on(block_import.import_block(import, Default::default())).unwrap();
match import_result {
ImportResult::Imported(_) => {},
@@ -664,7 +677,7 @@ fn importing_block_one_sets_genesis_epoch() {
let genesis_epoch = Epoch::genesis(&data.link.config, 999.into());
let epoch_changes = data.link.epoch_changes.lock();
let epoch_changes = data.link.epoch_changes.shared_data();
let epoch_for_second_block = epoch_changes.epoch_data_for_child_of(
descendent_query(&*client),
&block_hash,
@@ -739,13 +752,13 @@ fn importing_epoch_change_block_prunes_tree() {
// We should be tracking a total of 9 epochs in the fork tree
assert_eq!(
epoch_changes.lock().tree().iter().count(),
epoch_changes.shared_data().tree().iter().count(),
9,
);
// And only one root
assert_eq!(
epoch_changes.lock().tree().roots().count(),
epoch_changes.shared_data().tree().roots().count(),
1,
);
@@ -756,16 +769,16 @@ fn importing_epoch_change_block_prunes_tree() {
// at this point no hashes from the first fork must exist on the tree
assert!(
!epoch_changes.lock().tree().iter().map(|(h, _, _)| h).any(|h| fork_1.contains(h)),
!epoch_changes.shared_data().tree().iter().map(|(h, _, _)| h).any(|h| fork_1.contains(h)),
);
// but the epoch changes from the other forks must still exist
assert!(
epoch_changes.lock().tree().iter().map(|(h, _, _)| h).any(|h| fork_2.contains(h))
epoch_changes.shared_data().tree().iter().map(|(h, _, _)| h).any(|h| fork_2.contains(h))
);
assert!(
epoch_changes.lock().tree().iter().map(|(h, _, _)| h).any(|h| fork_3.contains(h)),
epoch_changes.shared_data().tree().iter().map(|(h, _, _)| h).any(|h| fork_3.contains(h)),
);
// finalizing block #25 from the canon chain should prune out the second fork
@@ -774,12 +787,12 @@ fn importing_epoch_change_block_prunes_tree() {
// at this point no hashes from the second fork must exist on the tree
assert!(
!epoch_changes.lock().tree().iter().map(|(h, _, _)| h).any(|h| fork_2.contains(h)),
!epoch_changes.shared_data().tree().iter().map(|(h, _, _)| h).any(|h| fork_2.contains(h)),
);
// while epoch changes from the last fork should still exist
assert!(
epoch_changes.lock().tree().iter().map(|(h, _, _)| h).any(|h| fork_3.contains(h)),
epoch_changes.shared_data().tree().iter().map(|(h, _, _)| h).any(|h| fork_3.contains(h)),
);
}
@@ -17,3 +17,4 @@ sc-client-api = { version = "3.0.0", path = "../../api" }
sp-blockchain = { version = "3.0.0", path = "../../../primitives/blockchain" }
sp-runtime = { version = "3.0.0", path = "../../../primitives/runtime" }
sp-consensus = { version = "0.9.0", path = "../../../primitives/consensus/common" }
parking_lot = "0.11.1"
@@ -17,6 +17,8 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//! Collection of common consensus specific implementations
mod longest_chain;
pub mod shared_data;
pub use longest_chain::LongestChain;
@@ -0,0 +1,271 @@
// This file is part of Substrate.
// Copyright (C) 2021 Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
// This program 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.
// This program 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 this program. If not, see <https://www.gnu.org/licenses/>.
//! Provides a generic wrapper around shared data. See [`SharedData`] for more information.
use std::sync::Arc;
use parking_lot::{Mutex, MappedMutexGuard, Condvar, MutexGuard};
/// Created by [`SharedDataLocked::release_mutex`].
///
/// As long as the object isn't dropped, the shared data is locked. It is advised to drop this
/// object when the shared data doesn't need to be locked anymore. To get access to the shared data
/// [`Self::upgrade`] is provided.
#[must_use = "Shared data will be unlocked on drop!"]
pub struct SharedDataLockedUpgradable<T> {
shared_data: SharedData<T>,
}
impl<T> SharedDataLockedUpgradable<T> {
/// Upgrade to a *real* mutex guard that will give access to the inner data.
///
/// Every call to this function will reaquire the mutex again.
pub fn upgrade(&mut self) -> MappedMutexGuard<T> {
MutexGuard::map(self.shared_data.inner.lock(), |i| &mut i.shared_data)
}
}
impl<T> Drop for SharedDataLockedUpgradable<T> {
fn drop(&mut self) {
let mut inner = self.shared_data.inner.lock();
// It should not be locked anymore
inner.locked = false;
// Notify all waiting threads.
self.shared_data.cond_var.notify_all();
}
}
/// Created by [`SharedData::shared_data_locked`].
///
/// As long as this object isn't dropped, the shared data is held in a mutex guard and the shared
/// data is tagged as locked. Access to the shared data is provided through [`Deref`] and
/// [`DerefMut`]. The trick is to use [`Self::release_mutex`] to release the mutex, but still keep
/// the shared data locked. This means every other thread trying to access the shared data in this
/// time will need to wait until this lock is freed.
///
/// If this object is dropped without calling [`Self::release_mutex`], the lock will be dropped
/// immediately.
#[must_use = "Shared data will be unlocked on drop!"]
pub struct SharedDataLocked<'a, T> {
/// The current active mutex guard holding the inner data.
inner: MutexGuard<'a, SharedDataInner<T>>,
/// The [`SharedData`] instance that created this instance.
///
/// This instance is only taken on drop or when calling [`Self::release_mutex`].
shared_data: Option<SharedData<T>>,
}
impl<'a, T> SharedDataLocked<'a, T> {
/// Release the mutex, but keep the shared data locked.
pub fn release_mutex(mut self) -> SharedDataLockedUpgradable<T> {
SharedDataLockedUpgradable {
shared_data: self.shared_data.take()
.expect("`shared_data` is only taken on drop; qed"),
}
}
}
impl<'a, T> Drop for SharedDataLocked<'a, T> {
fn drop(&mut self) {
if let Some(shared_data) = self.shared_data.take() {
// If the `shared_data` is still set, it means [`Self::release_mutex`] wasn't
// called and the lock should be released.
self.inner.locked = false;
// Notify all waiting threads about the released lock.
shared_data.cond_var.notify_all();
}
}
}
impl<'a, T> std::ops::Deref for SharedDataLocked<'a, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.inner.shared_data
}
}
impl<'a, T> std::ops::DerefMut for SharedDataLocked<'a, T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.inner.shared_data
}
}
/// Holds the shared data and if the shared data is currently locked.
///
/// For more information see [`SharedData`].
struct SharedDataInner<T> {
/// The actual shared data that is protected here against concurrent access.
shared_data: T,
/// Is `shared_data` currently locked and can not be accessed?
locked: bool,
}
/// Some shared data that provides support for locking this shared data for some time.
///
/// When working with consensus engines there is often data that needs to be shared between multiple
/// parts of the system, like block production and block import. This struct provides an abstraction
/// for this shared data in a generic way.
///
/// The pain point when sharing this data is often the usage of mutex guards in an async context as
/// this doesn't work for most of them as these guards don't implement `Send`. This abstraction
/// provides a way to lock the shared data, while not having the mutex locked. So, the data stays
/// locked and we are still able to hold this lock over an `await` call.
///
/// # Example
///
/// ```
///# use sc_consensus::shared_data::SharedData;
///
/// let shared_data = SharedData::new(String::from("hello world"));
///
/// let lock = shared_data.shared_data_locked();
///
/// let shared_data2 = shared_data.clone();
/// let join_handle1 = std::thread::spawn(move || {
/// // This will need to wait for the outer lock to be released before it can access the data.
/// shared_data2.shared_data().push_str("1");
/// });
///
/// assert_eq!(*lock, "hello world");
///
/// // Let us release the mutex, but we still keep it locked.
/// // Now we could call `await` for example.
/// let mut lock = lock.release_mutex();
///
/// let shared_data2 = shared_data.clone();
/// let join_handle2 = std::thread::spawn(move || {
/// shared_data2.shared_data().push_str("2");
/// });
///
/// // We still have the lock and can upgrade it to access the data.
/// assert_eq!(*lock.upgrade(), "hello world");
/// lock.upgrade().push_str("3");
///
/// drop(lock);
/// join_handle1.join().unwrap();
/// join_handle2.join().unwrap();
///
/// let data = shared_data.shared_data();
/// // As we don't know the order of the threads, we need to check for both combinations
/// assert!(*data == "hello world321" || *data == "hello world312");
/// ```
pub struct SharedData<T> {
inner: Arc<Mutex<SharedDataInner<T>>>,
cond_var: Arc<Condvar>,
}
impl<T> Clone for SharedData<T> {
fn clone(&self) -> Self {
Self {
inner: self.inner.clone(),
cond_var: self.cond_var.clone(),
}
}
}
impl<T> SharedData<T> {
/// Create a new instance of [`SharedData`] to share the given `shared_data`.
pub fn new(shared_data: T) -> Self {
Self {
inner: Arc::new(Mutex::new(SharedDataInner { shared_data, locked: false })),
cond_var: Default::default(),
}
}
/// Acquire access to the shared data.
///
/// This will give mutable access to the shared data. After the returned mutex guard is dropped,
/// the shared data is accessible by other threads. So, this function should be used when
/// reading/writing of the shared data in a local context is required.
///
/// When requiring to lock shared data for some longer time, even with temporarily releasing the
/// lock, [`Self::shared_data_locked`] should be used.
pub fn shared_data(&self) -> MappedMutexGuard<T> {
let mut guard = self.inner.lock();
while guard.locked {
self.cond_var.wait(&mut guard);
}
debug_assert!(!guard.locked);
MutexGuard::map(guard, |i| &mut i.shared_data)
}
/// Acquire access to the shared data and lock it.
///
/// This will give mutable access to the shared data. The returned [`SharedDataLocked`]
/// provides the function [`SharedDataLocked::release_mutex`] to release the mutex, but
/// keeping the data locked. This is useful in async contexts for example where the data needs to
/// be locked, but a mutex guard can not be held.
///
/// For an example see [`SharedData`].
pub fn shared_data_locked(&self) -> SharedDataLocked<T> {
let mut guard = self.inner.lock();
while guard.locked {
self.cond_var.wait(&mut guard);
}
debug_assert!(!guard.locked);
guard.locked = true;
SharedDataLocked {
inner: guard,
shared_data: Some(self.clone()),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn shared_data_locking_works() {
const THREADS: u32 = 100;
let shared_data = SharedData::new(0u32);
let lock = shared_data.shared_data_locked();
for i in 0..THREADS {
let data = shared_data.clone();
std::thread::spawn(move || {
if i % 2 == 1 {
*data.shared_data() += 1;
} else {
let mut lock = data.shared_data_locked().release_mutex();
// Give the other threads some time to wake up
std::thread::sleep(std::time::Duration::from_millis(10));
*lock.upgrade() += 1;
}
});
}
let lock = lock.release_mutex();
std::thread::sleep(std::time::Duration::from_millis(100));
drop(lock);
while *shared_data.shared_data() < THREADS {
std::thread::sleep(std::time::Duration::from_millis(100));
}
}
}
+1 -1
View File
@@ -14,8 +14,8 @@ targets = ["x86_64-unknown-linux-gnu"]
[dependencies]
codec = { package = "parity-scale-codec", version = "2.0.0", features = ["derive"] }
parking_lot = "0.11.1"
fork-tree = { version = "3.0.0", path = "../../../utils/fork-tree" }
sp-runtime = { path = "../../../primitives/runtime" , version = "3.0.0"}
sp-blockchain = { version = "3.0.0", path = "../../../primitives/blockchain" }
sc-client-api = { path = "../../api" , version = "3.0.0"}
sc-consensus = { path = "../common" , version = "0.9.0"}
+5 -4
View File
@@ -20,8 +20,7 @@
pub mod migration;
use std::{sync::Arc, ops::Add, collections::BTreeMap, borrow::{Borrow, BorrowMut}};
use parking_lot::Mutex;
use std::{ops::Add, collections::BTreeMap, borrow::{Borrow, BorrowMut}};
use codec::{Encode, Decode};
use fork_tree::ForkTree;
use sc_client_api::utils::is_descendent_of;
@@ -645,10 +644,12 @@ impl<Hash, Number, E: Epoch> EpochChanges<Hash, Number, E> where
}
/// Type alias to produce the epoch-changes tree from a block type.
pub type EpochChangesFor<Block, Epoch> = EpochChanges<<Block as BlockT>::Hash, NumberFor<Block>, Epoch>;
pub type EpochChangesFor<Block, Epoch> =
EpochChanges<<Block as BlockT>::Hash, NumberFor<Block>, Epoch>;
/// A shared epoch changes tree.
pub type SharedEpochChanges<Block, Epoch> = Arc<Mutex<EpochChangesFor<Block, Epoch>>>;
pub type SharedEpochChanges<Block, Epoch> =
sc_consensus::shared_data::SharedData<EpochChangesFor<Block, Epoch>>;
#[cfg(test)]
mod tests {
@@ -23,6 +23,7 @@ parking_lot = "0.11.1"
codec = { package = "parity-scale-codec", version = "2.0.0" }
serde = { version = "1.0", features=["derive"] }
assert_matches = "1.3.0"
async-trait = "0.1.42"
sc-client-api = { path = "../../api", version = "3.0.0"}
sc-consensus-babe = { path = "../../consensus/babe", version = "0.9.0"}
@@ -21,12 +21,7 @@
use super::ConsensusDataProvider;
use crate::Error;
use codec::Encode;
use std::{
any::Any,
borrow::Cow,
sync::{Arc, atomic},
time::SystemTime,
};
use std::{borrow::Cow, sync::{Arc, atomic}, time::SystemTime};
use sc_client_api::AuxStore;
use sc_consensus_babe::{
Config, Epoch, authorship, CompatibleDigestItem, BabeIntermediate,
@@ -102,7 +97,7 @@ impl<B, C> BabeConsensusDataProvider<B, C>
}
fn epoch(&self, parent: &B::Header, slot: Slot) -> Result<Epoch, Error> {
let epoch_changes = self.epoch_changes.lock();
let epoch_changes = self.epoch_changes.shared_data();
let epoch_descriptor = epoch_changes
.epoch_descriptor_for_child_of(
descendent_query(&*self.client),
@@ -156,7 +151,7 @@ impl<B, C> ConsensusDataProvider<B> for BabeConsensusDataProvider<B, C>
authority_index: 0_u32,
});
let mut epoch_changes = self.epoch_changes.lock();
let mut epoch_changes = self.epoch_changes.shared_data();
let epoch_descriptor = epoch_changes
.epoch_descriptor_for_child_of(
descendent_query(&*self.client),
@@ -200,7 +195,7 @@ impl<B, C> ConsensusDataProvider<B> for BabeConsensusDataProvider<B, C>
inherents: &InherentData
) -> Result<(), Error> {
let slot = inherents.babe_inherent_data()?;
let epoch_changes = self.epoch_changes.lock();
let epoch_changes = self.epoch_changes.shared_data();
let mut epoch_descriptor = epoch_changes
.epoch_descriptor_for_child_of(
descendent_query(&*self.client),
@@ -239,7 +234,7 @@ impl<B, C> ConsensusDataProvider<B> for BabeConsensusDataProvider<B, C>
params.intermediates.insert(
Cow::from(INTERMEDIATE_KEY),
Box::new(BabeIntermediate::<B> { epoch_descriptor }) as Box<dyn Any>,
Box::new(BabeIntermediate::<B> { epoch_descriptor }) as Box<_>,
);
Ok(())
@@ -55,8 +55,9 @@ pub const MANUAL_SEAL_ENGINE_ID: ConsensusEngineId = [b'm', b'a', b'n', b'l'];
/// The verifier for the manual seal engine; instantly finalizes.
struct ManualSealVerifier;
#[async_trait::async_trait]
impl<B: BlockT> Verifier<B> for ManualSealVerifier {
fn verify(
async fn verify(
&mut self,
origin: BlockOrigin,
header: B::Header,
@@ -144,7 +144,7 @@ pub async fn seal_block<B, BI, SC, C, E, P>(
digest_provider.append_block_import(&parent, &mut params, &id)?;
}
match block_import.import_block(params, HashMap::new())? {
match block_import.import_block(params, HashMap::new()).await? {
ImportResult::Imported(aux) => {
Ok(CreatedBlock { hash: <B as BlockT>::Header::hash(&header), aux })
},
@@ -30,3 +30,4 @@ parking_lot = "0.11.1"
sp-timestamp = { version = "3.0.0", path = "../../../primitives/timestamp" }
derive_more = "0.99.2"
prometheus-endpoint = { package = "substrate-prometheus-endpoint", path = "../../../utils/prometheus", version = "0.9.0"}
async-trait = "0.1.42"
+15 -12
View File
@@ -36,7 +36,7 @@ mod worker;
pub use crate::worker::{MiningWorker, MiningMetadata, MiningBuild};
use std::{
sync::Arc, any::Any, borrow::Cow, collections::HashMap, marker::PhantomData,
sync::Arc, borrow::Cow, collections::HashMap, marker::PhantomData,
cmp::Ordering, time::Duration,
};
use futures::{prelude::*, future::Either};
@@ -307,6 +307,7 @@ impl<B, I, C, S, Algorithm, CAW> PowBlockImport<B, I, C, S, Algorithm, CAW> wher
}
}
#[async_trait::async_trait]
impl<B, I, C, S, Algorithm, CAW> BlockImport<B> for PowBlockImport<B, I, C, S, Algorithm, CAW> where
B: BlockT,
I: BlockImport<B, Transaction = sp_api::TransactionFor<C, B>> + Send + Sync,
@@ -314,21 +315,21 @@ impl<B, I, C, S, Algorithm, CAW> BlockImport<B> for PowBlockImport<B, I, C, S, A
S: SelectChain<B>,
C: ProvideRuntimeApi<B> + Send + Sync + HeaderBackend<B> + AuxStore + ProvideCache<B> + BlockOf,
C::Api: BlockBuilderApi<B>,
Algorithm: PowAlgorithm<B>,
Algorithm::Difficulty: 'static,
CAW: CanAuthorWith<B>,
Algorithm: PowAlgorithm<B> + Send,
Algorithm::Difficulty: 'static + Send,
CAW: CanAuthorWith<B> + Send,
{
type Error = ConsensusError;
type Transaction = sp_api::TransactionFor<C, B>;
fn check_block(
async fn check_block(
&mut self,
block: BlockCheckParams<B>,
) -> Result<ImportResult, Self::Error> {
self.inner.check_block(block).map_err(Into::into)
self.inner.check_block(block).await.map_err(Into::into)
}
fn import_block(
async fn import_block(
&mut self,
mut block: BlockImportParams<B, Self::Transaction>,
new_cache: HashMap<CacheKeyId, Vec<u8>>,
@@ -403,7 +404,7 @@ impl<B, I, C, S, Algorithm, CAW> BlockImport<B> for PowBlockImport<B, I, C, S, A
));
}
self.inner.import_block(block, new_cache).map_err(Into::into)
self.inner.import_block(block, new_cache).await.map_err(Into::into)
}
}
@@ -449,11 +450,12 @@ impl<B: BlockT, Algorithm> PowVerifier<B, Algorithm> {
}
}
#[async_trait::async_trait]
impl<B: BlockT, Algorithm> Verifier<B> for PowVerifier<B, Algorithm> where
Algorithm: PowAlgorithm<B> + Send + Sync,
Algorithm::Difficulty: 'static,
Algorithm::Difficulty: 'static + Send,
{
fn verify(
async fn verify(
&mut self,
origin: BlockOrigin,
header: B::Header,
@@ -473,7 +475,7 @@ impl<B: BlockT, Algorithm> Verifier<B> for PowVerifier<B, Algorithm> where
import_block.justifications = justifications;
import_block.intermediates.insert(
Cow::from(INTERMEDIATE_KEY),
Box::new(intermediate) as Box<dyn Any>
Box::new(intermediate) as Box<_>,
);
import_block.post_hash = Some(hash);
@@ -513,6 +515,7 @@ pub fn import_queue<B, Transaction, Algorithm>(
B: BlockT,
Transaction: Send + Sync + 'static,
Algorithm: PowAlgorithm<B> + Clone + Send + Sync + 'static,
Algorithm::Difficulty: Send,
{
register_pow_inherent_data_provider(&inherent_data_providers)?;
@@ -556,7 +559,7 @@ pub fn start_mining_worker<Block, C, S, Algorithm, E, SO, CAW>(
C: ProvideRuntimeApi<Block> + BlockchainEvents<Block> + 'static,
S: SelectChain<Block> + 'static,
Algorithm: PowAlgorithm<Block> + Clone,
Algorithm::Difficulty: 'static,
Algorithm::Difficulty: Send + 'static,
E: Environment<Block> + Send + Sync + 'static,
E::Error: std::fmt::Debug,
E::Proposer: Proposer<Block, Transaction = sp_api::TransactionFor<C, Block>>,
+6 -5
View File
@@ -16,7 +16,7 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
use std::{pin::Pin, time::Duration, collections::HashMap, any::Any, borrow::Cow};
use std::{pin::Pin, time::Duration, collections::HashMap, borrow::Cow};
use sc_client_api::ImportNotifications;
use sp_runtime::{DigestItem, traits::Block as BlockT, generic::BlockId};
use sp_consensus::{Proposal, BlockOrigin, BlockImportParams, import_queue::BoxBlockImport};
@@ -68,7 +68,8 @@ impl<Block, Algorithm, C, Proof> MiningWorker<Block, Algorithm, C, Proof> where
Block: BlockT,
C: sp_api::ProvideRuntimeApi<Block>,
Algorithm: PowAlgorithm<Block>,
Algorithm::Difficulty: 'static,
Algorithm::Difficulty: 'static + Send,
sp_api::TransactionFor<C, Block>: Send + 'static,
{
/// Get the current best hash. `None` if the worker has just started or the client is doing
/// major syncing.
@@ -94,7 +95,7 @@ impl<Block, Algorithm, C, Proof> MiningWorker<Block, Algorithm, C, Proof> where
/// Submit a mined seal. The seal will be validated again. Returns true if the submission is
/// successful.
pub fn submit(&mut self, seal: Seal) -> bool {
pub async fn submit(&mut self, seal: Seal) -> bool {
if let Some(build) = self.build.take() {
match self.algorithm.verify(
&BlockId::Hash(build.metadata.best_hash),
@@ -135,10 +136,10 @@ impl<Block, Algorithm, C, Proof> MiningWorker<Block, Algorithm, C, Proof> where
import_block.intermediates.insert(
Cow::from(INTERMEDIATE_KEY),
Box::new(intermediate) as Box<dyn Any>
Box::new(intermediate) as Box<_>,
);
match self.block_import.import_block(import_block, HashMap::default()) {
match self.block_import.import_block(import_block, HashMap::default()).await {
Ok(_) => {
info!(
target: "pow",
@@ -398,6 +398,7 @@ pub trait SimpleSlotWorker<B: BlockT> {
let header = block_import_params.post_header();
if let Err(err) = block_import
.import_block(block_import_params, Default::default())
.await
{
warn!(
target: logging_target,
@@ -237,7 +237,7 @@ mod tests {
block.header.digest_mut().logs.push(digest);
}
client.import(BlockOrigin::Own, block).unwrap();
futures::executor::block_on(client.import(BlockOrigin::Own, block)).unwrap();
if let Some(new_authorities) = new_authorities {
// generate a justification for this block, finalize it and note the authority set
@@ -47,6 +47,7 @@ sc-block-builder = { version = "0.9.0", path = "../block-builder" }
finality-grandpa = { version = "0.14.0", features = ["derive-codec"] }
pin-project = "1.0.4"
linked-hash-map = "0.5.2"
async-trait = "0.1.42"
[dev-dependencies]
assert_matches = "1.3.0"
@@ -19,17 +19,17 @@
//! Utilities for dealing with authorities, authority sets, and handoffs.
use fork_tree::ForkTree;
use parking_lot::RwLock;
use parking_lot::MappedMutexGuard;
use finality_grandpa::voter_set::VoterSet;
use parity_scale_codec::{Encode, Decode};
use log::debug;
use sc_telemetry::{telemetry, TelemetryHandle, CONSENSUS_INFO};
use sp_finality_grandpa::{AuthorityId, AuthorityList};
use sc_consensus::shared_data::{SharedData, SharedDataLocked};
use std::cmp::Ord;
use std::fmt::Debug;
use std::ops::Add;
use std::sync::Arc;
/// Error type returned on operations on the `AuthoritySet`.
#[derive(Debug, derive_more::Display)]
@@ -70,19 +70,30 @@ impl<N, E: std::error::Error> From<E> for Error<N, E> {
/// A shared authority set.
pub struct SharedAuthoritySet<H, N> {
inner: Arc<RwLock<AuthoritySet<H, N>>>,
inner: SharedData<AuthoritySet<H, N>>,
}
impl<H, N> Clone for SharedAuthoritySet<H, N> {
fn clone(&self) -> Self {
SharedAuthoritySet { inner: self.inner.clone() }
SharedAuthoritySet {
inner: self.inner.clone(),
}
}
}
impl<H, N> SharedAuthoritySet<H, N> {
/// Acquire a reference to the inner read-write lock.
pub(crate) fn inner(&self) -> &RwLock<AuthoritySet<H, N>> {
&*self.inner
/// Returns access to the [`AuthoritySet`].
pub(crate) fn inner(&self) -> MappedMutexGuard<AuthoritySet<H, N>> {
self.inner.shared_data()
}
/// Returns access to the [`AuthoritySet`] and locks it.
///
/// For more information see [`SharedDataLocked`].
pub(crate) fn inner_locked(
&self,
) -> SharedDataLocked<AuthoritySet<H, N>> {
self.inner.shared_data_locked()
}
}
@@ -93,17 +104,17 @@ where N: Add<Output=N> + Ord + Clone + Debug,
/// Get the earliest limit-block number that's higher or equal to the given
/// min number, if any.
pub(crate) fn current_limit(&self, min: N) -> Option<N> {
self.inner.read().current_limit(min)
self.inner().current_limit(min)
}
/// Get the current set ID. This is incremented every time the set changes.
pub fn set_id(&self) -> u64 {
self.inner.read().set_id
self.inner().set_id
}
/// Get the current authorities and their weights (for the current set ID).
pub fn current_authorities(&self) -> VoterSet<AuthorityId> {
VoterSet::new(self.inner.read().current_authorities.iter().cloned()).expect(
VoterSet::new(self.inner().current_authorities.iter().cloned()).expect(
"current_authorities is non-empty and weights are non-zero; \
constructor and all mutating operations on `AuthoritySet` ensure this; \
qed.",
@@ -112,18 +123,20 @@ where N: Add<Output=N> + Ord + Clone + Debug,
/// Clone the inner `AuthoritySet`.
pub fn clone_inner(&self) -> AuthoritySet<H, N> {
self.inner.read().clone()
self.inner().clone()
}
/// Clone the inner `AuthoritySetChanges`.
pub fn authority_set_changes(&self) -> AuthoritySetChanges<N> {
self.inner.read().authority_set_changes.clone()
self.inner().authority_set_changes.clone()
}
}
impl<H, N> From<AuthoritySet<H, N>> for SharedAuthoritySet<H, N> {
fn from(set: AuthoritySet<H, N>) -> Self {
SharedAuthoritySet { inner: Arc::new(RwLock::new(set)) }
SharedAuthoritySet {
inner: SharedData::new(set),
}
}
}
@@ -592,7 +592,7 @@ mod test {
).unwrap();
assert_eq!(
*authority_set.inner().read(),
*authority_set.inner(),
AuthoritySet::new(
authorities.clone(),
set_id,
@@ -616,7 +616,7 @@ mod test {
votes: vec![],
},
set_id,
&*authority_set.inner().read(),
&*authority_set.inner(),
),
current_rounds,
},
@@ -688,7 +688,7 @@ mod test {
).unwrap();
assert_eq!(
*authority_set.inner().read(),
*authority_set.inner(),
AuthoritySet::new(
authorities.clone(),
set_id,
@@ -712,7 +712,7 @@ mod test {
votes: vec![],
},
set_id,
&*authority_set.inner().read(),
&*authority_set.inner(),
),
current_rounds,
},
@@ -781,7 +781,7 @@ mod test {
).unwrap();
assert_eq!(
*authority_set.inner().read(),
*authority_set.inner(),
AuthoritySet::new(
authorities.clone(),
set_id,
@@ -508,7 +508,7 @@ where
.best_chain()
.map_err(|e| Error::Blockchain(e.to_string()))?;
let authority_set = self.authority_set.inner().read();
let authority_set = self.authority_set.inner();
// block hash and number of the next pending authority set change in the
// given best chain.
@@ -1228,7 +1228,7 @@ where
// NOTE: lock must be held through writing to DB to avoid race. this lock
// also implicitly synchronizes the check for last finalized number
// below.
let mut authority_set = authority_set.inner().write();
let mut authority_set = authority_set.inner();
let status = client.info();
+35 -26
View File
@@ -20,13 +20,13 @@ use std::{sync::Arc, collections::HashMap};
use log::debug;
use parity_scale_codec::Encode;
use parking_lot::RwLockWriteGuard;
use sp_blockchain::{BlockStatus, well_known_cache_keys};
use sc_client_api::{backend::Backend, utils::is_descendent_of};
use sc_telemetry::TelemetryHandle;
use sp_utils::mpsc::TracingUnboundedSender;
use sp_api::TransactionFor;
use sc_consensus::shared_data::{SharedDataLockedUpgradable, SharedDataLocked};
use sp_consensus::{
BlockImport, Error as ConsensusError,
@@ -99,7 +99,7 @@ impl<BE, Block: BlockT, Client, SC> JustificationImport<Block>
let chain_info = self.inner.info();
// request justifications for all pending changes for which change blocks have already been imported
let authorities = self.authority_set.inner().read();
let authorities = self.authority_set.inner();
for pending_change in authorities.pending_changes() {
if pending_change.delay_kind == DelayKind::Finalized &&
pending_change.effective_number() > chain_info.finalized_number &&
@@ -157,30 +157,30 @@ impl<H, N> AppliedChanges<H, N> {
}
}
struct PendingSetChanges<'a, Block: 'a + BlockT> {
struct PendingSetChanges<Block: BlockT> {
just_in_case: Option<(
AuthoritySet<Block::Hash, NumberFor<Block>>,
RwLockWriteGuard<'a, AuthoritySet<Block::Hash, NumberFor<Block>>>,
SharedDataLockedUpgradable<AuthoritySet<Block::Hash, NumberFor<Block>>>,
)>,
applied_changes: AppliedChanges<Block::Hash, NumberFor<Block>>,
do_pause: bool,
}
impl<'a, Block: 'a + BlockT> PendingSetChanges<'a, Block> {
impl<Block: BlockT> PendingSetChanges<Block> {
// revert the pending set change explicitly.
fn revert(self) { }
fn revert(self) {}
fn defuse(mut self) -> (AppliedChanges<Block::Hash, NumberFor<Block>>, bool) {
self.just_in_case = None;
let applied_changes = ::std::mem::replace(&mut self.applied_changes, AppliedChanges::None);
let applied_changes = std::mem::replace(&mut self.applied_changes, AppliedChanges::None);
(applied_changes, self.do_pause)
}
}
impl<'a, Block: 'a + BlockT> Drop for PendingSetChanges<'a, Block> {
impl<Block: BlockT> Drop for PendingSetChanges<Block> {
fn drop(&mut self) {
if let Some((old_set, mut authorities)) = self.just_in_case.take() {
*authorities = old_set;
*authorities.upgrade() = old_set;
}
}
}
@@ -269,33 +269,38 @@ where
// when we update the authorities, we need to hold the lock
// until the block is written to prevent a race if we need to restore
// the old authority set on error or panic.
struct InnerGuard<'a, T: 'a> {
old: Option<T>,
guard: Option<RwLockWriteGuard<'a, T>>,
struct InnerGuard<'a, H, N> {
old: Option<AuthoritySet<H, N>>,
guard: Option<SharedDataLocked<'a, AuthoritySet<H, N>>>,
}
impl<'a, T: 'a> InnerGuard<'a, T> {
fn as_mut(&mut self) -> &mut T {
impl<'a, H, N> InnerGuard<'a, H, N> {
fn as_mut(&mut self) -> &mut AuthoritySet<H, N> {
&mut **self.guard.as_mut().expect("only taken on deconstruction; qed")
}
fn set_old(&mut self, old: T) {
fn set_old(&mut self, old: AuthoritySet<H, N>) {
if self.old.is_none() {
// ignore "newer" old changes.
self.old = Some(old);
}
}
fn consume(mut self) -> Option<(T, RwLockWriteGuard<'a, T>)> {
fn consume(
mut self,
) -> Option<(AuthoritySet<H, N>, SharedDataLocked<'a, AuthoritySet<H, N>>)> {
if let Some(old) = self.old.take() {
Some((old, self.guard.take().expect("only taken on deconstruction; qed")))
Some((
old,
self.guard.take().expect("only taken on deconstruction; qed"),
))
} else {
None
}
}
}
impl<'a, T: 'a> Drop for InnerGuard<'a, T> {
impl<'a, H, N> Drop for InnerGuard<'a, H, N> {
fn drop(&mut self) {
if let (Some(mut guard), Some(old)) = (self.guard.take(), self.old.take()) {
*guard = old;
@@ -315,7 +320,7 @@ where
let is_descendent_of = is_descendent_of(&*self.inner, Some((hash, parent_hash)));
let mut guard = InnerGuard {
guard: Some(self.authority_set.inner().write()),
guard: Some(self.authority_set.inner_locked()),
old: None,
};
@@ -413,10 +418,13 @@ where
);
}
let just_in_case = just_in_case.map(|(o, i)| (o, i.release_mutex()));
Ok(PendingSetChanges { just_in_case, applied_changes, do_pause })
}
}
#[async_trait::async_trait]
impl<BE, Block: BlockT, Client, SC> BlockImport<Block>
for GrandpaBlockImport<BE, Block, Client, SC> where
NumberFor<Block>: finality_grandpa::BlockNumberOps,
@@ -425,11 +433,13 @@ impl<BE, Block: BlockT, Client, SC> BlockImport<Block>
Client: crate::ClientForGrandpa<Block, BE>,
for<'a> &'a Client:
BlockImport<Block, Error = ConsensusError, Transaction = TransactionFor<Client, Block>>,
TransactionFor<Client, Block>: Send + 'static,
SC: Send,
{
type Error = ConsensusError;
type Transaction = TransactionFor<Client, Block>;
fn import_block(
async fn import_block(
&mut self,
mut block: BlockImportParams<Block, Self::Transaction>,
new_cache: HashMap<well_known_cache_keys::Id, Vec<u8>>,
@@ -452,7 +462,7 @@ impl<BE, Block: BlockT, Client, SC> BlockImport<Block>
// we don't want to finalize on `inner.import_block`
let mut justifications = block.justifications.take();
let import_result = (&*self.inner).import_block(block, new_cache);
let import_result = (&*self.inner).import_block(block, new_cache).await;
let mut imported_aux = {
match import_result {
@@ -556,11 +566,11 @@ impl<BE, Block: BlockT, Client, SC> BlockImport<Block>
Ok(ImportResult::Imported(imported_aux))
}
fn check_block(
async fn check_block(
&mut self,
block: BlockCheckParams<Block>,
) -> Result<ImportResult, Self::Error> {
self.inner.check_block(block)
self.inner.check_block(block).await
}
}
@@ -580,8 +590,7 @@ impl<Backend, Block: BlockT, Client, SC> GrandpaBlockImport<Backend, Block, Clie
.iter()
.find(|(set_id, _)| *set_id == authority_set.set_id())
{
let mut authority_set = authority_set.inner().write();
authority_set.current_authorities = change.next_authorities.clone();
authority_set.inner().current_authorities = change.next_authorities.clone();
}
// index authority set hard forks by block hash so that they can be used
@@ -596,7 +605,7 @@ impl<Backend, Block: BlockT, Client, SC> GrandpaBlockImport<Backend, Block, Clie
// any *pending* standard changes, checking by the block hash at which
// they were announced.
{
let mut authority_set = authority_set.inner().write();
let mut authority_set = authority_set.inner();
authority_set.pending_standard_changes = authority_set
.pending_standard_changes
+1 -1
View File
@@ -1019,7 +1019,7 @@ where
// set changed (not where the signal happened!) as the base.
let set_state = VoterSetState::live(
new.set_id,
&*self.env.authority_set.inner().read(),
&*self.env.authority_set.inner(),
(new.canon_hash, new.canon_number),
);
@@ -326,7 +326,7 @@ where
// set changed (not where the signal happened!) as the base.
let set_state = VoterSetState::live(
new.set_id,
&*self.persistent_data.authority_set.inner().read(),
&*self.persistent_data.authority_set.inner(),
(new.canon_hash, new.canon_number),
);
+31 -36
View File
@@ -28,9 +28,9 @@ use sc_network_test::{
use sc_network::config::ProtocolConfig;
use parking_lot::{RwLock, Mutex};
use futures_timer::Delay;
use futures::executor::block_on;
use tokio::runtime::{Runtime, Handle};
use sp_keyring::Ed25519Keyring;
use sc_client_api::backend::TransactionFor;
use sp_blockchain::Result;
use sp_api::{ApiRef, ProvideRuntimeApi};
use substrate_test_runtime_client::runtime::BlockNumber;
@@ -43,7 +43,9 @@ use sp_runtime::{Justifications, traits::{Block as BlockT, Header as HeaderT}};
use sp_runtime::generic::{BlockId, DigestItem};
use sp_core::H256;
use sp_keystore::{SyncCryptoStorePtr, SyncCryptoStore};
use sp_finality_grandpa::{GRANDPA_ENGINE_ID, AuthorityList, EquivocationProof, GrandpaApi, OpaqueKeyOwnershipProof};
use sp_finality_grandpa::{
GRANDPA_ENGINE_ID, AuthorityList, EquivocationProof, GrandpaApi, OpaqueKeyOwnershipProof,
};
use authorities::AuthoritySet;
use sc_block_builder::BlockBuilderProvider;
@@ -54,7 +56,13 @@ use sp_application_crypto::key_types::GRANDPA;
type TestLinkHalf =
LinkHalf<Block, PeersFullClient, LongestChain<substrate_test_runtime_client::Backend, Block>>;
type PeerData = Mutex<Option<TestLinkHalf>>;
type GrandpaPeer = Peer<PeerData>;
type GrandpaPeer = Peer<PeerData, GrandpaBlockImport>;
type GrandpaBlockImport = crate::GrandpaBlockImport<
substrate_test_runtime_client::Backend,
Block,
PeersFullClient,
LongestChain<substrate_test_runtime_client::Backend, Block>
>;
struct GrandpaTestNet {
peers: Vec<GrandpaPeer>,
@@ -93,6 +101,7 @@ impl GrandpaTestNet {
impl TestNetFactory for GrandpaTestNet {
type Verifier = PassThroughVerifier;
type PeerData = PeerData;
type BlockImport = GrandpaBlockImport;
/// Create new test network with peers and given config.
fn from_config(_config: &ProtocolConfig) -> Self {
@@ -124,9 +133,9 @@ impl TestNetFactory for GrandpaTestNet {
PassThroughVerifier::new(false) // use non-instant finality.
}
fn make_block_import<Transaction>(&self, client: PeersClient)
fn make_block_import(&self, client: PeersClient)
-> (
BlockImportAdapter<Transaction>,
BlockImportAdapter<Self::BlockImport>,
Option<BoxJustificationImport<Block>>,
PeerData,
)
@@ -141,7 +150,7 @@ impl TestNetFactory for GrandpaTestNet {
).expect("Could not create block import for fresh peer.");
let justification_import = Box::new(import.clone());
(
BlockImportAdapter::new_full(import),
BlockImportAdapter::new(import),
Some(justification_import),
Mutex::new(Some(link)),
)
@@ -820,11 +829,7 @@ fn allows_reimporting_change_blocks() {
let mut net = GrandpaTestNet::new(api.clone(), 3, 0);
let client = net.peer(0).client().clone();
let (mut block_import, ..) = net.make_block_import::<
TransactionFor<substrate_test_runtime_client::Backend, Block>
>(
client.clone(),
);
let (mut block_import, ..) = net.make_block_import(client.clone());
let full_client = client.as_full().unwrap();
let builder = full_client.new_block_at(&BlockId::Number(0), Default::default(), false).unwrap();
@@ -844,7 +849,7 @@ fn allows_reimporting_change_blocks() {
};
assert_eq!(
block_import.import_block(block(), HashMap::new()).unwrap(),
block_on(block_import.import_block(block(), HashMap::new())).unwrap(),
ImportResult::Imported(ImportedAux {
needs_justification: true,
clear_justification_requests: false,
@@ -855,7 +860,7 @@ fn allows_reimporting_change_blocks() {
);
assert_eq!(
block_import.import_block(block(), HashMap::new()).unwrap(),
block_on(block_import.import_block(block(), HashMap::new())).unwrap(),
ImportResult::AlreadyInChain
);
}
@@ -869,11 +874,7 @@ fn test_bad_justification() {
let mut net = GrandpaTestNet::new(api.clone(), 3, 0);
let client = net.peer(0).client().clone();
let (mut block_import, ..) = net.make_block_import::<
TransactionFor<substrate_test_runtime_client::Backend, Block>
>(
client.clone(),
);
let (mut block_import, ..) = net.make_block_import(client.clone());
let full_client = client.as_full().expect("only full clients are used in test");
let builder = full_client.new_block_at(&BlockId::Number(0), Default::default(), false).unwrap();
@@ -895,7 +896,7 @@ fn test_bad_justification() {
};
assert_eq!(
block_import.import_block(block(), HashMap::new()).unwrap(),
block_on(block_import.import_block(block(), HashMap::new())).unwrap(),
ImportResult::Imported(ImportedAux {
needs_justification: true,
clear_justification_requests: false,
@@ -906,7 +907,7 @@ fn test_bad_justification() {
);
assert_eq!(
block_import.import_block(block(), HashMap::new()).unwrap(),
block_on(block_import.import_block(block(), HashMap::new())).unwrap(),
ImportResult::AlreadyInChain
);
}
@@ -950,9 +951,7 @@ fn voter_persists_its_votes() {
let set_state = {
let bob_client = net.peer(1).client().clone();
let (_, _, link) = net
.make_block_import::<
TransactionFor<substrate_test_runtime_client::Backend, Block>
>(bob_client);
.make_block_import(bob_client);
let LinkHalf { persistent_data, .. } = link.lock().take().unwrap();
let PersistentData { set_state, .. } = persistent_data;
set_state
@@ -1019,9 +1018,7 @@ fn voter_persists_its_votes() {
let alice_client = net.peer(0).client().clone();
let (_block_import, _, link) = net
.make_block_import::<
TransactionFor<substrate_test_runtime_client::Backend, Block>
>(alice_client);
.make_block_import(alice_client);
let link = link.lock().take().unwrap();
let grandpa_params = GrandpaParams {
@@ -1422,7 +1419,7 @@ fn grandpa_environment_respects_voting_rules() {
// the unrestricted environment should just return the best block
assert_eq!(
futures::executor::block_on(unrestricted_env.best_chain_containing(
block_on(unrestricted_env.best_chain_containing(
peer.client().info().finalized_hash
)).unwrap().unwrap().1,
21,
@@ -1431,14 +1428,14 @@ fn grandpa_environment_respects_voting_rules() {
// both the other environments should return block 16, which is 3/4 of the
// way in the unfinalized chain
assert_eq!(
futures::executor::block_on(three_quarters_env.best_chain_containing(
block_on(three_quarters_env.best_chain_containing(
peer.client().info().finalized_hash
)).unwrap().unwrap().1,
16,
);
assert_eq!(
futures::executor::block_on(default_env.best_chain_containing(
block_on(default_env.best_chain_containing(
peer.client().info().finalized_hash
)).unwrap().unwrap().1,
16,
@@ -1449,7 +1446,7 @@ fn grandpa_environment_respects_voting_rules() {
// the 3/4 environment should propose block 21 for voting
assert_eq!(
futures::executor::block_on(three_quarters_env.best_chain_containing(
block_on(three_quarters_env.best_chain_containing(
peer.client().info().finalized_hash
)).unwrap().unwrap().1,
21,
@@ -1458,7 +1455,7 @@ fn grandpa_environment_respects_voting_rules() {
// while the default environment will always still make sure we don't vote
// on the best block (2 behind)
assert_eq!(
futures::executor::block_on(default_env.best_chain_containing(
block_on(default_env.best_chain_containing(
peer.client().info().finalized_hash
)).unwrap().unwrap().1,
19,
@@ -1471,7 +1468,7 @@ fn grandpa_environment_respects_voting_rules() {
// best block, there's a hard rule that we can't cast any votes lower than
// the given base (#21).
assert_eq!(
futures::executor::block_on(default_env.best_chain_containing(
block_on(default_env.best_chain_containing(
peer.client().info().finalized_hash
)).unwrap().unwrap().1,
21,
@@ -1557,9 +1554,7 @@ fn imports_justification_for_regular_blocks_on_import() {
let mut net = GrandpaTestNet::new(api.clone(), 1, 0);
let client = net.peer(0).client().clone();
let (mut block_import, ..) = net.make_block_import::<
TransactionFor<substrate_test_runtime_client::Backend, Block>
>(client.clone());
let (mut block_import, ..) = net.make_block_import(client.clone());
let full_client = client.as_full().expect("only full clients are used in test");
let builder = full_client.new_block_at(&BlockId::Number(0), Default::default(), false).unwrap();
@@ -1607,7 +1602,7 @@ fn imports_justification_for_regular_blocks_on_import() {
import.fork_choice = Some(ForkChoiceStrategy::LongestChain);
assert_eq!(
block_import.import_block(import, HashMap::new()).unwrap(),
block_on(block_import.import_block(import, HashMap::new())).unwrap(),
ImportResult::Imported(ImportedAux {
needs_justification: false,
clear_justification_requests: false,
@@ -372,7 +372,7 @@ mod tests {
.unwrap()
.block;
client.import(BlockOrigin::Own, block).unwrap();
futures::executor::block_on(client.import(BlockOrigin::Own, block)).unwrap();
}
let genesis = client
+3 -1
View File
@@ -47,8 +47,10 @@ fn build_test_full_node(network_config: config::NetworkConfiguration)
#[derive(Clone)]
struct PassThroughVerifier(bool);
#[async_trait::async_trait]
impl<B: BlockT> sp_consensus::import_queue::Verifier<B> for PassThroughVerifier {
fn verify(
async fn verify(
&mut self,
origin: sp_consensus::BlockOrigin,
header: B::Header,
@@ -2016,7 +2016,7 @@ mod test {
let mut new_blocks = |n| {
for _ in 0..n {
let block = client.new_block(Default::default()).unwrap().build().unwrap().block;
client.import(BlockOrigin::Own, block.clone()).unwrap();
block_on(client.import(BlockOrigin::Own, block.clone())).unwrap();
}
let info = client.info();
@@ -2147,7 +2147,7 @@ mod test {
let block = block_builder.build().unwrap().block;
client.import(BlockOrigin::Own, block.clone()).unwrap();
block_on(client.import(BlockOrigin::Own, block.clone())).unwrap();
block
}
@@ -2188,7 +2188,7 @@ mod test {
let block = block_builder.build().unwrap().block;
if import {
client2.import(BlockOrigin::Own, block.clone()).unwrap();
block_on(client2.import(BlockOrigin::Own, block.clone())).unwrap();
}
block
@@ -2213,7 +2213,7 @@ mod test {
send_block_announce(block3_fork.header().clone(), &peer_id2, &mut sync);
// Import and tell sync that we now have the fork.
client.import(BlockOrigin::Own, block3_fork.clone()).unwrap();
block_on(client.import(BlockOrigin::Own, block3_fork.clone())).unwrap();
sync.update_chain_info(&block3_fork.hash(), 3);
let block4 = build_block_at(block3_fork.hash(), false);
@@ -2325,7 +2325,7 @@ mod test {
resp_blocks.into_iter()
.rev()
.for_each(|b| client.import_as_final(BlockOrigin::Own, b).unwrap());
.for_each(|b| block_on(client.import_as_final(BlockOrigin::Own, b)).unwrap());
}
// Let peer2 announce that it finished syncing
@@ -2388,7 +2388,7 @@ mod test {
let mut client = Arc::new(TestClientBuilder::new().build());
let fork_blocks = blocks[..MAX_BLOCKS_TO_LOOK_BACKWARDS as usize * 2]
.into_iter()
.inspect(|b| client.import(BlockOrigin::Own, (*b).clone()).unwrap())
.inspect(|b| block_on(client.import(BlockOrigin::Own, (*b).clone())).unwrap())
.cloned()
.collect::<Vec<_>>();
@@ -2492,7 +2492,7 @@ mod test {
resp_blocks.into_iter()
.rev()
.for_each(|b| client.import(BlockOrigin::Own, b).unwrap());
.for_each(|b| block_on(client.import(BlockOrigin::Own, b)).unwrap());
}
// Request the tip
@@ -47,8 +47,10 @@ fn build_test_full_node(config: config::NetworkConfiguration)
#[derive(Clone)]
struct PassThroughVerifier(bool);
#[async_trait::async_trait]
impl<B: BlockT> sp_consensus::import_queue::Verifier<B> for PassThroughVerifier {
fn verify(
async fn verify(
&mut self,
origin: sp_consensus::BlockOrigin,
header: B::Header,
+1
View File
@@ -34,3 +34,4 @@ substrate-test-runtime = { version = "2.0.0", path = "../../../test-utils/runtim
tempfile = "3.1.0"
sp-tracing = { version = "3.0.0", path = "../../../primitives/tracing" }
sc-service = { version = "0.9.0", default-features = false, features = ["test-helpers"], path = "../../service" }
async-trait = "0.1.42"
@@ -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,
)
+3 -2
View File
@@ -240,6 +240,7 @@ mod tests {
use sp_consensus::BlockOrigin;
use sc_client_api::Backend as _;
use sc_block_builder::BlockBuilderProvider as _;
use futures::executor::block_on;
struct TestNetwork();
@@ -331,7 +332,7 @@ mod tests {
).unwrap();
let block = block_builder.build().unwrap().block;
client.import(BlockOrigin::Own, block).unwrap();
block_on(client.import(BlockOrigin::Own, block)).unwrap();
assert_eq!(value, &offchain_db.get(sp_offchain::STORAGE_PREFIX, &key).unwrap());
@@ -341,7 +342,7 @@ mod tests {
).unwrap();
let block = block_builder.build().unwrap().block;
client.import(BlockOrigin::Own, block).unwrap();
block_on(client.import(BlockOrigin::Own, block)).unwrap();
assert!(offchain_db.get(sp_offchain::STORAGE_PREFIX, &key).is_none());
}
+6 -6
View File
@@ -67,7 +67,7 @@ fn should_return_a_block() {
let block = client.new_block(Default::default()).unwrap().build().unwrap().block;
let block_hash = block.hash();
client.import(BlockOrigin::Own, block).unwrap();
executor::block_on(client.import(BlockOrigin::Own, block)).unwrap();
// Genesis block is not justified
assert_matches!(
@@ -133,7 +133,7 @@ fn should_return_block_hash() {
);
let block = client.new_block(Default::default()).unwrap().build().unwrap().block;
client.import(BlockOrigin::Own, block.clone()).unwrap();
executor::block_on(client.import(BlockOrigin::Own, block.clone())).unwrap();
assert_matches!(
api.block_hash(Some(ListOrValue::Value(0u64.into())).into()),
@@ -167,7 +167,7 @@ fn should_return_finalized_hash() {
// import new block
let block = client.new_block(Default::default()).unwrap().build().unwrap().block;
client.import(BlockOrigin::Own, block).unwrap();
executor::block_on(client.import(BlockOrigin::Own, block)).unwrap();
// no finalization yet
assert_matches!(
api.finalized_head(),
@@ -199,7 +199,7 @@ fn should_notify_about_latest_block() {
));
let block = client.new_block(Default::default()).unwrap().build().unwrap().block;
client.import(BlockOrigin::Own, block).unwrap();
executor::block_on(client.import(BlockOrigin::Own, block)).unwrap();
}
// assert initial head sent.
@@ -229,7 +229,7 @@ fn should_notify_about_best_block() {
));
let block = client.new_block(Default::default()).unwrap().build().unwrap().block;
client.import(BlockOrigin::Own, block).unwrap();
executor::block_on(client.import(BlockOrigin::Own, block)).unwrap();
}
// assert initial head sent.
@@ -259,7 +259,7 @@ fn should_notify_about_finalized_block() {
));
let block = client.new_block(Default::default()).unwrap().build().unwrap().block;
client.import(BlockOrigin::Own, block).unwrap();
executor::block_on(client.import(BlockOrigin::Own, block)).unwrap();
client.finalize_block(BlockId::number(1), None).unwrap();
}
+3 -3
View File
@@ -180,7 +180,7 @@ fn should_notify_about_storage_changes() {
nonce: 0,
}).unwrap();
let block = builder.build().unwrap().block;
client.import(BlockOrigin::Own, block).unwrap();
executor::block_on(client.import(BlockOrigin::Own, block)).unwrap();
}
// assert notification sent to transport
@@ -222,7 +222,7 @@ fn should_send_initial_storage_changes_and_notifications() {
nonce: 0,
}).unwrap();
let block = builder.build().unwrap().block;
client.import(BlockOrigin::Own, block).unwrap();
executor::block_on(client.import(BlockOrigin::Own, block)).unwrap();
}
// assert initial values sent to transport
@@ -258,7 +258,7 @@ fn should_query_storage() {
builder.push_storage_change(vec![5], Some(vec![nonce as u8])).unwrap();
let block = builder.build().unwrap().block;
let hash = block.header.hash();
client.import(BlockOrigin::Own, block).unwrap();
executor::block_on(client.import(BlockOrigin::Own, block)).unwrap();
hash
};
let block1_hash = add_block(0);
+1
View File
@@ -78,6 +78,7 @@ sp-tracing = { version = "3.0.0", path = "../../primitives/tracing" }
tracing = "0.1.25"
tracing-futures = { version = "0.2.4" }
parity-util-mem = { version = "0.9.0", default-features = false, features = ["primitive-types"] }
async-trait = "0.1.42"
[target.'cfg(not(target_os = "unknown"))'.dependencies]
tempfile = "3.1.0"
+12 -6
View File
@@ -1698,6 +1698,7 @@ impl<B, E, Block, RA> CallApiAt<Block> for Client<B, E, Block, RA> where
/// NOTE: only use this implementation when you are sure there are NO consensus-level BlockImport
/// objects. Otherwise, importing blocks directly into the client would be bypassing
/// important verification work.
#[async_trait::async_trait]
impl<B, E, Block, RA> sp_consensus::BlockImport<Block> for &Client<B, E, Block, RA> where
B: backend::Backend<Block>,
E: CallExecutor<Block> + Send + Sync,
@@ -1705,6 +1706,8 @@ impl<B, E, Block, RA> sp_consensus::BlockImport<Block> for &Client<B, E, Block,
Client<B, E, Block, RA>: ProvideRuntimeApi<Block>,
<Client<B, E, Block, RA> as ProvideRuntimeApi<Block>>::Api: CoreApi<Block> +
ApiExt<Block, StateBackend = B::State>,
RA: Sync + Send,
backend::TransactionFor<B, Block>: Send + 'static,
{
type Error = ConsensusError;
type Transaction = backend::TransactionFor<B, Block>;
@@ -1718,7 +1721,7 @@ impl<B, E, Block, RA> sp_consensus::BlockImport<Block> for &Client<B, E, Block,
///
/// If you are not sure that there are no BlockImport objects provided by the consensus
/// algorithm, don't use this function.
fn import_block(
async fn import_block(
&mut self,
mut import_block: BlockImportParams<Block, backend::TransactionFor<B, Block>>,
new_cache: HashMap<CacheKeyId, Vec<u8>>,
@@ -1742,7 +1745,7 @@ impl<B, E, Block, RA> sp_consensus::BlockImport<Block> for &Client<B, E, Block,
}
/// Check block preconditions.
fn check_block(
async fn check_block(
&mut self,
block: BlockCheckParams<Block>,
) -> Result<ImportResult, Self::Error> {
@@ -1798,6 +1801,7 @@ impl<B, E, Block, RA> sp_consensus::BlockImport<Block> for &Client<B, E, Block,
}
}
#[async_trait::async_trait]
impl<B, E, Block, RA> sp_consensus::BlockImport<Block> for Client<B, E, Block, RA> where
B: backend::Backend<Block>,
E: CallExecutor<Block> + Send + Sync,
@@ -1805,23 +1809,25 @@ impl<B, E, Block, RA> sp_consensus::BlockImport<Block> for Client<B, E, Block, R
Self: ProvideRuntimeApi<Block>,
<Self as ProvideRuntimeApi<Block>>::Api: CoreApi<Block> +
ApiExt<Block, StateBackend = B::State>,
RA: Sync + Send,
backend::TransactionFor<B, Block>: Send + 'static,
{
type Error = ConsensusError;
type Transaction = backend::TransactionFor<B, Block>;
fn import_block(
async fn import_block(
&mut self,
import_block: BlockImportParams<Block, Self::Transaction>,
new_cache: HashMap<CacheKeyId, Vec<u8>>,
) -> Result<ImportResult, Self::Error> {
(&*self).import_block(import_block, new_cache)
(&*self).import_block(import_block, new_cache).await
}
fn check_block(
async fn check_block(
&mut self,
block: BlockCheckParams<Block>,
) -> Result<ImportResult, Self::Error> {
(&*self).check_block(block)
(&*self).check_block(block).await
}
}
+1 -1
View File
@@ -28,7 +28,7 @@ sp-trie = { version = "3.0.0", path = "../../../primitives/trie" }
sp-storage = { version = "3.0.0", path = "../../../primitives/storage" }
sc-client-db = { version = "0.9.0", default-features = false, path = "../../db" }
futures = { version = "0.3.1", features = ["compat"] }
sc-service = { version = "0.9.0", default-features = false, features = ["test-helpers"], path = "../../service" }
sc-service = { version = "0.9.0", features = ["test-helpers"], path = "../../service" }
sc-network = { version = "0.9.0", path = "../../network" }
sp-consensus = { version = "0.9.0", path = "../../../primitives/consensus/common" }
sp-runtime = { version = "3.0.0", path = "../../../primitives/runtime" }
@@ -375,11 +375,11 @@ fn execution_proof_is_generated_and_checked() {
for i in 1u32..3u32 {
let mut digest = Digest::default();
digest.push(sp_runtime::generic::DigestItem::Other::<H256>(i.to_le_bytes().to_vec()));
remote_client.import_justified(
futures::executor::block_on(remote_client.import_justified(
BlockOrigin::Own,
remote_client.new_block(digest).unwrap().build().unwrap().block,
Justifications::from((*b"TEST", Default::default())),
).unwrap();
)).unwrap();
}
// check method that doesn't requires environment
@@ -540,7 +540,7 @@ fn prepare_for_header_proof_check(insert_cht: bool) -> (TestChecker, Hash, Heade
let mut local_headers_hashes = Vec::new();
for i in 0..4 {
let block = remote_client.new_block(Default::default()).unwrap().build().unwrap().block;
remote_client.import(BlockOrigin::Own, block).unwrap();
futures::executor::block_on(remote_client.import(BlockOrigin::Own, block)).unwrap();
local_headers_hashes.push(
remote_client.block_hash(i + 1)
.map_err(|_| ClientError::Backend("TestError".into()))
+170 -103
View File
@@ -54,6 +54,7 @@ use sp_storage::StorageKey;
use sp_trie::{TrieConfiguration, trie_types::Layout};
use sp_runtime::{generic::BlockId, DigestItem, Justifications};
use hex_literal::hex;
use futures::executor::block_on;
mod light;
mod db;
@@ -108,7 +109,7 @@ pub fn prepare_client_with_key_changes() -> (
}).unwrap();
}
let block = builder.build().unwrap().block;
remote_client.import(BlockOrigin::Own, block).unwrap();
block_on(remote_client.import(BlockOrigin::Own, block)).unwrap();
let header = remote_client.header(&BlockId::Number(i as u64 + 1)).unwrap().unwrap();
let trie_root = header.digest().log(DigestItem::as_changes_trie_root)
@@ -363,7 +364,7 @@ fn block_builder_works_with_no_transactions() {
let block = client.new_block(Default::default()).unwrap().build().unwrap().block;
client.import(BlockOrigin::Own, block).unwrap();
block_on(client.import(BlockOrigin::Own, block)).unwrap();
assert_eq!(client.chain_info().best_number, 1);
}
@@ -382,7 +383,7 @@ fn block_builder_works_with_transactions() {
}).unwrap();
let block = builder.build().unwrap().block;
client.import(BlockOrigin::Own, block).unwrap();
block_on(client.import(BlockOrigin::Own, block)).unwrap();
assert_eq!(client.chain_info().best_number, 1);
assert_ne!(
@@ -428,7 +429,7 @@ fn block_builder_does_not_include_invalid() {
);
let block = builder.build().unwrap().block;
client.import(BlockOrigin::Own, block).unwrap();
block_on(client.import(BlockOrigin::Own, block)).unwrap();
assert_eq!(client.chain_info().best_number, 1);
assert_ne!(
@@ -476,11 +477,11 @@ fn uncles_with_only_ancestors() {
// G -> A1
let a1 = client.new_block(Default::default()).unwrap().build().unwrap().block;
client.import(BlockOrigin::Own, a1.clone()).unwrap();
block_on(client.import(BlockOrigin::Own, a1.clone())).unwrap();
// A1 -> A2
let a2 = client.new_block(Default::default()).unwrap().build().unwrap().block;
client.import(BlockOrigin::Own, a2.clone()).unwrap();
block_on(client.import(BlockOrigin::Own, a2.clone())).unwrap();
let v: Vec<H256> = Vec::new();
assert_eq!(v, client.uncles(a2.hash(), 3).unwrap());
}
@@ -496,7 +497,7 @@ fn uncles_with_multiple_forks() {
// G -> A1
let a1 = client.new_block(Default::default()).unwrap().build().unwrap().block;
client.import(BlockOrigin::Own, a1.clone()).unwrap();
block_on(client.import(BlockOrigin::Own, a1.clone())).unwrap();
// A1 -> A2
let a2 = client.new_block_at(
@@ -504,7 +505,7 @@ fn uncles_with_multiple_forks() {
Default::default(),
false,
).unwrap().build().unwrap().block;
client.import(BlockOrigin::Own, a2.clone()).unwrap();
block_on(client.import(BlockOrigin::Own, a2.clone())).unwrap();
// A2 -> A3
let a3 = client.new_block_at(
@@ -512,7 +513,7 @@ fn uncles_with_multiple_forks() {
Default::default(),
false,
).unwrap().build().unwrap().block;
client.import(BlockOrigin::Own, a3.clone()).unwrap();
block_on(client.import(BlockOrigin::Own, a3.clone())).unwrap();
// A3 -> A4
let a4 = client.new_block_at(
@@ -520,7 +521,7 @@ fn uncles_with_multiple_forks() {
Default::default(),
false,
).unwrap().build().unwrap().block;
client.import(BlockOrigin::Own, a4.clone()).unwrap();
block_on(client.import(BlockOrigin::Own, a4.clone())).unwrap();
// A4 -> A5
let a5 = client.new_block_at(
@@ -528,7 +529,7 @@ fn uncles_with_multiple_forks() {
Default::default(),
false,
).unwrap().build().unwrap().block;
client.import(BlockOrigin::Own, a5.clone()).unwrap();
block_on(client.import(BlockOrigin::Own, a5.clone())).unwrap();
// A1 -> B2
let mut builder = client.new_block_at(
@@ -544,7 +545,7 @@ fn uncles_with_multiple_forks() {
nonce: 0,
}).unwrap();
let b2 = builder.build().unwrap().block;
client.import(BlockOrigin::Own, b2.clone()).unwrap();
block_on(client.import(BlockOrigin::Own, b2.clone())).unwrap();
// B2 -> B3
let b3 = client.new_block_at(
@@ -552,7 +553,7 @@ fn uncles_with_multiple_forks() {
Default::default(),
false,
).unwrap().build().unwrap().block;
client.import(BlockOrigin::Own, b3.clone()).unwrap();
block_on(client.import(BlockOrigin::Own, b3.clone())).unwrap();
// B3 -> B4
let b4 = client.new_block_at(
@@ -560,7 +561,7 @@ fn uncles_with_multiple_forks() {
Default::default(),
false,
).unwrap().build().unwrap().block;
client.import(BlockOrigin::Own, b4.clone()).unwrap();
block_on(client.import(BlockOrigin::Own, b4.clone())).unwrap();
// // B2 -> C3
let mut builder = client.new_block_at(
@@ -576,7 +577,7 @@ fn uncles_with_multiple_forks() {
nonce: 1,
}).unwrap();
let c3 = builder.build().unwrap().block;
client.import(BlockOrigin::Own, c3.clone()).unwrap();
block_on(client.import(BlockOrigin::Own, c3.clone())).unwrap();
// A1 -> D2
let mut builder = client.new_block_at(
@@ -592,7 +593,7 @@ fn uncles_with_multiple_forks() {
nonce: 0,
}).unwrap();
let d2 = builder.build().unwrap().block;
client.import(BlockOrigin::Own, d2.clone()).unwrap();
block_on(client.import(BlockOrigin::Own, d2.clone())).unwrap();
let genesis_hash = client.chain_info().genesis_hash;
@@ -624,11 +625,11 @@ fn best_containing_on_longest_chain_with_single_chain_3_blocks() {
// G -> A1
let a1 = client.new_block(Default::default()).unwrap().build().unwrap().block;
client.import(BlockOrigin::Own, a1.clone()).unwrap();
block_on(client.import(BlockOrigin::Own, a1.clone())).unwrap();
// A1 -> A2
let a2 = client.new_block(Default::default()).unwrap().build().unwrap().block;
client.import(BlockOrigin::Own, a2.clone()).unwrap();
block_on(client.import(BlockOrigin::Own, a2.clone())).unwrap();
let genesis_hash = client.chain_info().genesis_hash;
@@ -648,7 +649,7 @@ fn best_containing_on_longest_chain_with_multiple_forks() {
// G -> A1
let a1 = client.new_block(Default::default()).unwrap().build().unwrap().block;
client.import(BlockOrigin::Own, a1.clone()).unwrap();
block_on(client.import(BlockOrigin::Own, a1.clone())).unwrap();
// A1 -> A2
let a2 = client.new_block_at(
@@ -656,7 +657,7 @@ fn best_containing_on_longest_chain_with_multiple_forks() {
Default::default(),
false,
).unwrap().build().unwrap().block;
client.import(BlockOrigin::Own, a2.clone()).unwrap();
block_on(client.import(BlockOrigin::Own, a2.clone())).unwrap();
// A2 -> A3
let a3 = client.new_block_at(
@@ -664,7 +665,7 @@ fn best_containing_on_longest_chain_with_multiple_forks() {
Default::default(),
false,
).unwrap().build().unwrap().block;
client.import(BlockOrigin::Own, a3.clone()).unwrap();
block_on(client.import(BlockOrigin::Own, a3.clone())).unwrap();
// A3 -> A4
let a4 = client.new_block_at(
@@ -672,7 +673,7 @@ fn best_containing_on_longest_chain_with_multiple_forks() {
Default::default(),
false,
).unwrap().build().unwrap().block;
client.import(BlockOrigin::Own, a4.clone()).unwrap();
block_on(client.import(BlockOrigin::Own, a4.clone())).unwrap();
// A4 -> A5
let a5 = client.new_block_at(
@@ -680,7 +681,7 @@ fn best_containing_on_longest_chain_with_multiple_forks() {
Default::default(),
false,
).unwrap().build().unwrap().block;
client.import(BlockOrigin::Own, a5.clone()).unwrap();
block_on(client.import(BlockOrigin::Own, a5.clone())).unwrap();
// A1 -> B2
let mut builder = client.new_block_at(
@@ -696,7 +697,7 @@ fn best_containing_on_longest_chain_with_multiple_forks() {
nonce: 0,
}).unwrap();
let b2 = builder.build().unwrap().block;
client.import(BlockOrigin::Own, b2.clone()).unwrap();
block_on(client.import(BlockOrigin::Own, b2.clone())).unwrap();
// B2 -> B3
let b3 = client.new_block_at(
@@ -704,7 +705,7 @@ fn best_containing_on_longest_chain_with_multiple_forks() {
Default::default(),
false,
).unwrap().build().unwrap().block;
client.import(BlockOrigin::Own, b3.clone()).unwrap();
block_on(client.import(BlockOrigin::Own, b3.clone())).unwrap();
// B3 -> B4
let b4 = client.new_block_at(
@@ -712,7 +713,7 @@ fn best_containing_on_longest_chain_with_multiple_forks() {
Default::default(),
false,
).unwrap().build().unwrap().block;
client.import(BlockOrigin::Own, b4.clone()).unwrap();
block_on(client.import(BlockOrigin::Own, b4.clone())).unwrap();
// // B2 -> C3
let mut builder = client.new_block_at(
@@ -728,7 +729,7 @@ fn best_containing_on_longest_chain_with_multiple_forks() {
nonce: 1,
}).unwrap();
let c3 = builder.build().unwrap().block;
client.import(BlockOrigin::Own, c3.clone()).unwrap();
block_on(client.import(BlockOrigin::Own, c3.clone())).unwrap();
// A1 -> D2
let mut builder = client.new_block_at(
@@ -744,7 +745,7 @@ fn best_containing_on_longest_chain_with_multiple_forks() {
nonce: 0,
}).unwrap();
let d2 = builder.build().unwrap().block;
client.import(BlockOrigin::Own, d2.clone()).unwrap();
block_on(client.import(BlockOrigin::Own, d2.clone())).unwrap();
assert_eq!(client.chain_info().best_hash, a5.hash());
@@ -952,11 +953,15 @@ fn best_containing_on_longest_chain_with_multiple_forks() {
assert_eq!(None, longest_chain_select.finality_target(
b4.hash(), Some(0)).unwrap());
assert_eq!(None, longest_chain_select.finality_target(
c3.hash().clone(), Some(0)).unwrap());
assert_eq!(
None,
longest_chain_select.finality_target(c3.hash().clone(), Some(0)).unwrap(),
);
assert_eq!(None, longest_chain_select.finality_target(
d2.hash().clone(), Some(0)).unwrap());
assert_eq!(
None,
longest_chain_select.finality_target(d2.hash().clone(), Some(0)).unwrap(),
);
}
#[test]
@@ -968,15 +973,18 @@ fn best_containing_on_longest_chain_with_max_depth_higher_than_best() {
// G -> A1
let a1 = client.new_block(Default::default()).unwrap().build().unwrap().block;
client.import(BlockOrigin::Own, a1.clone()).unwrap();
block_on(client.import(BlockOrigin::Own, a1.clone())).unwrap();
// A1 -> A2
let a2 = client.new_block(Default::default()).unwrap().build().unwrap().block;
client.import(BlockOrigin::Own, a2.clone()).unwrap();
block_on(client.import(BlockOrigin::Own, a2.clone())).unwrap();
let genesis_hash = client.chain_info().genesis_hash;
assert_eq!(a2.hash(), longest_chain_select.finality_target(genesis_hash, Some(10)).unwrap().unwrap());
assert_eq!(
a2.hash(),
longest_chain_select.finality_target(genesis_hash, Some(10)).unwrap().unwrap(),
);
}
#[test]
@@ -1008,7 +1016,7 @@ fn import_with_justification() {
// G -> A1
let a1 = client.new_block(Default::default()).unwrap().build().unwrap().block;
client.import(BlockOrigin::Own, a1.clone()).unwrap();
block_on(client.import(BlockOrigin::Own, a1.clone())).unwrap();
// A1 -> A2
let a2 = client.new_block_at(
@@ -1016,7 +1024,7 @@ fn import_with_justification() {
Default::default(),
false,
).unwrap().build().unwrap().block;
client.import(BlockOrigin::Own, a2.clone()).unwrap();
block_on(client.import(BlockOrigin::Own, a2.clone())).unwrap();
// A2 -> A3
let justification = Justifications::from((TEST_ENGINE_ID, vec![1, 2, 3]));
@@ -1025,7 +1033,7 @@ fn import_with_justification() {
Default::default(),
false,
).unwrap().build().unwrap().block;
client.import_justified(BlockOrigin::Own, a3.clone(), justification.clone()).unwrap();
block_on(client.import_justified(BlockOrigin::Own, a3.clone(), justification.clone())).unwrap();
assert_eq!(
client.chain_info().finalized_hash,
@@ -1060,14 +1068,14 @@ fn importing_diverged_finalized_block_should_trigger_reorg() {
Default::default(),
false,
).unwrap().build().unwrap().block;
client.import(BlockOrigin::Own, a1.clone()).unwrap();
block_on(client.import(BlockOrigin::Own, a1.clone())).unwrap();
let a2 = client.new_block_at(
&BlockId::Hash(a1.hash()),
Default::default(),
false,
).unwrap().build().unwrap().block;
client.import(BlockOrigin::Own, a2.clone()).unwrap();
block_on(client.import(BlockOrigin::Own, a2.clone())).unwrap();
let mut b1 = client.new_block_at(
&BlockId::Number(0),
@@ -1092,7 +1100,7 @@ fn importing_diverged_finalized_block_should_trigger_reorg() {
// importing B1 as finalized should trigger a re-org and set it as new best
let justification = Justifications::from((TEST_ENGINE_ID, vec![1, 2, 3]));
client.import_justified(BlockOrigin::Own, b1.clone(), justification).unwrap();
block_on(client.import_justified(BlockOrigin::Own, b1.clone(), justification)).unwrap();
assert_eq!(
client.chain_info().best_hash,
@@ -1117,14 +1125,14 @@ fn finalizing_diverged_block_should_trigger_reorg() {
Default::default(),
false,
).unwrap().build().unwrap().block;
client.import(BlockOrigin::Own, a1.clone()).unwrap();
block_on(client.import(BlockOrigin::Own, a1.clone())).unwrap();
let a2 = client.new_block_at(
&BlockId::Hash(a1.hash()),
Default::default(),
false,
).unwrap().build().unwrap().block;
client.import(BlockOrigin::Own, a2.clone()).unwrap();
block_on(client.import(BlockOrigin::Own, a2.clone())).unwrap();
let mut b1 = client.new_block_at(
&BlockId::Number(0),
@@ -1139,14 +1147,14 @@ fn finalizing_diverged_block_should_trigger_reorg() {
nonce: 0,
}).unwrap();
let b1 = b1.build().unwrap().block;
client.import(BlockOrigin::Own, b1.clone()).unwrap();
block_on(client.import(BlockOrigin::Own, b1.clone())).unwrap();
let b2 = client.new_block_at(
&BlockId::Hash(b1.hash()),
Default::default(),
false,
).unwrap().build().unwrap().block;
client.import(BlockOrigin::Own, b2.clone()).unwrap();
block_on(client.import(BlockOrigin::Own, b2.clone())).unwrap();
// A2 is the current best since it's the longest chain
assert_eq!(
@@ -1184,7 +1192,7 @@ fn finalizing_diverged_block_should_trigger_reorg() {
Default::default(),
false,
).unwrap().build().unwrap().block;
client.import(BlockOrigin::Own, b3.clone()).unwrap();
block_on(client.import(BlockOrigin::Own, b3.clone())).unwrap();
assert_eq!(
client.chain_info().best_hash,
@@ -1227,7 +1235,7 @@ fn state_reverted_on_reorg() {
nonce: 0,
}).unwrap();
let a1 = a1.build().unwrap().block;
client.import(BlockOrigin::Own, a1.clone()).unwrap();
block_on(client.import(BlockOrigin::Own, a1.clone())).unwrap();
let mut b1 = client.new_block_at(
&BlockId::Number(0),
@@ -1242,7 +1250,7 @@ fn state_reverted_on_reorg() {
}).unwrap();
let b1 = b1.build().unwrap().block;
// Reorg to B1
client.import_as_best(BlockOrigin::Own, b1.clone()).unwrap();
block_on(client.import_as_best(BlockOrigin::Own, b1.clone())).unwrap();
assert_eq!(950, current_balance(&client));
let mut a2 = client.new_block_at(
@@ -1258,7 +1266,7 @@ fn state_reverted_on_reorg() {
}).unwrap();
let a2 = a2.build().unwrap().block;
// Re-org to A2
client.import_as_best(BlockOrigin::Own, a2).unwrap();
block_on(client.import_as_best(BlockOrigin::Own, a2)).unwrap();
assert_eq!(980, current_balance(&client));
}
@@ -1297,14 +1305,14 @@ fn doesnt_import_blocks_that_revert_finality() {
Default::default(),
false,
).unwrap().build().unwrap().block;
client.import(BlockOrigin::Own, a1.clone()).unwrap();
block_on(client.import(BlockOrigin::Own, a1.clone())).unwrap();
let a2 = client.new_block_at(
&BlockId::Hash(a1.hash()),
Default::default(),
false,
).unwrap().build().unwrap().block;
client.import(BlockOrigin::Own, a2.clone()).unwrap();
block_on(client.import(BlockOrigin::Own, a2.clone())).unwrap();
let mut b1 = client.new_block_at(&BlockId::Number(0), Default::default(), false).unwrap();
@@ -1316,11 +1324,11 @@ fn doesnt_import_blocks_that_revert_finality() {
nonce: 0,
}).unwrap();
let b1 = b1.build().unwrap().block;
client.import(BlockOrigin::Own, b1.clone()).unwrap();
block_on(client.import(BlockOrigin::Own, b1.clone())).unwrap();
let b2 = client.new_block_at(&BlockId::Hash(b1.hash()), Default::default(), false)
.unwrap().build().unwrap().block;
client.import(BlockOrigin::Own, b2.clone()).unwrap();
block_on(client.import(BlockOrigin::Own, b2.clone())).unwrap();
// prepare B3 before we finalize A2, because otherwise we won't be able to
// read changes trie configuration after A2 is finalized
@@ -1331,7 +1339,7 @@ fn doesnt_import_blocks_that_revert_finality() {
// B3 at the same height but that doesn't include it
ClientExt::finalize_block(&client, BlockId::Hash(a2.hash()), None).unwrap();
let import_err = client.import(BlockOrigin::Own, b3).err().unwrap();
let import_err = block_on(client.import(BlockOrigin::Own, b3)).err().unwrap();
let expected_err = ConsensusError::ClientImport(
sp_blockchain::Error::RuntimeApiError(
sp_api::ApiError::Application(Box::new(sp_blockchain::Error::NotInFinalizedChain))
@@ -1356,7 +1364,7 @@ fn doesnt_import_blocks_that_revert_finality() {
}).unwrap();
let c1 = c1.build().unwrap().block;
let import_err = client.import(BlockOrigin::Own, c1).err().unwrap();
let import_err = block_on(client.import(BlockOrigin::Own, c1)).err().unwrap();
let expected_err = ConsensusError::ClientImport(
sp_blockchain::Error::NotInFinalizedChain.to_string()
);
@@ -1367,7 +1375,6 @@ fn doesnt_import_blocks_that_revert_finality() {
);
}
#[test]
fn respects_block_rules() {
fn run_test(
@@ -1396,7 +1403,7 @@ fn respects_block_rules() {
allow_missing_state: false,
import_existing: false,
};
assert_eq!(client.check_block(params).unwrap(), ImportResult::imported(false));
assert_eq!(block_on(client.check_block(params)).unwrap(), ImportResult::imported(false));
// this is 0x0d6d6612a10485370d9e085aeea7ec427fb3f34d961c6a816cdbe5cde2278864
let mut block_not_ok = client.new_block_at(&BlockId::Number(0), Default::default(), false)
@@ -1414,11 +1421,11 @@ fn respects_block_rules() {
if record_only {
known_bad.insert(block_not_ok.hash());
} else {
assert_eq!(client.check_block(params).unwrap(), ImportResult::KnownBad);
assert_eq!(block_on(client.check_block(params)).unwrap(), ImportResult::KnownBad);
}
// Now going to the fork
client.import_as_final(BlockOrigin::Own, block_ok).unwrap();
block_on(client.import_as_final(BlockOrigin::Own, block_ok)).unwrap();
// And check good fork
let mut block_ok = client.new_block_at(&BlockId::Number(1), Default::default(), false)
@@ -1436,7 +1443,7 @@ fn respects_block_rules() {
if record_only {
fork_rules.push((1, block_ok.hash().clone()));
}
assert_eq!(client.check_block(params).unwrap(), ImportResult::imported(false));
assert_eq!(block_on(client.check_block(params)).unwrap(), ImportResult::imported(false));
// And now try bad fork
let mut block_not_ok = client.new_block_at(&BlockId::Number(1), Default::default(), false)
@@ -1453,7 +1460,7 @@ fn respects_block_rules() {
};
if !record_only {
assert_eq!(client.check_block(params).unwrap(), ImportResult::KnownBad);
assert_eq!(block_on(client.check_block(params)).unwrap(), ImportResult::KnownBad);
}
}
@@ -1491,8 +1498,11 @@ fn returns_status_for_pruned_blocks() {
let mut client = TestClientBuilder::with_backend(backend).build();
let a1 = client.new_block_at(&BlockId::Number(0), Default::default(), false)
.unwrap().build().unwrap().block;
let a1 = client.new_block_at(
&BlockId::Number(0),
Default::default(),
false,
).unwrap().build().unwrap().block;
let mut b1 = client.new_block_at(&BlockId::Number(0), Default::default(), false).unwrap();
@@ -1513,17 +1523,32 @@ fn returns_status_for_pruned_blocks() {
import_existing: false,
};
assert_eq!(client.check_block(check_block_a1.clone()).unwrap(), ImportResult::imported(false));
assert_eq!(client.block_status(&BlockId::hash(check_block_a1.hash)).unwrap(), BlockStatus::Unknown);
assert_eq!(
block_on(client.check_block(check_block_a1.clone())).unwrap(),
ImportResult::imported(false),
);
assert_eq!(
client.block_status(&BlockId::hash(check_block_a1.hash)).unwrap(),
BlockStatus::Unknown,
);
client.import_as_final(BlockOrigin::Own, a1.clone()).unwrap();
block_on(client.import_as_final(BlockOrigin::Own, a1.clone())).unwrap();
assert_eq!(client.check_block(check_block_a1.clone()).unwrap(), ImportResult::AlreadyInChain);
assert_eq!(client.block_status(&BlockId::hash(check_block_a1.hash)).unwrap(), BlockStatus::InChainWithState);
assert_eq!(
block_on(client.check_block(check_block_a1.clone())).unwrap(),
ImportResult::AlreadyInChain,
);
assert_eq!(
client.block_status(&BlockId::hash(check_block_a1.hash)).unwrap(),
BlockStatus::InChainWithState,
);
let a2 = client.new_block_at(&BlockId::Hash(a1.hash()), Default::default(), false)
.unwrap().build().unwrap().block;
client.import_as_final(BlockOrigin::Own, a2.clone()).unwrap();
let a2 = client.new_block_at(
&BlockId::Hash(a1.hash()),
Default::default(),
false,
).unwrap().build().unwrap().block;
block_on(client.import_as_final(BlockOrigin::Own, a2.clone())).unwrap();
let check_block_a2 = BlockCheckParams {
hash: a2.hash().clone(),
@@ -1533,15 +1558,30 @@ fn returns_status_for_pruned_blocks() {
import_existing: false,
};
assert_eq!(client.check_block(check_block_a1.clone()).unwrap(), ImportResult::AlreadyInChain);
assert_eq!(client.block_status(&BlockId::hash(check_block_a1.hash)).unwrap(), BlockStatus::InChainPruned);
assert_eq!(client.check_block(check_block_a2.clone()).unwrap(), ImportResult::AlreadyInChain);
assert_eq!(client.block_status(&BlockId::hash(check_block_a2.hash)).unwrap(), BlockStatus::InChainWithState);
assert_eq!(
block_on(client.check_block(check_block_a1.clone())).unwrap(),
ImportResult::AlreadyInChain,
);
assert_eq!(
client.block_status(&BlockId::hash(check_block_a1.hash)).unwrap(),
BlockStatus::InChainPruned,
);
assert_eq!(
block_on(client.check_block(check_block_a2.clone())).unwrap(),
ImportResult::AlreadyInChain,
);
assert_eq!(
client.block_status(&BlockId::hash(check_block_a2.hash)).unwrap(),
BlockStatus::InChainWithState,
);
let a3 = client.new_block_at(&BlockId::Hash(a2.hash()), Default::default(), false)
.unwrap().build().unwrap().block;
let a3 = client.new_block_at(
&BlockId::Hash(a2.hash()),
Default::default(),
false,
).unwrap().build().unwrap().block;
client.import_as_final(BlockOrigin::Own, a3.clone()).unwrap();
block_on(client.import_as_final(BlockOrigin::Own, a3.clone())).unwrap();
let check_block_a3 = BlockCheckParams {
hash: a3.hash().clone(),
number: 2,
@@ -1551,12 +1591,30 @@ fn returns_status_for_pruned_blocks() {
};
// a1 and a2 are both pruned at this point
assert_eq!(client.check_block(check_block_a1.clone()).unwrap(), ImportResult::AlreadyInChain);
assert_eq!(client.block_status(&BlockId::hash(check_block_a1.hash)).unwrap(), BlockStatus::InChainPruned);
assert_eq!(client.check_block(check_block_a2.clone()).unwrap(), ImportResult::AlreadyInChain);
assert_eq!(client.block_status(&BlockId::hash(check_block_a2.hash)).unwrap(), BlockStatus::InChainPruned);
assert_eq!(client.check_block(check_block_a3.clone()).unwrap(), ImportResult::AlreadyInChain);
assert_eq!(client.block_status(&BlockId::hash(check_block_a3.hash)).unwrap(), BlockStatus::InChainWithState);
assert_eq!(
block_on(client.check_block(check_block_a1.clone())).unwrap(),
ImportResult::AlreadyInChain,
);
assert_eq!(
client.block_status(&BlockId::hash(check_block_a1.hash)).unwrap(),
BlockStatus::InChainPruned,
);
assert_eq!(
block_on(client.check_block(check_block_a2.clone())).unwrap(),
ImportResult::AlreadyInChain,
);
assert_eq!(
client.block_status(&BlockId::hash(check_block_a2.hash)).unwrap(),
BlockStatus::InChainPruned,
);
assert_eq!(
block_on(client.check_block(check_block_a3.clone())).unwrap(),
ImportResult::AlreadyInChain,
);
assert_eq!(
client.block_status(&BlockId::hash(check_block_a3.hash)).unwrap(),
BlockStatus::InChainWithState,
);
let mut check_block_b1 = BlockCheckParams {
hash: b1.hash().clone(),
@@ -1565,11 +1623,20 @@ fn returns_status_for_pruned_blocks() {
allow_missing_state: false,
import_existing: false,
};
assert_eq!(client.check_block(check_block_b1.clone()).unwrap(), ImportResult::MissingState);
assert_eq!(
block_on(client.check_block(check_block_b1.clone())).unwrap(),
ImportResult::MissingState,
);
check_block_b1.allow_missing_state = true;
assert_eq!(client.check_block(check_block_b1.clone()).unwrap(), ImportResult::imported(false));
assert_eq!(
block_on(client.check_block(check_block_b1.clone())).unwrap(),
ImportResult::imported(false),
);
check_block_b1.parent_hash = H256::random();
assert_eq!(client.check_block(check_block_b1.clone()).unwrap(), ImportResult::UnknownParent);
assert_eq!(
block_on(client.check_block(check_block_b1.clone())).unwrap(),
ImportResult::UnknownParent,
);
}
#[test]
@@ -1600,18 +1667,18 @@ fn imports_blocks_with_changes_tries_config_change() {
(1..11).for_each(|number| {
let block = client.new_block_at(&BlockId::Number(number - 1), Default::default(), false)
.unwrap().build().unwrap().block;
client.import(BlockOrigin::Own, block).unwrap();
block_on(client.import(BlockOrigin::Own, block)).unwrap();
});
(11..12).for_each(|number| {
let mut block = client.new_block_at(&BlockId::Number(number - 1), Default::default(), false).unwrap();
block.push_storage_change(vec![42], Some(number.to_le_bytes().to_vec())).unwrap();
let block = block.build().unwrap().block;
client.import(BlockOrigin::Own, block).unwrap();
block_on(client.import(BlockOrigin::Own, block)).unwrap();
});
(12..23).for_each(|number| {
let block = client.new_block_at(&BlockId::Number(number - 1), Default::default(), false)
.unwrap().build().unwrap().block;
client.import(BlockOrigin::Own, block).unwrap();
block_on(client.import(BlockOrigin::Own, block)).unwrap();
});
(23..24).for_each(|number| {
let mut block = client.new_block_at(&BlockId::Number(number - 1), Default::default(), false).unwrap();
@@ -1620,24 +1687,24 @@ fn imports_blocks_with_changes_tries_config_change() {
digest_levels: 1,
})).unwrap();
let block = block.build().unwrap().block;
client.import(BlockOrigin::Own, block).unwrap();
block_on(client.import(BlockOrigin::Own, block)).unwrap();
});
(24..26).for_each(|number| {
let mut block = client.new_block_at(&BlockId::Number(number - 1), Default::default(), false).unwrap();
block.push_storage_change(vec![42], Some(number.to_le_bytes().to_vec())).unwrap();
let block = block.build().unwrap().block;
client.import(BlockOrigin::Own, block).unwrap();
block_on(client.import(BlockOrigin::Own, block)).unwrap();
});
(26..27).for_each(|number| {
let block = client.new_block_at(&BlockId::Number(number - 1), Default::default(), false)
.unwrap().build().unwrap().block;
client.import(BlockOrigin::Own, block).unwrap();
block_on(client.import(BlockOrigin::Own, block)).unwrap();
});
(27..28).for_each(|number| {
let mut block = client.new_block_at(&BlockId::Number(number - 1), Default::default(), false).unwrap();
block.push_storage_change(vec![42], Some(number.to_le_bytes().to_vec())).unwrap();
let block = block.build().unwrap().block;
client.import(BlockOrigin::Own, block).unwrap();
block_on(client.import(BlockOrigin::Own, block)).unwrap();
});
(28..29).for_each(|number| {
let mut block = client.new_block_at(&BlockId::Number(number - 1), Default::default(), false).unwrap();
@@ -1646,23 +1713,23 @@ fn imports_blocks_with_changes_tries_config_change() {
digest_levels: 1,
})).unwrap();
let block = block.build().unwrap().block;
client.import(BlockOrigin::Own, block).unwrap();
block_on(client.import(BlockOrigin::Own, block)).unwrap();
});
(29..30).for_each(|number| {
let block = client.new_block_at(&BlockId::Number(number - 1), Default::default(), false)
.unwrap().build().unwrap().block;
client.import(BlockOrigin::Own, block).unwrap();
block_on(client.import(BlockOrigin::Own, block)).unwrap();
});
(30..31).for_each(|number| {
let mut block = client.new_block_at(&BlockId::Number(number - 1), Default::default(), false).unwrap();
block.push_storage_change(vec![42], Some(number.to_le_bytes().to_vec())).unwrap();
let block = block.build().unwrap().block;
client.import(BlockOrigin::Own, block).unwrap();
block_on(client.import(BlockOrigin::Own, block)).unwrap();
});
(31..32).for_each(|number| {
let block = client.new_block_at(&BlockId::Number(number - 1), Default::default(), false)
.unwrap().build().unwrap().block;
client.import(BlockOrigin::Own, block).unwrap();
block_on(client.import(BlockOrigin::Own, block)).unwrap();
});
// now check that configuration cache works
@@ -1778,7 +1845,7 @@ fn cleans_up_closed_notification_sinks_on_block_import() {
let mut import = BlockImportParams::new(origin, header);
import.body = Some(extrinsics);
import.fork_choice = Some(ForkChoiceStrategy::LongestChain);
client.import_block(import, Default::default()).unwrap();
block_on(client.import_block(import, Default::default())).unwrap();
};
// after importing a block we should still have 4 notification sinks
@@ -1821,14 +1888,14 @@ fn reorg_triggers_a_notification_even_for_sources_that_should_not_trigger_notifi
Default::default(),
false,
).unwrap().build().unwrap().block;
client.import(BlockOrigin::NetworkInitialSync, a1.clone()).unwrap();
block_on(client.import(BlockOrigin::NetworkInitialSync, a1.clone())).unwrap();
let a2 = client.new_block_at(
&BlockId::Hash(a1.hash()),
Default::default(),
false,
).unwrap().build().unwrap().block;
client.import(BlockOrigin::NetworkInitialSync, a2.clone()).unwrap();
block_on(client.import(BlockOrigin::NetworkInitialSync, a2.clone())).unwrap();
let mut b1 = client.new_block_at(
&BlockId::Number(0),
@@ -1843,7 +1910,7 @@ fn reorg_triggers_a_notification_even_for_sources_that_should_not_trigger_notifi
nonce: 0,
}).unwrap();
let b1 = b1.build().unwrap().block;
client.import(BlockOrigin::NetworkInitialSync, b1.clone()).unwrap();
block_on(client.import(BlockOrigin::NetworkInitialSync, b1.clone())).unwrap();
let b2 = client.new_block_at(
&BlockId::Hash(b1.hash()),
@@ -1852,7 +1919,7 @@ fn reorg_triggers_a_notification_even_for_sources_that_should_not_trigger_notifi
).unwrap().build().unwrap().block;
// Should trigger a notification because we reorg
client.import_as_best(BlockOrigin::NetworkInitialSync, b2.clone()).unwrap();
block_on(client.import_as_best(BlockOrigin::NetworkInitialSync, b2.clone())).unwrap();
// There should be one notification
let notification = notification_stream.next().unwrap();
+3 -3
View File
@@ -37,7 +37,7 @@ type SharedEpochChanges<TBl> = sc_consensus_epochs::SharedEpochChanges<TBl, sc_c
enum Error<Block: BlockT> {
#[error(transparent)]
Blockchain(#[from] sp_blockchain::Error),
#[error("Failed to load the block weight for block {0:?}")]
LoadingBlockWeightFailed(<Block as BlockT>::Hash),
@@ -94,7 +94,7 @@ impl<TBl, TCl> SyncStateRpcHandler<TBl, TCl>
chain_spec, client, shared_authority_set, shared_epoch_changes, deny_unsafe,
}
}
fn build_sync_state(&self) -> Result<sc_chain_spec::LightSyncState<TBl>, Error<TBl>> {
let finalized_hash = self.client.info().finalized_hash;
let finalized_header = self.client.header(BlockId::Hash(finalized_hash))?
@@ -108,7 +108,7 @@ impl<TBl, TCl> SyncStateRpcHandler<TBl, TCl>
Ok(sc_chain_spec::LightSyncState {
finalized_block_header: finalized_header,
babe_epoch_changes: self.shared_epoch_changes.lock().clone(),
babe_epoch_changes: self.shared_epoch_changes.shared_data().clone(),
babe_finalized_block_weight: finalized_block_weight,
grandpa_authority_set: self.shared_authority_set.clone_inner(),
})
@@ -985,7 +985,7 @@ fn import_notification_to_pool_maintain_works() {
let mut block_builder = client.new_block(Default::default()).unwrap();
block_builder.push(xt).unwrap();
let block = block_builder.build().unwrap().block;
client.import(BlockOrigin::Own, block).unwrap();
block_on(client.import(BlockOrigin::Own, block)).unwrap();
// Get the notification of the block import and maintain the pool with it,
// Now, the pool should not contain any transactions.
@@ -34,6 +34,7 @@ parking_lot = "0.11.1"
serde = { version = "1.0", features = ["derive"] }
prometheus-endpoint = { package = "substrate-prometheus-endpoint", path = "../../../utils/prometheus", version = "0.9.0"}
wasm-timer = "0.2.5"
async-trait = "0.1.42"
[dev-dependencies]
futures = "0.3.9"
@@ -146,7 +146,7 @@ pub struct BlockImportParams<Block: BlockT, Transaction> {
/// Intermediate values that are interpreted by block importers. Each block importer,
/// upon handling a value, removes it from the intermediate list. The final block importer
/// rejects block import if there are still intermediate values that remain unhandled.
pub intermediates: HashMap<Cow<'static, [u8]>, Box<dyn Any>>,
pub intermediates: HashMap<Cow<'static, [u8]>, Box<dyn Any + Send>>,
/// Auxiliary consensus data produced by the block.
/// Contains a list of key-value pairs. If values are `None`, the keys
/// will be deleted.
@@ -264,14 +264,15 @@ impl<Block: BlockT, Transaction> BlockImportParams<Block, Transaction> {
}
/// Block import trait.
#[async_trait::async_trait]
pub trait BlockImport<B: BlockT> {
/// The error type.
type Error: std::error::Error + Send + 'static;
/// The transaction type used by the backend.
type Transaction;
type Transaction: Send + 'static;
/// Check block preconditions.
fn check_block(
async fn check_block(
&mut self,
block: BlockCheckParams<B>,
) -> Result<ImportResult, Self::Error>;
@@ -279,56 +280,64 @@ pub trait BlockImport<B: BlockT> {
/// Import a block.
///
/// Cached data can be accessed through the blockchain cache.
fn import_block(
async fn import_block(
&mut self,
block: BlockImportParams<B, Self::Transaction>,
cache: HashMap<CacheKeyId, Vec<u8>>,
) -> Result<ImportResult, Self::Error>;
}
impl<B: BlockT, Transaction> BlockImport<B> for crate::import_queue::BoxBlockImport<B, Transaction> {
#[async_trait::async_trait]
impl<B: BlockT, Transaction> BlockImport<B> for crate::import_queue::BoxBlockImport<B, Transaction>
where
Transaction: Send + 'static,
{
type Error = crate::error::Error;
type Transaction = Transaction;
/// Check block preconditions.
fn check_block(
async fn check_block(
&mut self,
block: BlockCheckParams<B>,
) -> Result<ImportResult, Self::Error> {
(**self).check_block(block)
(**self).check_block(block).await
}
/// Import a block.
///
/// Cached data can be accessed through the blockchain cache.
fn import_block(
async fn import_block(
&mut self,
block: BlockImportParams<B, Transaction>,
cache: HashMap<CacheKeyId, Vec<u8>>,
) -> Result<ImportResult, Self::Error> {
(**self).import_block(block, cache)
(**self).import_block(block, cache).await
}
}
#[async_trait::async_trait]
impl<B: BlockT, T, E: std::error::Error + Send + 'static, Transaction> BlockImport<B> for Arc<T>
where for<'r> &'r T: BlockImport<B, Error = E, Transaction = Transaction>
where
for<'r> &'r T: BlockImport<B, Error = E, Transaction = Transaction>,
T: Send + Sync,
Transaction: Send + 'static,
{
type Error = E;
type Transaction = Transaction;
fn check_block(
async fn check_block(
&mut self,
block: BlockCheckParams<B>,
) -> Result<ImportResult, Self::Error> {
(&**self).check_block(block)
(&**self).check_block(block).await
}
fn import_block(
async fn import_block(
&mut self,
block: BlockImportParams<B, Transaction>,
cache: HashMap<CacheKeyId, Vec<u8>>,
) -> Result<ImportResult, Self::Error> {
(&**self).import_block(block, cache)
(&**self).import_block(block, cache).await
}
}
@@ -82,11 +82,12 @@ pub struct IncomingBlock<B: BlockT> {
pub type CacheKeyId = [u8; 4];
/// Verify a justification of a block
#[async_trait::async_trait]
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(
async fn verify(
&mut self,
origin: BlockOrigin,
header: B::Header,
@@ -163,18 +164,18 @@ pub enum BlockImportError {
}
/// Single block import function.
pub fn import_single_block<B: BlockT, V: Verifier<B>, Transaction>(
import_handle: &mut dyn BlockImport<B, Transaction = Transaction, Error = ConsensusError>,
pub async fn import_single_block<B: BlockT, V: Verifier<B>, Transaction: Send + 'static>(
import_handle: &mut impl BlockImport<B, Transaction = Transaction, Error = ConsensusError>,
block_origin: BlockOrigin,
block: IncomingBlock<B>,
verifier: &mut V,
) -> Result<BlockImportResult<NumberFor<B>>, BlockImportError> {
import_single_block_metered(import_handle, block_origin, block, verifier, None)
import_single_block_metered(import_handle, block_origin, block, verifier, None).await
}
/// Single block import function with metering.
pub(crate) fn import_single_block_metered<B: BlockT, V: Verifier<B>, Transaction>(
import_handle: &mut dyn BlockImport<B, Transaction = Transaction, Error = ConsensusError>,
pub(crate) async fn import_single_block_metered<B: BlockT, V: Verifier<B>, Transaction: Send + 'static>(
import_handle: &mut impl BlockImport<B, Transaction = Transaction, Error = ConsensusError>,
block_origin: BlockOrigin,
block: IncomingBlock<B>,
verifier: &mut V,
@@ -232,24 +233,28 @@ pub(crate) fn import_single_block_metered<B: BlockT, V: Verifier<B>, Transaction
parent_hash,
allow_missing_state: block.allow_missing_state,
import_existing: block.import_existing,
}))? {
}).await)? {
BlockImportResult::ImportedUnknown { .. } => (),
r => return Ok(r), // Any other successful result means that the block is already imported.
}
let started = wasm_timer::Instant::now();
let (mut import_block, maybe_keys) = verifier.verify(block_origin, header, justifications, 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);
}
if let Some(metrics) = metrics.as_ref() {
metrics.report_verification(false, started.elapsed());
}
BlockImportError::VerificationFailed(peer.clone(), msg)
})?;
let (mut import_block, maybe_keys) = verifier.verify(
block_origin,
header,
justifications,
block.body
).await.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);
}
if let Some(metrics) = metrics.as_ref() {
metrics.report_verification(false, started.elapsed());
}
BlockImportError::VerificationFailed(peer.clone(), msg)
})?;
if let Some(metrics) = metrics.as_ref() {
metrics.report_verification(true, started.elapsed());
@@ -261,7 +266,7 @@ pub(crate) fn import_single_block_metered<B: BlockT, V: Verifier<B>, Transaction
}
import_block.allow_missing_state = block.allow_missing_state;
let imported = import_handle.import_block(import_block.convert_transaction(), cache);
let imported = import_handle.import_block(import_block.convert_transaction(), cache).await;
if let Some(metrics) = metrics.as_ref() {
metrics.report_verification_and_import(started.elapsed());
}
@@ -155,7 +155,7 @@ mod worker_messages {
/// to be run.
///
/// Returns when `block_import` ended.
async fn block_import_process<B: BlockT, Transaction: Send>(
async fn block_import_process<B: BlockT, Transaction: Send + 'static>(
mut block_import: BoxBlockImport<B, Transaction>,
mut verifier: impl Verifier<B>,
mut result_sender: BufferedLinkSender<B>,
@@ -195,7 +195,7 @@ struct BlockImportWorker<B: BlockT> {
}
impl<B: BlockT> BlockImportWorker<B> {
fn new<V: 'static + Verifier<B>, Transaction: Send>(
fn new<V: 'static + Verifier<B>, Transaction: Send + 'static>(
result_sender: BufferedLinkSender<B>,
verifier: V,
block_import: BoxBlockImport<B, Transaction>,
@@ -322,7 +322,7 @@ struct ImportManyBlocksResult<B: BlockT> {
/// Import several blocks at once, returning import result for each block.
///
/// This will yield after each imported block once, to ensure that other futures can be called as well.
async fn import_many_blocks<B: BlockT, V: Verifier<B>, Transaction>(
async fn import_many_blocks<B: BlockT, V: Verifier<B>, Transaction: Send + 'static>(
import_handle: &mut BoxBlockImport<B, Transaction>,
blocks_origin: BlockOrigin,
blocks: Vec<IncomingBlock<B>>,
@@ -371,7 +371,7 @@ async fn import_many_blocks<B: BlockT, V: Verifier<B>, Transaction>(
block,
verifier,
metrics.clone(),
)
).await
};
if let Some(metrics) = metrics.as_ref() {
@@ -439,8 +439,9 @@ mod tests {
use sp_test_primitives::{Block, BlockNumber, Extrinsic, Hash, Header};
use std::collections::HashMap;
#[async_trait::async_trait]
impl Verifier<Block> for () {
fn verify(
async fn verify(
&mut self,
origin: BlockOrigin,
header: Header,
@@ -451,18 +452,19 @@ mod tests {
}
}
#[async_trait::async_trait]
impl BlockImport<Block> for () {
type Error = crate::Error;
type Transaction = Extrinsic;
fn check_block(
async fn check_block(
&mut self,
_block: BlockCheckParams<Block>,
) -> Result<ImportResult, Self::Error> {
Ok(ImportResult::imported(false))
}
fn import_block(
async fn import_block(
&mut self,
_block: BlockImportParams<Block, Self::Transaction>,
_cache: HashMap<CacheKeyId, Vec<u8>>,
+1
View File
@@ -33,3 +33,4 @@ sp-keystore = { version = "0.9.0", path = "../../primitives/keystore" }
sp-keyring = { version = "3.0.0", path = "../../primitives/keyring" }
sp-runtime = { version = "3.0.0", path = "../../primitives/runtime" }
sp-state-machine = { version = "0.9.0", path = "../../primitives/state-machine" }
async-trait = "0.1.42"
+56 -21
View File
@@ -43,18 +43,20 @@ pub trait ClientExt<Block: BlockT>: Sized {
}
/// Extension trait for a test client around block importing.
#[async_trait::async_trait]
pub trait ClientBlockImportExt<Block: BlockT>: Sized {
/// Import block to the chain. No finality.
fn import(&mut self, origin: BlockOrigin, block: Block) -> Result<(), ConsensusError>;
async fn import(&mut self, origin: BlockOrigin, block: Block) -> Result<(), ConsensusError>;
/// Import a block and make it our best block if possible.
fn import_as_best(&mut self, origin: BlockOrigin, block: Block) -> Result<(), ConsensusError>;
async fn import_as_best(&mut self, origin: BlockOrigin, block: Block) -> Result<(), ConsensusError>;
/// Import a block and finalize it.
fn import_as_final(&mut self, origin: BlockOrigin, block: Block) -> Result<(), ConsensusError>;
async fn import_as_final(&mut self, origin: BlockOrigin, block: Block)
-> Result<(), ConsensusError>;
/// Import block with justification(s), finalizes block.
fn import_justified(
async fn import_justified(
&mut self,
origin: BlockOrigin,
block: Block,
@@ -83,38 +85,54 @@ impl<B, E, RA, Block> ClientExt<Block> for Client<B, E, Block, RA>
}
/// This implementation is required, because of the weird api requirements around `BlockImport`.
#[async_trait::async_trait]
impl<Block: BlockT, T, Transaction> ClientBlockImportExt<Block> for std::sync::Arc<T>
where for<'r> &'r T: BlockImport<Block, Error = ConsensusError, Transaction = Transaction>
where
for<'r> &'r T: BlockImport<Block, Error = ConsensusError, Transaction = Transaction>,
Transaction: Send + 'static,
T: Send + Sync,
{
fn import(&mut self, origin: BlockOrigin, block: Block) -> Result<(), ConsensusError> {
async fn import(
&mut self,
origin: BlockOrigin,
block: Block,
) -> Result<(), ConsensusError> {
let (header, extrinsics) = block.deconstruct();
let mut import = BlockImportParams::new(origin, header);
import.body = Some(extrinsics);
import.fork_choice = Some(ForkChoiceStrategy::LongestChain);
BlockImport::import_block(self, import, HashMap::new()).map(|_| ())
BlockImport::import_block(self, import, HashMap::new()).await.map(|_| ())
}
fn import_as_best(&mut self, origin: BlockOrigin, block: Block) -> Result<(), ConsensusError> {
async fn import_as_best(
&mut self,
origin: BlockOrigin,
block: Block,
) -> Result<(), ConsensusError> {
let (header, extrinsics) = block.deconstruct();
let mut import = BlockImportParams::new(origin, header);
import.body = Some(extrinsics);
import.fork_choice = Some(ForkChoiceStrategy::Custom(true));
BlockImport::import_block(self, import, HashMap::new()).map(|_| ())
BlockImport::import_block(self, import, HashMap::new()).await.map(|_| ())
}
fn import_as_final(&mut self, origin: BlockOrigin, block: Block) -> Result<(), ConsensusError> {
async fn import_as_final(
&mut self,
origin: BlockOrigin,
block: Block,
) -> Result<(), ConsensusError> {
let (header, extrinsics) = block.deconstruct();
let mut import = BlockImportParams::new(origin, header);
import.body = Some(extrinsics);
import.finalized = true;
import.fork_choice = Some(ForkChoiceStrategy::Custom(true));
BlockImport::import_block(self, import, HashMap::new()).map(|_| ())
BlockImport::import_block(self, import, HashMap::new()).await.map(|_| ())
}
fn import_justified(
async fn import_justified(
&mut self,
origin: BlockOrigin,
block: Block,
@@ -127,43 +145,60 @@ impl<Block: BlockT, T, Transaction> ClientBlockImportExt<Block> for std::sync::A
import.finalized = true;
import.fork_choice = Some(ForkChoiceStrategy::LongestChain);
BlockImport::import_block(self, import, HashMap::new()).map(|_| ())
BlockImport::import_block(self, import, HashMap::new()).await.map(|_| ())
}
}
#[async_trait::async_trait]
impl<B, E, RA, Block: BlockT> ClientBlockImportExt<Block> for Client<B, E, Block, RA>
where
Self: BlockImport<Block, Error = ConsensusError>,
RA: Send,
B: Send + Sync,
E: Send,
<Self as BlockImport<Block>>::Transaction: Send,
{
fn import(&mut self, origin: BlockOrigin, block: Block) -> Result<(), ConsensusError> {
async fn import(
&mut self,
origin: BlockOrigin,
block: Block,
) -> Result<(), ConsensusError> {
let (header, extrinsics) = block.deconstruct();
let mut import = BlockImportParams::new(origin, header);
import.body = Some(extrinsics);
import.fork_choice = Some(ForkChoiceStrategy::LongestChain);
BlockImport::import_block(self, import, HashMap::new()).map(|_| ())
BlockImport::import_block(self, import, HashMap::new()).await.map(|_| ())
}
fn import_as_best(&mut self, origin: BlockOrigin, block: Block) -> Result<(), ConsensusError> {
async fn import_as_best(
&mut self,
origin: BlockOrigin,
block: Block,
) -> Result<(), ConsensusError> {
let (header, extrinsics) = block.deconstruct();
let mut import = BlockImportParams::new(origin, header);
import.body = Some(extrinsics);
import.fork_choice = Some(ForkChoiceStrategy::Custom(true));
BlockImport::import_block(self, import, HashMap::new()).map(|_| ())
BlockImport::import_block(self, import, HashMap::new()).await.map(|_| ())
}
fn import_as_final(&mut self, origin: BlockOrigin, block: Block) -> Result<(), ConsensusError> {
async fn import_as_final(
&mut self,
origin: BlockOrigin,
block: Block,
) -> Result<(), ConsensusError> {
let (header, extrinsics) = block.deconstruct();
let mut import = BlockImportParams::new(origin, header);
import.body = Some(extrinsics);
import.finalized = true;
import.fork_choice = Some(ForkChoiceStrategy::Custom(true));
BlockImport::import_block(self, import, HashMap::new()).map(|_| ())
BlockImport::import_block(self, import, HashMap::new()).await.map(|_| ())
}
fn import_justified(
async fn import_justified(
&mut self,
origin: BlockOrigin,
block: Block,
@@ -176,6 +211,6 @@ impl<B, E, RA, Block: BlockT> ClientBlockImportExt<Block> for Client<B, E, Block
import.finalized = true;
import.fork_choice = Some(ForkChoiceStrategy::LongestChain);
BlockImport::import_block(self, import, HashMap::new()).map(|_| ())
BlockImport::import_block(self, import, HashMap::new()).await.map(|_| ())
}
}
+1
View File
@@ -53,6 +53,7 @@ serde = { version = "1.0.101", optional = true, features = ["derive"] }
sc-block-builder = { version = "0.9.0", path = "../../client/block-builder" }
sc-executor = { version = "0.9.0", path = "../../client/executor" }
substrate-test-runtime-client = { version = "2.0.0", path = "./client" }
futures = "0.3.9"
[build-dependencies]
substrate-wasm-builder = { version = "4.0.0", path = "../../utils/wasm-builder" }
@@ -32,6 +32,7 @@ use substrate_test_runtime::{self, Transfer};
use sp_runtime::generic::BlockId;
use sp_runtime::traits::{Block as BlockT, HashFor};
use sc_block_builder::BlockBuilderProvider;
use futures::executor::block_on;
/// helper to test the `leaves` implementation for various backends
pub fn test_leaves_for_backend<B: 'static>(backend: Arc<B>) where
@@ -57,7 +58,7 @@ pub fn test_leaves_for_backend<B: 'static>(backend: Arc<B>) where
// G -> A1
let a1 = client.new_block(Default::default()).unwrap().build().unwrap().block;
client.import(BlockOrigin::Own, a1.clone()).unwrap();
block_on(client.import(BlockOrigin::Own, a1.clone())).unwrap();
assert_eq!(
blockchain.leaves().unwrap(),
vec![a1.hash()],
@@ -69,7 +70,7 @@ pub fn test_leaves_for_backend<B: 'static>(backend: Arc<B>) where
Default::default(),
false,
).unwrap().build().unwrap().block;
client.import(BlockOrigin::Own, a2.clone()).unwrap();
block_on(client.import(BlockOrigin::Own, a2.clone())).unwrap();
#[allow(deprecated)]
assert_eq!(
@@ -83,7 +84,7 @@ pub fn test_leaves_for_backend<B: 'static>(backend: Arc<B>) where
Default::default(),
false,
).unwrap().build().unwrap().block;
client.import(BlockOrigin::Own, a3.clone()).unwrap();
block_on(client.import(BlockOrigin::Own, a3.clone())).unwrap();
assert_eq!(
blockchain.leaves().unwrap(),
@@ -96,7 +97,7 @@ pub fn test_leaves_for_backend<B: 'static>(backend: Arc<B>) where
Default::default(),
false,
).unwrap().build().unwrap().block;
client.import(BlockOrigin::Own, a4.clone()).unwrap();
block_on(client.import(BlockOrigin::Own, a4.clone())).unwrap();
assert_eq!(
blockchain.leaves().unwrap(),
vec![a4.hash()],
@@ -109,7 +110,7 @@ pub fn test_leaves_for_backend<B: 'static>(backend: Arc<B>) where
false,
).unwrap().build().unwrap().block;
client.import(BlockOrigin::Own, a5.clone()).unwrap();
block_on(client.import(BlockOrigin::Own, a5.clone())).unwrap();
assert_eq!(
blockchain.leaves().unwrap(),
vec![a5.hash()],
@@ -130,7 +131,7 @@ pub fn test_leaves_for_backend<B: 'static>(backend: Arc<B>) where
nonce: 0,
}).unwrap();
let b2 = builder.build().unwrap().block;
client.import(BlockOrigin::Own, b2.clone()).unwrap();
block_on(client.import(BlockOrigin::Own, b2.clone())).unwrap();
assert_eq!(
blockchain.leaves().unwrap(),
vec![a5.hash(), b2.hash()],
@@ -143,7 +144,7 @@ pub fn test_leaves_for_backend<B: 'static>(backend: Arc<B>) where
false,
).unwrap().build().unwrap().block;
client.import(BlockOrigin::Own, b3.clone()).unwrap();
block_on(client.import(BlockOrigin::Own, b3.clone())).unwrap();
assert_eq!(
blockchain.leaves().unwrap(),
vec![a5.hash(), b3.hash()],
@@ -155,7 +156,7 @@ pub fn test_leaves_for_backend<B: 'static>(backend: Arc<B>) where
Default::default(),
false,
).unwrap().build().unwrap().block;
client.import(BlockOrigin::Own, b4.clone()).unwrap();
block_on(client.import(BlockOrigin::Own, b4.clone())).unwrap();
assert_eq!(
blockchain.leaves().unwrap(),
vec![a5.hash(), b4.hash()],
@@ -175,7 +176,7 @@ pub fn test_leaves_for_backend<B: 'static>(backend: Arc<B>) where
nonce: 1,
}).unwrap();
let c3 = builder.build().unwrap().block;
client.import(BlockOrigin::Own, c3.clone()).unwrap();
block_on(client.import(BlockOrigin::Own, c3.clone())).unwrap();
assert_eq!(
blockchain.leaves().unwrap(),
vec![a5.hash(), b4.hash(), c3.hash()],
@@ -195,7 +196,7 @@ pub fn test_leaves_for_backend<B: 'static>(backend: Arc<B>) where
nonce: 0,
}).unwrap();
let d2 = builder.build().unwrap().block;
client.import(BlockOrigin::Own, d2.clone()).unwrap();
block_on(client.import(BlockOrigin::Own, d2.clone())).unwrap();
assert_eq!(
blockchain.leaves().unwrap(),
vec![a5.hash(), b4.hash(), c3.hash(), d2.hash()],
@@ -220,7 +221,7 @@ pub fn test_children_for_backend<B: 'static>(backend: Arc<B>) where
// G -> A1
let a1 = client.new_block(Default::default()).unwrap().build().unwrap().block;
client.import(BlockOrigin::Own, a1.clone()).unwrap();
block_on(client.import(BlockOrigin::Own, a1.clone())).unwrap();
// A1 -> A2
let a2 = client.new_block_at(
@@ -228,7 +229,7 @@ pub fn test_children_for_backend<B: 'static>(backend: Arc<B>) where
Default::default(),
false,
).unwrap().build().unwrap().block;
client.import(BlockOrigin::Own, a2.clone()).unwrap();
block_on(client.import(BlockOrigin::Own, a2.clone())).unwrap();
// A2 -> A3
let a3 = client.new_block_at(
@@ -236,7 +237,7 @@ pub fn test_children_for_backend<B: 'static>(backend: Arc<B>) where
Default::default(),
false,
).unwrap().build().unwrap().block;
client.import(BlockOrigin::Own, a3.clone()).unwrap();
block_on(client.import(BlockOrigin::Own, a3.clone())).unwrap();
// A3 -> A4
let a4 = client.new_block_at(
@@ -244,7 +245,7 @@ pub fn test_children_for_backend<B: 'static>(backend: Arc<B>) where
Default::default(),
false,
).unwrap().build().unwrap().block;
client.import(BlockOrigin::Own, a4.clone()).unwrap();
block_on(client.import(BlockOrigin::Own, a4.clone())).unwrap();
// A4 -> A5
let a5 = client.new_block_at(
@@ -252,7 +253,7 @@ pub fn test_children_for_backend<B: 'static>(backend: Arc<B>) where
Default::default(),
false,
).unwrap().build().unwrap().block;
client.import(BlockOrigin::Own, a5.clone()).unwrap();
block_on(client.import(BlockOrigin::Own, a5.clone())).unwrap();
// A1 -> B2
let mut builder = client.new_block_at(
@@ -268,7 +269,7 @@ pub fn test_children_for_backend<B: 'static>(backend: Arc<B>) where
nonce: 0,
}).unwrap();
let b2 = builder.build().unwrap().block;
client.import(BlockOrigin::Own, b2.clone()).unwrap();
block_on(client.import(BlockOrigin::Own, b2.clone())).unwrap();
// B2 -> B3
let b3 = client.new_block_at(
@@ -276,7 +277,7 @@ pub fn test_children_for_backend<B: 'static>(backend: Arc<B>) where
Default::default(),
false,
).unwrap().build().unwrap().block;
client.import(BlockOrigin::Own, b3.clone()).unwrap();
block_on(client.import(BlockOrigin::Own, b3.clone())).unwrap();
// B3 -> B4
let b4 = client.new_block_at(
@@ -284,7 +285,7 @@ pub fn test_children_for_backend<B: 'static>(backend: Arc<B>) where
Default::default(),
false,
).unwrap().build().unwrap().block;
client.import(BlockOrigin::Own, b4).unwrap();
block_on(client.import(BlockOrigin::Own, b4)).unwrap();
// // B2 -> C3
let mut builder = client.new_block_at(
@@ -300,7 +301,7 @@ pub fn test_children_for_backend<B: 'static>(backend: Arc<B>) where
nonce: 1,
}).unwrap();
let c3 = builder.build().unwrap().block;
client.import(BlockOrigin::Own, c3.clone()).unwrap();
block_on(client.import(BlockOrigin::Own, c3.clone())).unwrap();
// A1 -> D2
let mut builder = client.new_block_at(
@@ -316,7 +317,7 @@ pub fn test_children_for_backend<B: 'static>(backend: Arc<B>) where
nonce: 0,
}).unwrap();
let d2 = builder.build().unwrap().block;
client.import(BlockOrigin::Own, d2.clone()).unwrap();
block_on(client.import(BlockOrigin::Own, d2.clone())).unwrap();
let genesis_hash = client.chain_info().genesis_hash;
@@ -349,7 +350,7 @@ pub fn test_blockchain_query_by_number_gets_canonical<B: 'static>(backend: Arc<B
// G -> A1
let a1 = client.new_block(Default::default()).unwrap().build().unwrap().block;
client.import(BlockOrigin::Own, a1.clone()).unwrap();
block_on(client.import(BlockOrigin::Own, a1.clone())).unwrap();
// A1 -> A2
let a2 = client.new_block_at(
@@ -357,7 +358,7 @@ pub fn test_blockchain_query_by_number_gets_canonical<B: 'static>(backend: Arc<B
Default::default(),
false,
).unwrap().build().unwrap().block;
client.import(BlockOrigin::Own, a2.clone()).unwrap();
block_on(client.import(BlockOrigin::Own, a2.clone())).unwrap();
// A2 -> A3
let a3 = client.new_block_at(
@@ -365,7 +366,7 @@ pub fn test_blockchain_query_by_number_gets_canonical<B: 'static>(backend: Arc<B
Default::default(),
false,
).unwrap().build().unwrap().block;
client.import(BlockOrigin::Own, a3.clone()).unwrap();
block_on(client.import(BlockOrigin::Own, a3.clone())).unwrap();
// A3 -> A4
let a4 = client.new_block_at(
@@ -373,7 +374,7 @@ pub fn test_blockchain_query_by_number_gets_canonical<B: 'static>(backend: Arc<B
Default::default(),
false,
).unwrap().build().unwrap().block;
client.import(BlockOrigin::Own, a4.clone()).unwrap();
block_on(client.import(BlockOrigin::Own, a4.clone())).unwrap();
// A4 -> A5
let a5 = client.new_block_at(
@@ -381,7 +382,7 @@ pub fn test_blockchain_query_by_number_gets_canonical<B: 'static>(backend: Arc<B
Default::default(),
false,
).unwrap().build().unwrap().block;
client.import(BlockOrigin::Own, a5.clone()).unwrap();
block_on(client.import(BlockOrigin::Own, a5.clone())).unwrap();
// A1 -> B2
let mut builder = client.new_block_at(
@@ -397,7 +398,7 @@ pub fn test_blockchain_query_by_number_gets_canonical<B: 'static>(backend: Arc<B
nonce: 0,
}).unwrap();
let b2 = builder.build().unwrap().block;
client.import(BlockOrigin::Own, b2.clone()).unwrap();
block_on(client.import(BlockOrigin::Own, b2.clone())).unwrap();
// B2 -> B3
let b3 = client.new_block_at(
@@ -405,7 +406,7 @@ pub fn test_blockchain_query_by_number_gets_canonical<B: 'static>(backend: Arc<B
Default::default(),
false,
).unwrap().build().unwrap().block;
client.import(BlockOrigin::Own, b3.clone()).unwrap();
block_on(client.import(BlockOrigin::Own, b3.clone())).unwrap();
// B3 -> B4
let b4 = client.new_block_at(
@@ -413,7 +414,7 @@ pub fn test_blockchain_query_by_number_gets_canonical<B: 'static>(backend: Arc<B
Default::default(),
false,
).unwrap().build().unwrap().block;
client.import(BlockOrigin::Own, b4).unwrap();
block_on(client.import(BlockOrigin::Own, b4)).unwrap();
// // B2 -> C3
let mut builder = client.new_block_at(
@@ -429,7 +430,7 @@ pub fn test_blockchain_query_by_number_gets_canonical<B: 'static>(backend: Arc<B
nonce: 1,
}).unwrap();
let c3 = builder.build().unwrap().block;
client.import(BlockOrigin::Own, c3).unwrap();
block_on(client.import(BlockOrigin::Own, c3)).unwrap();
// A1 -> D2
let mut builder = client.new_block_at(
@@ -445,7 +446,7 @@ pub fn test_blockchain_query_by_number_gets_canonical<B: 'static>(backend: Arc<B
nonce: 0,
}).unwrap();
let d2 = builder.build().unwrap().block;
client.import(BlockOrigin::Own, d2).unwrap();
block_on(client.import(BlockOrigin::Own, d2)).unwrap();
let genesis_hash = client.chain_info().genesis_hash;
+1 -1
View File
@@ -1261,7 +1261,7 @@ mod tests {
(BlockId::Hash(hash), block)
};
client.import(BlockOrigin::Own, block).unwrap();
futures::executor::block_on(client.import(BlockOrigin::Own, block)).unwrap();
// Allocation of 1024k while having ~2048k should succeed.
let ret = client.runtime_api().vec_with_capacity(&new_block_id, 1048576);