Fix block propagation between non-collator nodes (#363)

* Create builder for test nodes

* Fix syncing issue

* Adds missing file
This commit is contained in:
Bastian Köcher
2021-03-11 22:29:19 +01:00
committed by GitHub
parent 321b4434ac
commit 64061a4117
12 changed files with 445 additions and 271 deletions
+155 -152
View File
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -79,7 +79,7 @@ where
fn new( fn new(
block_status: Arc<BS>, block_status: Arc<BS>,
spawner: Arc<dyn SpawnNamed + Send + Sync>, spawner: Arc<dyn SpawnNamed + Send + Sync>,
announce_block: Arc<dyn Fn(Block::Hash, Vec<u8>) + Send + Sync>, announce_block: Arc<dyn Fn(Block::Hash, Option<Vec<u8>>) + Send + Sync>,
backend: Arc<Backend>, backend: Arc<Backend>,
parachain_consensus: Box<dyn ParachainConsensus<Block>>, parachain_consensus: Box<dyn ParachainConsensus<Block>>,
) -> Self { ) -> Self {
@@ -337,7 +337,7 @@ pub struct StartCollatorParams<Block: BlockT, Backend, BS, Spawner> {
pub para_id: ParaId, pub para_id: ParaId,
pub backend: Arc<Backend>, pub backend: Arc<Backend>,
pub block_status: Arc<BS>, pub block_status: Arc<BS>,
pub announce_block: Arc<dyn Fn(Block::Hash, Vec<u8>) + Send + Sync>, pub announce_block: Arc<dyn Fn(Block::Hash, Option<Vec<u8>>) + Send + Sync>,
pub overseer_handler: OverseerHandler, pub overseer_handler: OverseerHandler,
pub spawner: Spawner, pub spawner: Spawner,
pub key: CollatorPair, pub key: CollatorPair,
+1 -1
View File
@@ -26,7 +26,7 @@ polkadot-runtime = { git = "https://github.com/paritytech/polkadot", branch = "m
futures = { version = "0.3.8", features = ["compat"] } futures = { version = "0.3.8", features = ["compat"] }
tokio = "0.1.22" tokio = "0.1.22"
codec = { package = "parity-scale-codec", version = "2.0.0", features = [ "derive" ] } codec = { package = "parity-scale-codec", version = "2.0.0", features = [ "derive" ] }
tracing = "0.1.22" tracing = "0.1.25"
async-trait = "0.1.42" async-trait = "0.1.42"
dyn-clone = "1.0.4" dyn-clone = "1.0.4"
+14 -12
View File
@@ -143,7 +143,7 @@ pub async fn run_parachain_consensus<P, R, Block, B>(
para_id: ParaId, para_id: ParaId,
parachain: Arc<P>, parachain: Arc<P>,
relay_chain: R, relay_chain: R,
announce_block: Arc<dyn Fn(Block::Hash, Vec<u8>) + Send + Sync>, announce_block: Arc<dyn Fn(Block::Hash, Option<Vec<u8>>) + Send + Sync>,
) -> ClientResult<()> ) -> ClientResult<()>
where where
Block: BlockT, Block: BlockT,
@@ -175,7 +175,7 @@ async fn follow_new_best<P, R, Block, B>(
para_id: ParaId, para_id: ParaId,
parachain: Arc<P>, parachain: Arc<P>,
relay_chain: R, relay_chain: R,
announce_block: Arc<dyn Fn(Block::Hash, Vec<u8>) + Send + Sync>, announce_block: Arc<dyn Fn(Block::Hash, Option<Vec<u8>>) + Send + Sync>,
) -> ClientResult<()> ) -> ClientResult<()>
where where
Block: BlockT, Block: BlockT,
@@ -203,7 +203,6 @@ where
Some(h) => handle_new_best_parachain_head( Some(h) => handle_new_best_parachain_head(
h, h,
&*parachain, &*parachain,
&*announce_block,
&mut unset_best_header, &mut unset_best_header,
), ),
None => { None => {
@@ -241,12 +240,19 @@ fn handle_new_block_imported<Block, P>(
notification: BlockImportNotification<Block>, notification: BlockImportNotification<Block>,
unset_best_header_opt: &mut Option<Block::Header>, unset_best_header_opt: &mut Option<Block::Header>,
parachain: &P, parachain: &P,
announce_block: &dyn Fn(Block::Hash, Vec<u8>), announce_block: &dyn Fn(Block::Hash, Option<Vec<u8>>),
) where ) where
Block: BlockT, Block: BlockT,
P: UsageProvider<Block> + Send + Sync + BlockBackend<Block>, P: UsageProvider<Block> + Send + Sync + BlockBackend<Block>,
for<'a> &'a P: BlockImport<Block>, for<'a> &'a P: BlockImport<Block>,
{ {
// HACK
//
// Remove after https://github.com/paritytech/substrate/pull/8052 or similar is merged
if notification.origin != BlockOrigin::Own {
announce_block(notification.hash, None);
}
let unset_best_header = match (notification.is_new_best, &unset_best_header_opt) { let unset_best_header = match (notification.is_new_best, &unset_best_header_opt) {
// If this is the new best block or we don't have any unset block, we can end it here. // If this is the new best block or we don't have any unset block, we can end it here.
(true, _) | (_, None) => return, (true, _) | (_, None) => return,
@@ -274,12 +280,12 @@ fn handle_new_block_imported<Block, P>(
.take() .take()
.expect("We checked above that the value is set; qed"); .expect("We checked above that the value is set; qed");
import_block_as_new_best(unset_hash, unset_best_header, parachain, announce_block); import_block_as_new_best(unset_hash, unset_best_header, parachain);
} }
state => tracing::debug!( state => tracing::debug!(
target: "cumulus-consensus", target: "cumulus-consensus",
unset_best_header = ?unset_best_header, ?unset_best_header,
imported_header = ?notification.header, ?notification.header,
?state, ?state,
"Unexpected state for unset best header.", "Unexpected state for unset best header.",
), ),
@@ -290,7 +296,6 @@ fn handle_new_block_imported<Block, P>(
fn handle_new_best_parachain_head<Block, P>( fn handle_new_best_parachain_head<Block, P>(
head: Vec<u8>, head: Vec<u8>,
parachain: &P, parachain: &P,
announce_block: &dyn Fn(Block::Hash, Vec<u8>),
unset_best_header: &mut Option<Block::Header>, unset_best_header: &mut Option<Block::Header>,
) where ) where
Block: BlockT, Block: BlockT,
@@ -323,7 +328,7 @@ fn handle_new_best_parachain_head<Block, P>(
Ok(BlockStatus::InChainWithState) => { Ok(BlockStatus::InChainWithState) => {
unset_best_header.take(); unset_best_header.take();
import_block_as_new_best(hash, parachain_head, parachain, announce_block); import_block_as_new_best(hash, parachain_head, parachain);
} }
Ok(BlockStatus::InChainPruned) => { Ok(BlockStatus::InChainPruned) => {
tracing::error!( tracing::error!(
@@ -358,7 +363,6 @@ fn import_block_as_new_best<Block, P>(
hash: Block::Hash, hash: Block::Hash,
header: Block::Header, header: Block::Header,
parachain: &P, parachain: &P,
announce_block: &dyn Fn(Block::Hash, Vec<u8>),
) where ) where
Block: BlockT, Block: BlockT,
P: UsageProvider<Block> + Send + Sync + BlockBackend<Block>, P: UsageProvider<Block> + Send + Sync + BlockBackend<Block>,
@@ -376,8 +380,6 @@ fn import_block_as_new_best<Block, P>(
error = ?err, error = ?err,
"Failed to set new best block.", "Failed to set new best block.",
); );
} else {
(*announce_block)(hash, Vec::new());
} }
} }
+7
View File
@@ -30,11 +30,15 @@ parking_lot = "0.10.2"
derive_more = "0.99.2" derive_more = "0.99.2"
[dev-dependencies] [dev-dependencies]
tokio = { version = "0.2.21", features = ["macros"] }
# Cumulus deps # Cumulus deps
cumulus-test-service = { path = "../../test/service" } cumulus-test-service = { path = "../../test/service" }
cumulus-primitives-core = { path = "../../primitives/core" }
# Polkadot deps # Polkadot deps
polkadot-test-client = { git = "https://github.com/paritytech/polkadot", branch = "master" } polkadot-test-client = { git = "https://github.com/paritytech/polkadot", branch = "master" }
polkadot-test-service = { git = "https://github.com/paritytech/polkadot", branch = "master" }
# substrate deps # substrate deps
sc-client-api = { git = "https://github.com/paritytech/substrate", branch = "master" } sc-client-api = { git = "https://github.com/paritytech/substrate", branch = "master" }
@@ -42,3 +46,6 @@ sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" }
sp-keyring = { git = "https://github.com/paritytech/substrate", branch = "master" } sp-keyring = { git = "https://github.com/paritytech/substrate", branch = "master" }
sp-keystore = { git = "https://github.com/paritytech/substrate", branch = "master" } sp-keystore = { git = "https://github.com/paritytech/substrate", branch = "master" }
sp-consensus = { git = "https://github.com/paritytech/substrate", branch = "master" } sp-consensus = { git = "https://github.com/paritytech/substrate", branch = "master" }
sc-cli = { git = "https://github.com/paritytech/substrate", branch = "master" }
substrate-test-utils = { git = "https://github.com/paritytech/substrate", branch = "master" }
sc-service = { git = "https://github.com/paritytech/substrate", branch = "master" }
+4 -4
View File
@@ -473,14 +473,14 @@ where
/// the previous task running. /// the previous task running.
pub struct WaitToAnnounce<Block: BlockT> { pub struct WaitToAnnounce<Block: BlockT> {
spawner: Arc<dyn SpawnNamed + Send + Sync>, spawner: Arc<dyn SpawnNamed + Send + Sync>,
announce_block: Arc<dyn Fn(Block::Hash, Vec<u8>) + Send + Sync>, announce_block: Arc<dyn Fn(Block::Hash, Option<Vec<u8>>) + Send + Sync>,
} }
impl<Block: BlockT> WaitToAnnounce<Block> { impl<Block: BlockT> WaitToAnnounce<Block> {
/// Create the `WaitToAnnounce` object /// Create the `WaitToAnnounce` object
pub fn new( pub fn new(
spawner: Arc<dyn SpawnNamed + Send + Sync>, spawner: Arc<dyn SpawnNamed + Send + Sync>,
announce_block: Arc<dyn Fn(Block::Hash, Vec<u8>) + Send + Sync>, announce_block: Arc<dyn Fn(Block::Hash, Option<Vec<u8>>) + Send + Sync>,
) -> WaitToAnnounce<Block> { ) -> WaitToAnnounce<Block> {
WaitToAnnounce { WaitToAnnounce {
spawner, spawner,
@@ -522,7 +522,7 @@ impl<Block: BlockT> WaitToAnnounce<Block> {
async fn wait_to_announce<Block: BlockT>( async fn wait_to_announce<Block: BlockT>(
block_hash: <Block as BlockT>::Hash, block_hash: <Block as BlockT>::Hash,
pov_hash: PHash, pov_hash: PHash,
announce_block: Arc<dyn Fn(Block::Hash, Vec<u8>) + Send + Sync>, announce_block: Arc<dyn Fn(Block::Hash, Option<Vec<u8>>) + Send + Sync>,
signed_stmt_recv: oneshot::Receiver<SignedFullStatement>, signed_stmt_recv: oneshot::Receiver<SignedFullStatement>,
) { ) {
let statement = match signed_stmt_recv.await { let statement = match signed_stmt_recv.await {
@@ -541,7 +541,7 @@ async fn wait_to_announce<Block: BlockT>(
match statement.payload() { match statement.payload() {
Statement::Seconded(c) if &c.descriptor.pov_hash == &pov_hash => { Statement::Seconded(c) if &c.descriptor.pov_hash == &pov_hash => {
if let Ok(data) = BlockAnnounceData::try_from(statement) { if let Ok(data) = BlockAnnounceData::try_from(statement) {
announce_block(block_hash, data.encode()); announce_block(block_hash, Some(data.encode()));
} }
} }
_ => tracing::debug!( _ => tracing::debug!(
+86
View File
@@ -0,0 +1,86 @@
// Copyright 2021 Parity Technologies (UK) Ltd.
// This file is part of Substrate.
// Substrate is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Substrate is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
use cumulus_primitives_core::ParaId;
use cumulus_test_service::{initial_head_data, Keyring::*};
use futures::join;
use sc_service::TaskExecutor;
#[substrate_test_utils::test]
async fn sync_blocks_from_tip_without_being_connected_to_a_collator(task_executor: TaskExecutor) {
let mut builder = sc_cli::LoggerBuilder::new("");
builder.with_colors(false);
let _ = builder.init();
let para_id = ParaId::from(100);
// start alice
let alice =
polkadot_test_service::run_validator_node(task_executor.clone(), Alice, || {}, vec![]);
// start bob
let bob = polkadot_test_service::run_validator_node(
task_executor.clone(),
Bob,
|| {},
vec![alice.addr.clone()],
);
// register parachain
alice
.register_parachain(
para_id,
cumulus_test_service::runtime::WASM_BINARY
.expect("You need to build the WASM binary to run this test!")
.to_vec(),
initial_head_data(para_id),
)
.await
.unwrap();
// run charlie as parachain collator
let charlie =
cumulus_test_service::TestNodeBuilder::new(para_id, task_executor.clone(), Charlie)
.enable_collator()
.connect_to_relay_chain_nodes(vec![&alice, &bob])
.build()
.await;
// run dave as parachain full node
let dave = cumulus_test_service::TestNodeBuilder::new(para_id, task_executor.clone(), Dave)
.connect_to_parachain_node(&charlie)
.connect_to_relay_chain_nodes(vec![&alice, &bob])
.build()
.await;
// run eve as parachain full node that is only connected to dave
let eve = cumulus_test_service::TestNodeBuilder::new(para_id, task_executor.clone(), Eve)
.connect_to_parachain_node(&dave)
.exclusively_connect_to_registered_parachain_nodes()
.connect_to_relay_chain_nodes(vec![&alice, &bob])
.build()
.await;
eve.wait_for_blocks(7).await;
join!(
alice.task_manager.clean_shutdown(),
bob.task_manager.clean_shutdown(),
charlie.task_manager.clean_shutdown(),
dave.task_manager.clean_shutdown(),
eve.task_manager.clean_shutdown(),
);
}
+3 -3
View File
@@ -44,7 +44,7 @@ pub struct StartCollatorParams<'a, Block: BlockT, BS, Client, Backend, Spawner,
pub backend: Arc<Backend>, pub backend: Arc<Backend>,
pub block_status: Arc<BS>, pub block_status: Arc<BS>,
pub client: Arc<Client>, pub client: Arc<Client>,
pub announce_block: Arc<dyn Fn(Block::Hash, Vec<u8>) + Send + Sync>, pub announce_block: Arc<dyn Fn(Block::Hash, Option<Vec<u8>>) + Send + Sync>,
pub spawner: Spawner, pub spawner: Spawner,
pub para_id: ParaId, pub para_id: ParaId,
pub collator_key: CollatorPair, pub collator_key: CollatorPair,
@@ -121,7 +121,7 @@ pub struct StartFullNodeParams<'a, Block: BlockT, Client, PClient> {
pub client: Arc<Client>, pub client: Arc<Client>,
pub polkadot_full_node: RFullNode<PClient>, pub polkadot_full_node: RFullNode<PClient>,
pub task_manager: &'a mut TaskManager, pub task_manager: &'a mut TaskManager,
pub announce_block: Arc<dyn Fn(Block::Hash, Vec<u8>) + Send + Sync>, pub announce_block: Arc<dyn Fn(Block::Hash, Option<Vec<u8>>) + Send + Sync>,
} }
/// Start a full node for a parachain. /// Start a full node for a parachain.
@@ -165,7 +165,7 @@ where
struct StartConsensus<'a, Block: BlockT, Client, Backend> { struct StartConsensus<'a, Block: BlockT, Client, Backend> {
para_id: ParaId, para_id: ParaId,
announce_block: Arc<dyn Fn(Block::Hash, Vec<u8>) + Send + Sync>, announce_block: Arc<dyn Fn(Block::Hash, Option<Vec<u8>>) + Send + Sync>,
client: Arc<Client>, client: Arc<Client>,
task_manager: &'a mut TaskManager, task_manager: &'a mut TaskManager,
_phantom: PhantomData<Backend>, _phantom: PhantomData<Backend>,
+1 -1
View File
@@ -207,7 +207,7 @@ where
let announce_block = { let announce_block = {
let network = network.clone(); let network = network.clone();
Arc::new(move |hash, data| network.announce_block(hash, Some(data))) Arc::new(move |hash, data| network.announce_block(hash, data))
}; };
if validator { if validator {
-6
View File
@@ -56,12 +56,6 @@ jsonrpc-core = "15.1.0"
futures = { version = "0.3.5" } futures = { version = "0.3.5" }
tokio = { version = "0.2.21", features = ["macros"] } tokio = { version = "0.2.21", features = ["macros"] }
# Polkadot dependencies
polkadot-test-runtime = { git = "https://github.com/paritytech/polkadot", branch = "master" }
polkadot-test-service = { git = "https://github.com/paritytech/polkadot", branch = "master" }
# Substrate dependencies # Substrate dependencies
pallet-sudo = { git = "https://github.com/paritytech/substrate", branch = "master" }
substrate-test-runtime-client = { git = "https://github.com/paritytech/substrate", branch = "master" }
substrate-test-utils = { git = "https://github.com/paritytech/substrate", branch = "master" } substrate-test-utils = { git = "https://github.com/paritytech/substrate", branch = "master" }
sc-cli = { git = "https://github.com/paritytech/substrate", branch = "master" } sc-cli = { git = "https://github.com/paritytech/substrate", branch = "master" }
+135 -42
View File
@@ -21,10 +21,6 @@
mod chain_spec; mod chain_spec;
mod genesis; mod genesis;
pub use chain_spec::*;
pub use cumulus_test_runtime as runtime;
pub use genesis::*;
use core::future::Future; use core::future::Future;
use cumulus_client_network::BlockAnnounceValidator; use cumulus_client_network::BlockAnnounceValidator;
use cumulus_client_service::{ use cumulus_client_service::{
@@ -53,6 +49,11 @@ use sp_trie::PrefixedMemoryDB;
use std::sync::Arc; use std::sync::Arc;
use substrate_test_client::BlockchainEventsExt; use substrate_test_client::BlockchainEventsExt;
pub use chain_spec::*;
pub use cumulus_test_runtime as runtime;
pub use genesis::*;
pub use sp_keyring::Sr25519Keyring as Keyring;
// Native executor instance. // Native executor instance.
native_executor_instance!( native_executor_instance!(
pub RuntimeExecutor, pub RuntimeExecutor,
@@ -122,10 +123,9 @@ pub fn new_partial(
#[sc_tracing::logging::prefix_logs_with(parachain_config.network.node_name.as_str())] #[sc_tracing::logging::prefix_logs_with(parachain_config.network.node_name.as_str())]
async fn start_node_impl<RB>( async fn start_node_impl<RB>(
parachain_config: Configuration, parachain_config: Configuration,
collator_key: CollatorPair, collator_key: Option<CollatorPair>,
relay_chain_config: Configuration, relay_chain_config: Configuration,
para_id: ParaId, para_id: ParaId,
is_collator: bool,
rpc_ext_builder: RB, rpc_ext_builder: RB,
) -> sc_service::error::Result<( ) -> sc_service::error::Result<(
TaskManager, TaskManager,
@@ -157,7 +157,11 @@ where
let relay_chain_full_node = polkadot_test_service::new_full( let relay_chain_full_node = polkadot_test_service::new_full(
relay_chain_config, relay_chain_config,
polkadot_service::IsCollator::Yes(collator_key.public()), if let Some(ref key) = collator_key {
polkadot_service::IsCollator::Yes(key.public())
} else {
polkadot_service::IsCollator::No
},
) )
.map_err(|e| match e { .map_err(|e| match e {
polkadot_service::Error::Sub(x) => x, polkadot_service::Error::Sub(x) => x,
@@ -212,10 +216,10 @@ where
let announce_block = { let announce_block = {
let network = network.clone(); let network = network.clone();
Arc::new(move |hash, data| network.announce_block(hash, Some(data))) Arc::new(move |hash, data| network.announce_block(hash, data))
}; };
if is_collator { if let Some(collator_key) = collator_key {
let proposer_factory = sc_basic_authorship::ProposerFactory::with_proof_recording( let proposer_factory = sc_basic_authorship::ProposerFactory::with_proof_recording(
task_manager.spawn_handle(), task_manager.spawn_handle(),
client.clone(), client.clone(),
@@ -270,7 +274,7 @@ where
} }
/// A Cumulus test node instance used for testing. /// A Cumulus test node instance used for testing.
pub struct CumulusTestNode { pub struct TestNode {
/// TaskManager's instance. /// TaskManager's instance.
pub task_manager: TaskManager, pub task_manager: TaskManager,
/// Client's instance. /// Client's instance.
@@ -284,35 +288,114 @@ pub struct CumulusTestNode {
pub rpc_handlers: RpcHandlers, pub rpc_handlers: RpcHandlers,
} }
/// Run a Cumulus test node using the Cumulus test runtime. The node will be using an in-memory
/// socket, therefore you need to provide boot nodes if you want it to be connected to other nodes. /// A builder to create a [`TestNode`].
/// The `storage_update_func` can be used to make adjustements to the runtime before the node pub struct TestNodeBuilder {
/// starts. para_id: ParaId,
pub async fn run_test_node(
task_executor: TaskExecutor, task_executor: TaskExecutor,
key: Sr25519Keyring, key: Sr25519Keyring,
parachain_storage_update_func: impl Fn(), collator_key: Option<CollatorPair>,
relay_chain_storage_update_func: impl Fn(), parachain_nodes: Vec<MultiaddrWithPeerId>,
parachain_boot_nodes: Vec<MultiaddrWithPeerId>, parachain_nodes_exclusive: bool,
relay_chain_boot_nodes: Vec<MultiaddrWithPeerId>, relay_chain_nodes: Vec<MultiaddrWithPeerId>,
para_id: ParaId, }
is_collator: bool,
) -> CumulusTestNode { impl TestNodeBuilder {
let collator_key = CollatorPair::generate().0; /// Create a new instance of `Self`.
let parachain_config = node_config( ///
parachain_storage_update_func, /// `para_id` - The parachain id this node is running for.
task_executor.clone(), /// `task_executor` - The task executor to use.
/// `key` - The key that will be used to generate the name and that will be passed as `dev_seed`.
pub fn new(para_id: ParaId, task_executor: TaskExecutor, key: Sr25519Keyring) -> Self {
TestNodeBuilder {
key, key,
parachain_boot_nodes,
para_id, para_id,
is_collator, task_executor,
collator_key: None,
parachain_nodes: Vec::new(),
parachain_nodes_exclusive: false,
relay_chain_nodes: Vec::new(),
}
}
/// Enable collator for this node.
pub fn enable_collator(mut self) -> Self {
let collator_key = CollatorPair::generate().0;
self.collator_key = Some(collator_key);
self
}
/// Instruct the node to exclusively connect to registered parachain nodes.
///
/// Parachain nodes can be registered using [`Self::connect_to_parachain_node`] and
/// [`Self::connect_to_parachain_nodes`].
pub fn exclusively_connect_to_registered_parachain_nodes(mut self) -> Self {
self.parachain_nodes_exclusive = true;
self
}
/// Make the node connect to the given parachain node.
///
/// By default the node will not be connected to any node or will be able to discover any other
/// node.
pub fn connect_to_parachain_node(mut self, node: &TestNode) -> Self {
self.parachain_nodes.push(node.addr.clone());
self
}
/// Make the node connect to the given parachain nodes.
///
/// By default the node will not be connected to any node or will be able to discover any other
/// node.
pub fn connect_to_parachain_nodes<'a>(
mut self,
nodes: impl Iterator<Item = &'a TestNode>,
) -> Self {
self.parachain_nodes.extend(nodes.map(|n| n.addr.clone()));
self
}
/// Make the node connect to the given relay chain node.
///
/// By default the node will not be connected to any node or will be able to discover any other
/// node.
pub fn connect_to_relay_chain_node(
mut self,
node: &polkadot_test_service::PolkadotTestNode,
) -> Self {
self.relay_chain_nodes.push(node.addr.clone());
self
}
/// Make the node connect to the given relay chain nodes.
///
/// By default the node will not be connected to any node or will be able to discover any other
/// node.
pub fn connect_to_relay_chain_nodes<'a>(
mut self,
nodes: impl IntoIterator<Item = &'a polkadot_test_service::PolkadotTestNode>,
) -> Self {
self.relay_chain_nodes.extend(nodes.into_iter().map(|n| n.addr.clone()));
self
}
/// Build the [`TestNode`].
pub async fn build(self) -> TestNode {
let parachain_config = node_config(
|| (),
self.task_executor.clone(),
self.key.clone(),
self.parachain_nodes,
self.parachain_nodes_exclusive,
self.para_id,
self.collator_key.is_some(),
) )
.expect("could not generate Configuration"); .expect("could not generate Configuration");
let mut relay_chain_config = polkadot_test_service::node_config( let mut relay_chain_config = polkadot_test_service::node_config(
relay_chain_storage_update_func, || (),
task_executor.clone(), self.task_executor,
key, self.key,
relay_chain_boot_nodes, self.relay_chain_nodes,
false, false,
); );
@@ -322,10 +405,9 @@ pub async fn run_test_node(
let multiaddr = parachain_config.network.listen_addresses[0].clone(); let multiaddr = parachain_config.network.listen_addresses[0].clone();
let (task_manager, client, network, rpc_handlers) = start_node_impl( let (task_manager, client, network, rpc_handlers) = start_node_impl(
parachain_config, parachain_config,
collator_key, self.collator_key,
relay_chain_config, relay_chain_config,
para_id, self.para_id,
is_collator,
|_| Default::default(), |_| Default::default(),
) )
.await .await
@@ -334,7 +416,7 @@ pub async fn run_test_node(
let peer_id = network.local_peer_id().clone(); let peer_id = network.local_peer_id().clone();
let addr = MultiaddrWithPeerId { multiaddr, peer_id }; let addr = MultiaddrWithPeerId { multiaddr, peer_id };
CumulusTestNode { TestNode {
task_manager, task_manager,
client, client,
network, network,
@@ -342,15 +424,20 @@ pub async fn run_test_node(
rpc_handlers, rpc_handlers,
} }
} }
}
/// Create a Cumulus `Configuration`. By default an in-memory socket will be used, therefore you /// Create a Cumulus `Configuration`.
/// need to provide boot nodes if you want the future node to be connected to other nodes. The ///
/// `storage_update_func` can be used to make adjustments to the runtime before the node starts. /// By default an in-memory socket will be used, therefore you need to provide nodes if you want the
/// node to be connected to other nodes. If `nodes_exclusive` is `true`, the node will only connect
/// to the given `nodes` and not to any other node. The `storage_update_func` can be used to make
/// adjustments to the runtime genesis.
pub fn node_config( pub fn node_config(
storage_update_func: impl Fn(), storage_update_func: impl Fn(),
task_executor: TaskExecutor, task_executor: TaskExecutor,
key: Sr25519Keyring, key: Sr25519Keyring,
boot_nodes: Vec<MultiaddrWithPeerId>, nodes: Vec<MultiaddrWithPeerId>,
nodes_exlusive: bool,
para_id: ParaId, para_id: ParaId,
is_collator: bool, is_collator: bool,
) -> Result<Configuration, ServiceError> { ) -> Result<Configuration, ServiceError> {
@@ -379,7 +466,13 @@ pub fn node_config(
None, None,
); );
network_config.boot_nodes = boot_nodes; if nodes_exlusive {
network_config.default_peers_set.reserved_nodes = nodes;
network_config.default_peers_set.non_reserved_mode =
sc_network::config::NonReservedPeerMode::Deny;
} else {
network_config.boot_nodes = nodes;
}
network_config.allow_non_globals_in_dht = true; network_config.allow_non_globals_in_dht = true;
@@ -445,7 +538,7 @@ pub fn node_config(
}) })
} }
impl CumulusTestNode { impl TestNode {
/// Wait for `count` blocks to be imported in the node and then exit. This function will not /// Wait for `count` blocks to be imported in the node and then exit. This function will not
/// return if no blocks are ever created, thus you should restrict the maximum amount of time of /// return if no blocks are ever created, thus you should restrict the maximum amount of time of
/// the test execution. /// the test execution.
+11 -22
View File
@@ -15,10 +15,9 @@
// along with Substrate. If not, see <http://www.gnu.org/licenses/>. // along with Substrate. If not, see <http://www.gnu.org/licenses/>.
use cumulus_primitives_core::ParaId; use cumulus_primitives_core::ParaId;
use cumulus_test_service::initial_head_data; use cumulus_test_service::{initial_head_data, Keyring::*};
use futures::join; use futures::join;
use sc_service::TaskExecutor; use sc_service::TaskExecutor;
use substrate_test_runtime_client::AccountKeyring::*;
#[substrate_test_utils::test] #[substrate_test_utils::test]
async fn test_collating_and_non_collator_mode_catching_up(task_executor: TaskExecutor) { async fn test_collating_and_non_collator_mode_catching_up(task_executor: TaskExecutor) {
@@ -53,31 +52,21 @@ async fn test_collating_and_non_collator_mode_catching_up(task_executor: TaskExe
.unwrap(); .unwrap();
// run cumulus charlie (a parachain collator) // run cumulus charlie (a parachain collator)
let charlie = cumulus_test_service::run_test_node( let charlie =
task_executor.clone(), cumulus_test_service::TestNodeBuilder::new(para_id, task_executor.clone(), Charlie)
Charlie, .enable_collator()
|| {}, .connect_to_relay_chain_nodes(vec![&alice, &bob])
|| {}, .build()
vec![],
vec![alice.addr.clone(), bob.addr.clone()],
para_id,
true,
)
.await; .await;
charlie.wait_for_blocks(5).await; charlie.wait_for_blocks(5).await;
// run cumulus dave (a parachain full node) and wait for it to sync some blocks // run cumulus dave (a parachain full node) and wait for it to sync some blocks
let dave = cumulus_test_service::run_test_node( let dave = cumulus_test_service::TestNodeBuilder::new(para_id, task_executor.clone(), Dave)
task_executor.clone(), .connect_to_parachain_node(&charlie)
Dave, .connect_to_relay_chain_nodes(vec![&alice, &bob])
|| {}, .build()
|| {},
vec![charlie.addr.clone()],
vec![alice.addr.clone(), bob.addr.clone()],
para_id,
false,
)
.await; .await;
dave.wait_for_blocks(7).await; dave.wait_for_blocks(7).await;
join!( join!(