diff --git a/Cargo.lock b/Cargo.lock index 1fa877c5c3c..9ce6babba84 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -878,7 +878,10 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223" dependencies = [ + "lazy_static", "memchr", + "regex-automata", + "serde", ] [[package]] @@ -974,6 +977,15 @@ dependencies = [ "serde_json", ] +[[package]] +name = "cast" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c24dab4283a142afa2fdca129b80ad2c6284e073930f964c3a1293c225ee39a" +dependencies = [ + "rustc_version 0.4.0", +] + [[package]] name = "cc" version = "1.0.71" @@ -1275,6 +1287,44 @@ dependencies = [ "cfg-if 1.0.0", ] +[[package]] +name = "criterion" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1604dafd25fba2fe2d5895a9da139f8dc9b319a5fe5354ca137cbbce4e178d10" +dependencies = [ + "atty", + "cast", + "clap", + "criterion-plot", + "csv", + "futures 0.3.17", + "itertools", + "lazy_static", + "num-traits", + "oorandom", + "plotters", + "rayon", + "regex", + "serde", + "serde_cbor", + "serde_derive", + "serde_json", + "tinytemplate", + "tokio", + "walkdir", +] + +[[package]] +name = "criterion-plot" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d00996de9f2f7559f7f4dc286073197f83e92256a59ed395f9aac01fe717da57" +dependencies = [ + "cast", + "itertools", +] + [[package]] name = "crossbeam-channel" version = "0.5.1" @@ -1345,6 +1395,28 @@ dependencies = [ "subtle", ] +[[package]] +name = "csv" +version = "1.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22813a6dc45b335f9bade10bf7271dc477e81113e89eb251a0bc2a8a81c536e1" +dependencies = [ + "bstr", + "csv-core", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "csv-core" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b2466559f260f48ad25fe6317b3c8dac77b5bdb5763ac7d9d6103530663bc90" +dependencies = [ + "memchr", +] + [[package]] name = "ct-logs" version = "0.8.0" @@ -1859,6 +1931,7 @@ dependencies = [ "frame-executive", "frame-support", "frame-system", + "frame-system-rpc-runtime-api", "pallet-balances", "pallet-sudo", "pallet-timestamp", @@ -1885,6 +1958,7 @@ name = "cumulus-test-service" version = "0.1.0" dependencies = [ "async-trait", + "criterion", "cumulus-client-consensus-common", "cumulus-client-consensus-relay-chain", "cumulus-client-network", @@ -1894,6 +1968,7 @@ dependencies = [ "cumulus-test-relay-validation-worker-provider", "cumulus-test-runtime", "frame-system", + "frame-system-rpc-runtime-api", "futures 0.3.17", "jsonrpc-core", "pallet-transaction-payment", @@ -1913,6 +1988,7 @@ dependencies = [ "sc-service 0.10.0-dev (git+https://github.com/paritytech/substrate?branch=master)", "sc-tracing 4.0.0-dev (git+https://github.com/paritytech/substrate?branch=master)", "sc-transaction-pool 4.0.0-dev (git+https://github.com/paritytech/substrate?branch=master)", + "sc-transaction-pool-api 4.0.0-dev (git+https://github.com/paritytech/substrate?branch=master)", "serde", "sp-arithmetic 4.0.0-dev (git+https://github.com/paritytech/substrate?branch=master)", "sp-blockchain 4.0.0-dev (git+https://github.com/paritytech/substrate?branch=master)", @@ -1922,6 +1998,7 @@ dependencies = [ "sp-runtime 4.0.0-dev (git+https://github.com/paritytech/substrate?branch=master)", "sp-state-machine 0.10.0-dev (git+https://github.com/paritytech/substrate?branch=master)", "sp-timestamp", + "sp-tracing 4.0.0-dev (git+https://github.com/paritytech/substrate?branch=master)", "sp-trie 4.0.0-dev (git+https://github.com/paritytech/substrate?branch=master)", "substrate-test-client", "substrate-test-utils 4.0.0-dev (git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.12)", @@ -2920,6 +2997,12 @@ dependencies = [ "web-sys", ] +[[package]] +name = "half" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" + [[package]] name = "handlebars" version = "3.5.5" @@ -4962,6 +5045,12 @@ version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56" +[[package]] +name = "oorandom" +version = "11.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" + [[package]] name = "opaque-debug" version = "0.2.3" @@ -6449,6 +6538,34 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "989d43012e2ca1c4a02507c67282691a0a3207f9dc67cec596b43fe925b3d325" +[[package]] +name = "plotters" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a3fd9ec30b9749ce28cd91f255d569591cdf937fe280c312143e3c4bad6f2a" +dependencies = [ + "num-traits", + "plotters-backend", + "plotters-svg", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "plotters-backend" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d88417318da0eaf0fdcdb51a0ee6c3bed624333bff8f946733049380be67ac1c" + +[[package]] +name = "plotters-svg" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521fa9638fa597e1dc53e9412a4f9cefb01187ee1f7413076f9e6749e2885ba9" +dependencies = [ + "plotters-backend", +] + [[package]] name = "polkadot-approval-distribution" version = "0.9.12" @@ -10387,6 +10504,16 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "serde_cbor" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bef2ebfde456fb76bbcf9f59315333decc4fda0b2b44b420243c11e0f5ec1f5" +dependencies = [ + "half", + "serde", +] + [[package]] name = "serde_derive" version = "1.0.130" @@ -12495,6 +12622,16 @@ dependencies = [ "crunchy", ] +[[package]] +name = "tinytemplate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" +dependencies = [ + "serde", + "serde_json", +] + [[package]] name = "tinyvec" version = "1.5.0" diff --git a/test/runtime-upgrade/Cargo.toml b/test/runtime-upgrade/Cargo.toml deleted file mode 100644 index a425abc59e2..00000000000 --- a/test/runtime-upgrade/Cargo.toml +++ /dev/null @@ -1,68 +0,0 @@ -[package] -name = "cumulus-test-runtime-upgrade" -version = "0.1.0" -authors = ["Parity Technologies "] -edition = "2018" - -[dependencies] -codec = { package = "parity-scale-codec", version = "2.3.0", default-features = false, features = ["derive"] } -scale-info = { version = "1.0.0", default-features = false, features = ["derive"] } -serde = { version = "1.0.101", optional = true, features = ["derive"] } - -# Substrate dependencies -frame-executive = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } -frame-support = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } -frame-system = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } -pallet-balances = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } -pallet-sudo = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } -pallet-timestamp = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } -pallet-transaction-payment = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } -sp-api = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } -sp-block-builder = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } -sp-core = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } -sp-inherents = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } -sp-io = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } -sp-offchain = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } -sp-runtime = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } -sp-session = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } -sp-std = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } -sp-transaction-pool = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } -sp-version = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } - -# Cumulus dependencies -cumulus-pallet-parachain-system = { path = "../../pallets/parachain-system", default-features = false } -cumulus-primitives-core = { path = "../../primitives/core", default-features = false } -cumulus-primitives-timestamp = { path = "../../primitives/timestamp", default-features = false } - -[build-dependencies] -substrate-wasm-builder = { git = "https://github.com/paritytech/substrate", branch = "master" } - -[features] -default = [ "std", "upgrade" ] -std = [ - "codec/std", - "scale-info/std", - "cumulus-pallet-parachain-system/std", - "cumulus-primitives-core/std", - "cumulus-primitives-timestamp/std", - "frame-executive/std", - "frame-support/std", - "frame-system/std", - "pallet-balances/std", - "pallet-sudo/std", - "pallet-timestamp/std", - "pallet-transaction-payment/std", - "serde", - "sp-api/std", - "sp-block-builder/std", - "sp-core/std", - "sp-inherents/std", - "sp-io/std", - "sp-offchain/std", - "sp-runtime/std", - "sp-session/std", - "sp-std/std", - "sp-transaction-pool/std", - "sp-version/std", -] -upgrade = [] diff --git a/test/runtime/Cargo.toml b/test/runtime/Cargo.toml index 47cd1806349..f62ad76b51b 100644 --- a/test/runtime/Cargo.toml +++ b/test/runtime/Cargo.toml @@ -13,6 +13,7 @@ serde = { version = "1.0.101", optional = true, features = ["derive"] } frame-executive = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } frame-support = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } frame-system = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } +frame-system-rpc-runtime-api = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } pallet-balances = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } pallet-sudo = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } pallet-timestamp = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } @@ -48,6 +49,7 @@ std = [ "frame-executive/std", "frame-support/std", "frame-system/std", + "frame-system-rpc-runtime-api/std", "pallet-balances/std", "pallet-sudo/std", "pallet-timestamp/std", diff --git a/test/runtime/src/lib.rs b/test/runtime/src/lib.rs index 2ed83ae8292..fb32be5b223 100644 --- a/test/runtime/src/lib.rs +++ b/test/runtime/src/lib.rs @@ -371,6 +371,12 @@ impl_runtime_apis! { } } + impl frame_system_rpc_runtime_api::AccountNonceApi for Runtime { + fn account_nonce(account: AccountId) -> Index { + System::account_nonce(account) + } + } + impl sp_block_builder::BlockBuilder for Runtime { fn apply_extrinsic( extrinsic: ::Extrinsic, diff --git a/test/service/Cargo.toml b/test/service/Cargo.toml index cef2b138057..716e78df003 100644 --- a/test/service/Cargo.toml +++ b/test/service/Cargo.toml @@ -14,9 +14,11 @@ tokio = { version = "1.10", features = ["macros"] } # Substrate frame-system = { git = "https://github.com/paritytech/substrate", branch = "master" } pallet-transaction-payment = { git = "https://github.com/paritytech/substrate", branch = "master" } +frame-system-rpc-runtime-api = { git = "https://github.com/paritytech/substrate", branch = "master" } sc-basic-authorship = { git = "https://github.com/paritytech/substrate", branch = "master" } sc-chain-spec = { git = "https://github.com/paritytech/substrate", branch = "master" } sc-client-api = { git = "https://github.com/paritytech/substrate", branch = "master" } +sc-transaction-pool-api = { git = "https://github.com/paritytech/substrate", branch = "master" } sc-consensus = { git = "https://github.com/paritytech/substrate", branch = "master" } sc-executor = { git = "https://github.com/paritytech/substrate", branch = "master" } sc-network = { git = "https://github.com/paritytech/substrate", branch = "master" } @@ -33,6 +35,7 @@ sp-runtime = { git = "https://github.com/paritytech/substrate", default-features sp-state-machine = { git = "https://github.com/paritytech/substrate", branch = "master" } sp-timestamp = { git = "https://github.com/paritytech/substrate", branch = "master" } sp-trie = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-tracing = { git = "https://github.com/paritytech/substrate", branch = "master" } substrate-test-client = { git = "https://github.com/paritytech/substrate", branch = "master" } # Polkadot @@ -50,6 +53,8 @@ cumulus-primitives-parachain-inherent = { path = "../../primitives/parachain-inh cumulus-test-runtime = { path = "../runtime" } cumulus-test-relay-validation-worker-provider = { path = "../relay-validation-worker-provider" } +criterion = { version = "0.3.5", features = [ "async_tokio" ] } + # RPC related dependencies jsonrpc-core = "18.0.0" @@ -62,3 +67,7 @@ polkadot-test-service = { git = "https://github.com/paritytech/polkadot", branch # Substrate dependencies sc-cli = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.12" } substrate-test-utils = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.12" } + +[[bench]] +name = "transaction_throughput" +harness = false diff --git a/test/service/benches/transaction_throughput.rs b/test/service/benches/transaction_throughput.rs new file mode 100644 index 00000000000..ea711173132 --- /dev/null +++ b/test/service/benches/transaction_throughput.rs @@ -0,0 +1,253 @@ +// This file is part of Cumulus. + +// 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 . + +use criterion::{criterion_group, criterion_main, BatchSize, Criterion, Throughput}; +use cumulus_test_runtime::{AccountId, BalancesCall, SudoCall}; +use futures::{future, join, StreamExt}; +use polkadot_service::polkadot_runtime::constants::currency::DOLLARS; +use sc_transaction_pool_api::{TransactionPool as _, TransactionSource, TransactionStatus}; +use sp_core::{crypto::Pair, sr25519}; +use sp_runtime::{generic::BlockId, OpaqueExtrinsic}; + +use cumulus_primitives_core::ParaId; +use cumulus_test_service::{ + construct_extrinsic, fetch_nonce, initial_head_data, Client, Keyring::*, TransactionPool, +}; + +fn create_accounts(num: usize) -> Vec { + (0..num) + .map(|i| { + Pair::from_string(&format!("{}/{}", Alice.to_seed(), i), None) + .expect("Creates account pair") + }) + .collect() +} + +/// Create the extrinsics that will initialize the accounts from the sudo account (Alice). +/// +/// `start_nonce` is the current nonce of Alice. +fn create_account_extrinsics(client: &Client, accounts: &[sr25519::Pair]) -> Vec { + let start_nonce = fetch_nonce(client, Alice.public()); + + accounts + .iter() + .enumerate() + .map(|(i, a)| { + vec![ + // Reset the nonce by removing any funds + construct_extrinsic( + client, + SudoCall::sudo { + call: Box::new( + BalancesCall::set_balance { + who: AccountId::from(a.public()).into(), + new_free: 0, + new_reserved: 0, + } + .into(), + ), + }, + Alice.pair(), + Some(start_nonce + (i as u32) * 2), + ), + // Give back funds + construct_extrinsic( + client, + SudoCall::sudo { + call: Box::new( + BalancesCall::set_balance { + who: AccountId::from(a.public()).into(), + new_free: 1_000_000 * DOLLARS, + new_reserved: 0, + } + .into(), + ), + }, + Alice.pair(), + Some(start_nonce + (i as u32) * 2 + 1), + ), + ] + }) + .flatten() + .map(OpaqueExtrinsic::from) + .collect() +} + +fn create_benchmark_extrinsics( + client: &Client, + accounts: &[sr25519::Pair], + extrinsics_per_account: usize, +) -> Vec { + accounts + .iter() + .map(|account| { + (0..extrinsics_per_account).map(move |nonce| { + construct_extrinsic( + client, + BalancesCall::transfer { dest: Bob.to_account_id().into(), value: 1 * DOLLARS }, + account.clone(), + Some(nonce as u32), + ) + }) + }) + .flatten() + .map(OpaqueExtrinsic::from) + .collect() +} + +async fn submit_tx_and_wait_for_inclusion( + tx_pool: &TransactionPool, + tx: OpaqueExtrinsic, + client: &Client, + wait_for_finalized: bool, +) { + let best_hash = client.chain_info().best_hash; + + let mut watch = tx_pool + .submit_and_watch(&BlockId::Hash(best_hash), TransactionSource::External, tx.clone()) + .await + .expect("Submits tx to pool") + .fuse(); + + loop { + match watch.select_next_some().await { + TransactionStatus::Finalized(_) => break, + TransactionStatus::InBlock(_) if !wait_for_finalized => break, + _ => {}, + } + } +} + +fn transaction_throughput_benchmarks(c: &mut Criterion) { + sp_tracing::try_init_simple(); + let mut builder = sc_cli::LoggerBuilder::new(""); + builder.with_colors(false); + let _ = builder.init(); + + let para_id = ParaId::from(100); + let runtime = tokio::runtime::Runtime::new().expect("Creates tokio runtime"); + let tokio_handle = runtime.handle(); + + // Start alice + let alice = cumulus_test_service::run_relay_chain_validator_node( + tokio_handle.clone(), + Alice, + || {}, + vec![], + ); + + // Start bob + let bob = cumulus_test_service::run_relay_chain_validator_node( + tokio_handle.clone(), + Bob, + || {}, + vec![alice.addr.clone()], + ); + + // Register parachain + runtime + .block_on( + 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), + ), + ) + .unwrap(); + + // Run charlie as parachain collator + let charlie = runtime.block_on( + cumulus_test_service::TestNodeBuilder::new(para_id, tokio_handle.clone(), Charlie) + .enable_collator() + .connect_to_relay_chain_nodes(vec![&alice, &bob]) + .build(), + ); + + // Run dave as parachain collator + let dave = runtime.block_on( + cumulus_test_service::TestNodeBuilder::new(para_id, tokio_handle.clone(), Dave) + .enable_collator() + .connect_to_parachain_node(&charlie) + .connect_to_relay_chain_nodes(vec![&alice, &bob]) + .build(), + ); + + runtime.block_on(dave.wait_for_blocks(1)); + + let mut group = c.benchmark_group("Transaction pool"); + let account_num = 10; + let extrinsics_per_account = 20; + group.sample_size(10); + group.throughput(Throughput::Elements(account_num as u64 * extrinsics_per_account as u64)); + + let accounts = create_accounts(account_num); + let mut counter = 1; + + let benchmark_handle = tokio_handle.clone(); + group.bench_function( + format!("{} transfers from {} accounts", account_num * extrinsics_per_account, account_num), + |b| { + b.iter_batched( + || { + let prepare_extrinsics = create_account_extrinsics(&*dave.client, &accounts); + + benchmark_handle.block_on(future::join_all( + prepare_extrinsics.into_iter().map(|tx| { + submit_tx_and_wait_for_inclusion( + &dave.transaction_pool, + tx, + &*dave.client, + true, + ) + }), + )); + + create_benchmark_extrinsics(&*dave.client, &accounts, extrinsics_per_account) + }, + |extrinsics| { + benchmark_handle.block_on(future::join_all(extrinsics.into_iter().map(|tx| { + submit_tx_and_wait_for_inclusion( + &dave.transaction_pool, + tx, + &*dave.client, + false, + ) + }))); + + println!("Finished {}", counter); + counter += 1; + }, + BatchSize::SmallInput, + ) + }, + ); + + runtime.block_on(async { + join!( + alice.task_manager.clean_shutdown(), + bob.task_manager.clean_shutdown(), + charlie.task_manager.clean_shutdown(), + dave.task_manager.clean_shutdown(), + ) + }); +} + +criterion_group!(benches, transaction_throughput_benchmarks); +criterion_main!(benches); diff --git a/test/service/src/lib.rs b/test/service/src/lib.rs index 61ed0bb5010..197164efd55 100644 --- a/test/service/src/lib.rs +++ b/test/service/src/lib.rs @@ -29,7 +29,9 @@ use cumulus_client_service::{ }; use cumulus_primitives_core::ParaId; use cumulus_test_runtime::{Hash, Header, NodeBlock as Block, RuntimeApi}; +use frame_system_rpc_runtime_api::AccountNonceApi; use polkadot_primitives::v1::{CollatorPair, Hash as PHash, PersistedValidationData}; +use polkadot_service::ProvideRuntimeApi; use sc_client_api::execution_extensions::ExecutionStrategies; use sc_network::{config::TransportConfig, multiaddr, NetworkService}; use sc_service::{ @@ -98,6 +100,9 @@ pub type Client = TFullClient< sc_executor::NativeElseWasmExecutor, >; +/// Transaction pool type used by the test service +pub type TransactionPool = Arc>; + /// Starts a `ServiceBuilder` for a full service. /// /// Use this macro if you don't actually need the full service, but just the builder in order to @@ -174,6 +179,7 @@ async fn start_node_impl( Arc, Arc>, RpcHandlers, + TransactionPool, )> where RB: Fn(Arc) -> Result, sc_service::Error> @@ -266,7 +272,7 @@ where let proposer_factory = sc_basic_authorship::ProposerFactory::with_proof_recording( task_manager.spawn_handle(), client.clone(), - transaction_pool, + transaction_pool.clone(), prometheus_registry.as_ref(), None, ); @@ -345,7 +351,7 @@ where start_network.start_network(); - Ok((task_manager, client, network, rpc_handlers)) + Ok((task_manager, client, network, rpc_handlers, transaction_pool)) } /// A Cumulus test node instance used for testing. @@ -361,6 +367,8 @@ pub struct TestNode { pub addr: MultiaddrWithPeerId, /// RPCHandlers to make RPC queries. pub rpc_handlers: RpcHandlers, + /// Node's transaction pool + pub transaction_pool: TransactionPool, } enum Consensus { @@ -519,7 +527,7 @@ impl TestNodeBuilder { format!("{} (relay chain)", relay_chain_config.network.node_name); 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, transaction_pool) = start_node_impl( parachain_config, self.collator_key, relay_chain_config, @@ -534,7 +542,7 @@ impl TestNodeBuilder { let peer_id = network.local_peer_id().clone(); let addr = MultiaddrWithPeerId { multiaddr, peer_id }; - TestNode { task_manager, client, network, addr, rpc_handlers } + TestNode { task_manager, client, network, addr, rpc_handlers, transaction_pool } } } @@ -651,7 +659,7 @@ impl TestNode { function: impl Into, caller: Sr25519Keyring, ) -> Result { - let extrinsic = construct_extrinsic(&*self.client, function, caller); + let extrinsic = construct_extrinsic(&*self.client, function, caller.pair(), Some(0)); self.rpc_handlers.send_transaction(extrinsic.into()).await } @@ -669,17 +677,27 @@ impl TestNode { } } +/// Fetch account nonce for key pair +pub fn fetch_nonce(client: &Client, account: sp_core::sr25519::Public) -> u32 { + let best_hash = client.chain_info().best_hash; + client + .runtime_api() + .account_nonce(&generic::BlockId::Hash(best_hash), account.into()) + .expect("Fetching account nonce works; qed") +} + /// Construct an extrinsic that can be applied to the test runtime. pub fn construct_extrinsic( client: &Client, function: impl Into, - caller: Sr25519Keyring, + caller: sp_core::sr25519::Pair, + nonce: Option, ) -> runtime::UncheckedExtrinsic { let function = function.into(); let current_block_hash = client.info().best_hash; let current_block = client.info().best_number.saturated_into(); let genesis_block = client.hash(0).unwrap().unwrap(); - let nonce = 0; + let nonce = nonce.unwrap_or_else(|| fetch_nonce(client, caller.public())); let period = runtime::BlockHashCount::get() .checked_next_power_of_two() .map(|c| c / 2)