From 2163988f54db807686ce0c0e55720fbf4acaaf26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20K=C3=B6cher?= Date: Sun, 25 Oct 2020 15:45:30 +0100 Subject: [PATCH] Improve and unify testing facilities (#1844) * Improve and unify testing facilities This improves the testing facilities by making the test client easier to use. It also removes code that is not required for the test client. Besides that it also moves the test service and test client under `node/test`. * Update Cargo.lock * Update node/test/client/src/block_builder.rs Co-authored-by: Andronik Ordian * Remove explicit lifetime annotation * Fix warnings and add extra `BlockBuilderExt` Co-authored-by: Andronik Ordian --- polkadot/Cargo.lock | 87 +++-- polkadot/Cargo.toml | 5 +- polkadot/node/service/Cargo.toml | 5 +- polkadot/node/service/src/grandpa_support.rs | 26 +- .../test}/client/Cargo.toml | 37 +- .../node/test/client/src/block_builder.rs | 102 ++++++ polkadot/node/test/client/src/lib.rs | 117 ++++++ .../{test-service => test/service}/Cargo.toml | 17 +- .../service}/src/chain_spec.rs | 20 +- .../{test-service => test/service}/src/lib.rs | 154 ++++---- .../service}/tests/build-blocks.rs | 0 .../service}/tests/call-function.rs | 2 +- .../runtime/test-runtime/client/src/lib.rs | 343 ------------------ polkadot/runtime/test-runtime/src/lib.rs | 13 + 14 files changed, 423 insertions(+), 505 deletions(-) rename polkadot/{runtime/test-runtime => node/test}/client/Cargo.toml (55%) create mode 100644 polkadot/node/test/client/src/block_builder.rs create mode 100644 polkadot/node/test/client/src/lib.rs rename polkadot/node/{test-service => test/service}/Cargo.toml (86%) rename polkadot/node/{test-service => test/service}/src/chain_spec.rs (91%) rename polkadot/node/{test-service => test/service}/src/lib.rs (73%) rename polkadot/node/{test-service => test/service}/tests/build-blocks.rs (100%) rename polkadot/node/{test-service => test/service}/tests/call-function.rs (96%) delete mode 100644 polkadot/runtime/test-runtime/client/src/lib.rs diff --git a/polkadot/Cargo.lock b/polkadot/Cargo.lock index ce62da6696..3505bf379a 100644 --- a/polkadot/Cargo.lock +++ b/polkadot/Cargo.lock @@ -464,7 +464,7 @@ dependencies = [ "cfg-if", "clang-sys", "clap", - "env_logger", + "env_logger 0.7.1", "lazy_static", "lazycell", "log 0.4.11", @@ -1312,7 +1312,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" dependencies = [ "atty", - "humantime", + "humantime 1.3.0", + "log 0.4.11", + "regex", + "termcolor", +] + +[[package]] +name = "env_logger" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54532e3223c5af90a6a757c90b5c5521564b07e5e7a958681bcd2afad421cdcd" +dependencies = [ + "atty", + "humantime 2.0.1", "log 0.4.11", "regex", "termcolor", @@ -1454,7 +1467,7 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b3937f028664bd0e13df401ba49a4567ccda587420365823242977f06609ed1" dependencies = [ - "env_logger", + "env_logger 0.7.1", "log 0.4.11", ] @@ -2268,6 +2281,12 @@ dependencies = [ "quick-error", ] +[[package]] +name = "humantime" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c1ad908cc71012b7bea4d0c53ba96a8cba9962f048fa68d143376143d863b7a" + [[package]] name = "hyper" version = "0.12.35" @@ -4763,7 +4782,7 @@ version = "0.1.0" dependencies = [ "assert_matches", "bitvec", - "env_logger", + "env_logger 0.7.1", "futures 0.3.5", "futures-timer 3.0.2", "log 0.4.11", @@ -4794,7 +4813,7 @@ dependencies = [ "assert_matches", "bitvec", "derive_more 0.99.11", - "env_logger", + "env_logger 0.7.1", "futures 0.3.5", "futures-timer 3.0.2", "log 0.4.11", @@ -4848,7 +4867,7 @@ version = "0.1.0" dependencies = [ "assert_matches", "derive_more 0.99.11", - "env_logger", + "env_logger 0.7.1", "futures 0.3.5", "futures-timer 3.0.2", "log 0.4.11", @@ -4932,7 +4951,7 @@ version = "0.1.0" dependencies = [ "assert_matches", "derive_more 0.99.11", - "env_logger", + "env_logger 0.7.1", "futures 0.3.5", "futures-timer 3.0.2", "kvdb", @@ -5177,7 +5196,7 @@ dependencies = [ "assert_matches", "async-trait", "derive_more 0.99.11", - "env_logger", + "env_logger 0.7.1", "futures 0.3.5", "futures-timer 3.0.2", "log 0.4.11", @@ -5485,7 +5504,7 @@ dependencies = [ name = "polkadot-service" version = "0.8.3" dependencies = [ - "env_logger", + "env_logger 0.8.1", "frame-benchmarking", "frame-system-rpc-runtime-api", "futures 0.3.5", @@ -5506,7 +5525,7 @@ dependencies = [ "polkadot-primitives", "polkadot-rpc", "polkadot-runtime", - "polkadot-test-runtime-client", + "polkadot-test-client", "rococo-v1-runtime", "sc-authority-discovery", "sc-block-builder", @@ -5581,6 +5600,29 @@ dependencies = [ "sp-core", ] +[[package]] +name = "polkadot-test-client" +version = "0.8.25" +dependencies = [ + "parity-scale-codec", + "polkadot-primitives", + "polkadot-test-runtime", + "polkadot-test-service", + "sc-block-builder", + "sc-consensus", + "sc-service", + "sp-api", + "sp-blockchain", + "sp-consensus", + "sp-core", + "sp-inherents", + "sp-keyring", + "sp-runtime", + "sp-state-machine", + "sp-timestamp", + "substrate-test-client", +] + [[package]] name = "polkadot-test-runtime" version = "0.8.25" @@ -5640,32 +5682,9 @@ dependencies = [ "tiny-keccak 1.5.0", ] -[[package]] -name = "polkadot-test-runtime-client" -version = "2.0.0" -dependencies = [ - "futures 0.3.5", - "pallet-timestamp", - "parity-scale-codec", - "polkadot-primitives", - "polkadot-runtime-common", - "polkadot-test-runtime", - "polkadot-test-service", - "sc-block-builder", - "sc-client-api", - "sc-consensus", - "sc-light", - "sc-service", - "sp-api", - "sp-blockchain", - "sp-core", - "sp-runtime", - "substrate-test-client", -] - [[package]] name = "polkadot-test-service" -version = "0.8.2" +version = "0.8.25" dependencies = [ "frame-benchmarking", "frame-system", diff --git a/polkadot/Cargo.toml b/polkadot/Cargo.toml index 9217bd442f..2b48f30bc1 100644 --- a/polkadot/Cargo.toml +++ b/polkadot/Cargo.toml @@ -35,7 +35,6 @@ members = [ "runtime/rococo-v1", "runtime/westend", "runtime/test-runtime", - "runtime/test-runtime/client", "statement-table", "validation", "xcm", @@ -64,8 +63,8 @@ members = [ "node/subsystem", "node/subsystem-test-helpers", "node/subsystem-util", - "node/test-service", - + "node/test/client", + "node/test/service", "parachain/test-parachains", "parachain/test-parachains/adder", ] diff --git a/polkadot/node/service/Cargo.toml b/polkadot/node/service/Cargo.toml index 77040e9ed7..6029b849c6 100644 --- a/polkadot/node/service/Cargo.toml +++ b/polkadot/node/service/Cargo.toml @@ -73,9 +73,8 @@ rococo-runtime = { package = "rococo-v1-runtime", path = "../../runtime/rococo-v westend-runtime = { path = "../../runtime/westend" } [dev-dependencies] -polkadot-test-runtime-client = { path = "../../runtime/test-runtime/client" } -sc-block-builder = { git = "https://github.com/paritytech/substrate", branch = "master" } -env_logger = "0.7.0" +polkadot-test-client = { path = "../test/client" } +env_logger = "0.8.1" [features] default = ["db", "full-node"] diff --git a/polkadot/node/service/src/grandpa_support.rs b/polkadot/node/service/src/grandpa_support.rs index a683052f54..b34044469e 100644 --- a/polkadot/node/service/src/grandpa_support.rs +++ b/polkadot/node/service/src/grandpa_support.rs @@ -232,37 +232,29 @@ pub(crate) fn kusama_hard_forks() -> Vec<( #[cfg(test)] mod tests { use grandpa::VotingRule; - use polkadot_test_runtime_client::prelude::*; - use polkadot_test_runtime_client::sp_consensus::BlockOrigin; - use sc_block_builder::BlockBuilderProvider; + use polkadot_test_client::{ + TestClientBuilder, TestClientBuilderExt, DefaultTestClientBuilderExt, InitPolkadotBlockBuilder, + ClientBlockImportExt, + }; use sp_blockchain::HeaderBackend; - use sp_runtime::generic::BlockId; - use sp_runtime::traits::Header; + use sp_runtime::{generic::BlockId, traits::Header}; + use consensus_common::BlockOrigin; use std::sync::Arc; #[test] fn grandpa_pause_voting_rule_works() { let _ = env_logger::try_init(); - let client = Arc::new(polkadot_test_runtime_client::new()); + let client = Arc::new(TestClientBuilder::new().build()); let mut push_blocks = { let mut client = client.clone(); - let mut base = 0; move |n| { - for i in 0..n { - let mut builder = client.new_block(Default::default()).unwrap(); - - for extrinsic in polkadot_test_runtime_client::needed_extrinsics(base + i) { - builder.push(extrinsic).unwrap() - } - - let block = builder.build().unwrap().block; + for _ in 0..n { + let block = client.init_polkadot_block_builder().build().unwrap().block; client.import(BlockOrigin::Own, block).unwrap(); } - - base += n; } }; diff --git a/polkadot/runtime/test-runtime/client/Cargo.toml b/polkadot/node/test/client/Cargo.toml similarity index 55% rename from polkadot/runtime/test-runtime/client/Cargo.toml rename to polkadot/node/test/client/Cargo.toml index ce00e4a51d..60d5184a51 100644 --- a/polkadot/runtime/test-runtime/client/Cargo.toml +++ b/polkadot/node/test/client/Cargo.toml @@ -1,29 +1,30 @@ [package] -name = "polkadot-test-runtime-client" -version = "2.0.0" +name = "polkadot-test-client" +version = "0.8.25" authors = ["Parity Technologies "] edition = "2018" -license = "GPL-3.0" [dependencies] -futures = "0.3.1" -codec = { package = "parity-scale-codec", version = "1.3.4" } +codec = { package = "parity-scale-codec", version = "1.3.4", default-features = false, features = ["derive"] } # Polkadot dependencies +polkadot-test-runtime = { path = "../../../runtime/test-runtime" } +polkadot-test-service = { path = "../service" } polkadot-primitives = { path = "../../../primitives" } -polkadot-runtime-common = { path = "../../common" } -polkadot-test-runtime = { path = ".." } -polkadot-test-service = { path = "../../../node/test-service" } # Substrate dependencies -pallet-timestamp = { git = "https://github.com/paritytech/substrate", branch = "master" } -sc-block-builder = { git = "https://github.com/paritytech/substrate", branch = "master" } -sc-client-api = { git = "https://github.com/paritytech/substrate", branch = "master" } -sc-consensus = { git = "https://github.com/paritytech/substrate", branch = "master" } -sc-light = { git = "https://github.com/paritytech/substrate", branch = "master" } -sc-service = { git = "https://github.com/paritytech/substrate", branch = "master", features = ["test-helpers"], default-features = false } -sp-api = { git = "https://github.com/paritytech/substrate", branch = "master" } -sp-blockchain = { git = "https://github.com/paritytech/substrate", branch = "master" } -sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" } -sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master" } substrate-test-client = { git = "https://github.com/paritytech/substrate", branch = "master" } +sc-service = { git = "https://github.com/paritytech/substrate", branch = "master" } +sc-block-builder = { git = "https://github.com/paritytech/substrate", branch = "master" } +sc-consensus = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-blockchain = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-inherents = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-api = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-timestamp = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-consensus = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-state-machine = { git = "https://github.com/paritytech/substrate", branch = "master" } + +[dev-dependencies] +sp-keyring = { git = "https://github.com/paritytech/substrate", branch = "master" } diff --git a/polkadot/node/test/client/src/block_builder.rs b/polkadot/node/test/client/src/block_builder.rs new file mode 100644 index 0000000000..f377bf6c16 --- /dev/null +++ b/polkadot/node/test/client/src/block_builder.rs @@ -0,0 +1,102 @@ +// Copyright 2020 Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot 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. + +// Polkadot 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 Polkadot. If not, see . + +use crate::{Client, FullBackend}; +use polkadot_test_runtime::{GetLastTimestamp, UncheckedExtrinsic}; +use polkadot_primitives::v1::Block; +use sp_runtime::generic::BlockId; +use sp_api::ProvideRuntimeApi; +use sc_block_builder::{BlockBuilderProvider, BlockBuilder}; +use sp_state_machine::BasicExternalities; +use codec::{Encode, Decode}; + +/// An extension for the test client to init a Polkadot specific block builder. +pub trait InitPolkadotBlockBuilder { + /// Init a Polkadot specific block builder that works for the test runtime. + /// + /// This will automatically create and push the inherents for you to make the block valid for the test runtime. + fn init_polkadot_block_builder(&self) -> sc_block_builder::BlockBuilder; + + /// Init a Polkadot specific block builder at a specific block that works for the test runtime. + /// + /// Same as [`InitPolkadotBlockBuilder::init_polkadot_block_builder`] besides that it takes a [`BlockId`] to say + /// which should be the parent block of the block that is being build. + fn init_polkadot_block_builder_at( + &self, + at: &BlockId, + ) -> sc_block_builder::BlockBuilder; +} + +impl InitPolkadotBlockBuilder for Client { + fn init_polkadot_block_builder( + &self, + ) -> BlockBuilder { + let chain_info = self.chain_info(); + self.init_polkadot_block_builder_at(&BlockId::Hash(chain_info.best_hash)) + } + + fn init_polkadot_block_builder_at( + &self, + at: &BlockId, + ) -> BlockBuilder { + let mut block_builder = self.new_block_at(at, Default::default(), false) + .expect("Creates new block builder for test runtime"); + + let mut inherent_data = sp_inherents::InherentData::new(); + let last_timestamp = self + .runtime_api() + .get_last_timestamp(&at) + .expect("Get last timestamp"); + + // `MinimumPeriod` is a storage parameter type that requires externalities to access the value. + let minimum_period = BasicExternalities::new_empty() + .execute_with(|| polkadot_test_runtime::MinimumPeriod::get()); + + let timestamp = last_timestamp + minimum_period; + + inherent_data + .put_data(sp_timestamp::INHERENT_IDENTIFIER, ×tamp) + .expect("Put timestamp failed"); + + let inherents = block_builder.create_inherents(inherent_data).expect("Creates inherents"); + + inherents.into_iter().for_each(|ext| block_builder.push(ext).expect("Pushes inherent")); + + block_builder + } +} + +/// Polkadot specific extensions for the [`BlockBuilder`]. +pub trait BlockBuilderExt { + /// Push a Polkadot test runtime specific extrinsic to the block. + /// + /// This will internally use the [`BlockBuilder::push`] method, but this method expects a opaque extrinsic. So, + /// we provide this wrapper which converts a test runtime specific extrinsic to a opaque extrinsic and pushes it to + /// the block. + /// + /// Returns the result of the application of the extrinsic. + fn push_polkadot_extrinsic(&mut self, ext: UncheckedExtrinsic) -> Result<(), sp_blockchain::Error>; +} + +impl BlockBuilderExt for BlockBuilder<'_, Block, Client, FullBackend> { + fn push_polkadot_extrinsic(&mut self, ext: UncheckedExtrinsic) -> Result<(), sp_blockchain::Error> { + let encoded = ext.encode(); + self.push( + Decode::decode(&mut &encoded[..]) + .expect("The runtime specific extrinsic always decodes to an opaque extrinsic; qed"), + ) + } +} diff --git a/polkadot/node/test/client/src/lib.rs b/polkadot/node/test/client/src/lib.rs new file mode 100644 index 0000000000..d3312e06ca --- /dev/null +++ b/polkadot/node/test/client/src/lib.rs @@ -0,0 +1,117 @@ +// Copyright 2020 Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot 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. + +// Polkadot 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 Polkadot. If not, see . + +//! A Polkadot test client. +//! +//! This test client is using the Polkadot test runtime. + +mod block_builder; + +use polkadot_primitives::v1::Block; +use sc_service::client; +use sp_core::storage::Storage; +use sp_runtime::BuildStorage; + +pub use block_builder::*; +pub use substrate_test_client::*; +pub use polkadot_test_service::{ + Client, construct_extrinsic, construct_transfer_extrinsic, PolkadotTestExecutor, FullBackend, +}; +pub use polkadot_test_runtime as runtime; + +/// Test client executor. +pub type Executor = client::LocalCallExecutor>; + +/// Test client builder for Polkadot. +pub type TestClientBuilder = substrate_test_client::TestClientBuilder; + +/// LongestChain type for the test runtime/client. +pub type LongestChain = sc_consensus::LongestChain; + +/// Parameters of test-client builder with test-runtime. +#[derive(Default)] +pub struct GenesisParameters; + +impl substrate_test_client::GenesisInit for GenesisParameters { + fn genesis_storage(&self) -> Storage { + polkadot_test_service::chain_spec::polkadot_local_testnet_genesis() + .build_storage() + .expect("Builds test runtime genesis storage") + } +} + +/// A `test-runtime` extensions to `TestClientBuilder`. +pub trait TestClientBuilderExt: Sized { + /// Build the test client. + fn build(self) -> Client { + self.build_with_longest_chain().0 + } + + /// Build the test client and longest chain selector. + fn build_with_longest_chain(self) -> (Client, LongestChain); +} + +impl TestClientBuilderExt for TestClientBuilder { + fn build_with_longest_chain(self) -> (Client, LongestChain) { + self.build_with_native_executor(None) + } +} + +/// A `TestClientBuilder` with default backend and executor. +pub trait DefaultTestClientBuilderExt: Sized { + /// Create new `TestClientBuilder` + fn new() -> Self; +} + +impl DefaultTestClientBuilderExt for TestClientBuilder { + fn new() -> Self { + Self::with_default_backend() + } +} + +#[cfg(test)] +mod tests{ + use super::*; + use sp_consensus::BlockOrigin; + + #[test] + fn ensure_test_client_can_build_and_import_block() { + let mut client = TestClientBuilder::new().build(); + + let block_builder = client.init_polkadot_block_builder(); + let block = block_builder.build().expect("Finalizes the block").block; + + client.import(BlockOrigin::Own, block).expect("Imports the block"); + } + + #[test] + fn ensure_test_client_can_push_extrinsic() { + let mut client = TestClientBuilder::new().build(); + + let transfer = construct_transfer_extrinsic( + &client, + sp_keyring::Sr25519Keyring::Alice, + sp_keyring::Sr25519Keyring::Bob, + 1000, + ); + let mut block_builder = client.init_polkadot_block_builder(); + block_builder.push_polkadot_extrinsic(transfer).expect("Pushes extrinsic"); + + let block = block_builder.build().expect("Finalizes the block").block; + + client.import(BlockOrigin::Own, block).expect("Imports the block"); + } +} diff --git a/polkadot/node/test-service/Cargo.toml b/polkadot/node/test/service/Cargo.toml similarity index 86% rename from polkadot/node/test-service/Cargo.toml rename to polkadot/node/test/service/Cargo.toml index 4b1a0a922e..d139f470a7 100644 --- a/polkadot/node/test-service/Cargo.toml +++ b/polkadot/node/test/service/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "polkadot-test-service" -version = "0.8.2" +version = "0.8.25" authors = ["Parity Technologies "] edition = "2018" @@ -13,13 +13,13 @@ rand = "0.7.3" tempfile = "3.1.0" # Polkadot dependencies -polkadot-overseer = { path = "../overseer" } -polkadot-primitives = { path = "../../primitives" } -polkadot-rpc = { path = "../../rpc" } -polkadot-runtime-common = { path = "../../runtime/common" } -polkadot-service = { path = "../service" } -polkadot-test-runtime = { path = "../../runtime/test-runtime" } -polkadot-runtime-parachains = { path = "../../runtime/parachains" } +polkadot-overseer = { path = "../../overseer" } +polkadot-primitives = { path = "../../../primitives" } +polkadot-rpc = { path = "../../../rpc" } +polkadot-runtime-common = { path = "../../../runtime/common" } +polkadot-service = { path = "../../service" } +polkadot-test-runtime = { path = "../../../runtime/test-runtime" } +polkadot-runtime-parachains = { path = "../../../runtime/parachains" } # Substrate dependencies authority-discovery = { package = "sc-authority-discovery", git = "https://github.com/paritytech/substrate", branch = "master" } @@ -32,6 +32,7 @@ grandpa = { package = "sc-finality-grandpa", git = "https://github.com/paritytec grandpa_primitives = { package = "sp-finality-grandpa", git = "https://github.com/paritytech/substrate", branch = "master" } inherents = { package = "sp-inherents", git = "https://github.com/paritytech/substrate", branch = "master" } pallet-staking = { git = "https://github.com/paritytech/substrate", branch = "master" } +pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "master" } pallet-transaction-payment = { git = "https://github.com/paritytech/substrate", branch = "master" } sc-chain-spec = { git = "https://github.com/paritytech/substrate", branch = "master" } sc-cli = { git = "https://github.com/paritytech/substrate", branch = "master" } diff --git a/polkadot/node/test-service/src/chain_spec.rs b/polkadot/node/test/service/src/chain_spec.rs similarity index 91% rename from polkadot/node/test-service/src/chain_spec.rs rename to polkadot/node/test/service/src/chain_spec.rs index 93a614b7d3..e677d0b9ac 100644 --- a/polkadot/node/test-service/src/chain_spec.rs +++ b/polkadot/node/test/service/src/chain_spec.rs @@ -14,6 +14,8 @@ // You should have received a copy of the GNU General Public License // along with Polkadot. If not, see . +//! Chain specifications for the test runtime. + use babe_primitives::AuthorityId as BabeId; use grandpa::AuthorityId as GrandpaId; use pallet_staking::Forcing; @@ -21,22 +23,22 @@ use polkadot_primitives::v0::{ValidatorId, AccountId}; use polkadot_service::chain_spec::{get_account_id_from_seed, get_from_seed, Extensions}; use polkadot_test_runtime::constants::currency::DOTS; use sc_chain_spec::{ChainSpec, ChainType}; -use sp_core::{sr25519, ChangesTrieConfiguration}; +use sp_core::sr25519; use sp_runtime::Perbill; const DEFAULT_PROTOCOL_ID: &str = "dot"; -/// The `ChainSpec parametrised for polkadot runtime`. +/// The `ChainSpec` parametrized for polkadot test runtime. pub type PolkadotChainSpec = service::GenericChainSpec; -/// Polkadot local testnet config (multivalidator Alice + Bob) +/// Local testnet config (multivalidator Alice + Bob) pub fn polkadot_local_testnet_config() -> PolkadotChainSpec { PolkadotChainSpec::from_genesis( "Local Testnet", "local_testnet", ChainType::Local, - || polkadot_local_testnet_genesis(None), + || polkadot_local_testnet_genesis(), vec![], None, Some(DEFAULT_PROTOCOL_ID), @@ -45,10 +47,8 @@ pub fn polkadot_local_testnet_config() -> PolkadotChainSpec { ) } -/// Polkadot local testnet genesis config (multivalidator Alice + Bob) -pub fn polkadot_local_testnet_genesis( - changes_trie_config: Option, -) -> polkadot_test_runtime::GenesisConfig { +/// Local testnet genesis config (multivalidator Alice + Bob) +pub fn polkadot_local_testnet_genesis() -> polkadot_test_runtime::GenesisConfig { polkadot_testnet_genesis( vec![ get_authority_keys_from_seed("Alice"), @@ -57,7 +57,6 @@ pub fn polkadot_local_testnet_genesis( ], get_account_id_from_seed::("Alice"), None, - changes_trie_config, ) } @@ -96,7 +95,6 @@ fn polkadot_testnet_genesis( initial_authorities: Vec<(AccountId, AccountId, BabeId, GrandpaId, ValidatorId)>, root_key: AccountId, endowed_accounts: Option>, - changes_trie_config: Option, ) -> polkadot_test_runtime::GenesisConfig { use polkadot_test_runtime as polkadot; @@ -108,7 +106,7 @@ fn polkadot_testnet_genesis( polkadot::GenesisConfig { frame_system: Some(polkadot::SystemConfig { code: polkadot::WASM_BINARY.expect("Wasm binary must be built for testing").to_vec(), - changes_trie_config, + ..Default::default() }), pallet_indices: Some(polkadot::IndicesConfig { indices: vec![] }), pallet_balances: Some(polkadot::BalancesConfig { diff --git a/polkadot/node/test-service/src/lib.rs b/polkadot/node/test/service/src/lib.rs similarity index 73% rename from polkadot/node/test-service/src/lib.rs rename to polkadot/node/test/service/src/lib.rs index f17ab6bca7..106e3ff724 100644 --- a/polkadot/node/test-service/src/lib.rs +++ b/polkadot/node/test/service/src/lib.rs @@ -18,20 +18,20 @@ #![warn(missing_docs)] -mod chain_spec; +pub mod chain_spec; pub use chain_spec::*; use futures::future::Future; use polkadot_overseer::OverseerHandler; -use polkadot_primitives::v1::{Block, Id as ParaId, HeadData, ValidationCode}; +use polkadot_primitives::v1::{Id as ParaId, HeadData, ValidationCode, Balance}; use polkadot_runtime_common::BlockHashCount; use polkadot_service::{ - new_full, NewFull, FullClient, AbstractClient, ClientHandle, ExecuteWithClient, + new_full, NewFull, FullClient, ClientHandle, ExecuteWithClient, }; -use polkadot_test_runtime::{Runtime, SignedExtra, SignedPayload, VERSION, ParasSudoWrapperCall}; +use polkadot_test_runtime::{Runtime, SignedExtra, SignedPayload, VERSION, ParasSudoWrapperCall, UncheckedExtrinsic}; use polkadot_runtime_parachains::paras::ParaGenesisArgs; use sc_chain_spec::ChainSpec; -use sc_client_api::{execution_extensions::ExecutionStrategies, BlockchainEvents}; +use sc_client_api::execution_extensions::ExecutionStrategies; use sc_executor::native_executor_instance; use sc_informant::OutputFormat; use sc_network::{ @@ -43,11 +43,11 @@ use service::{ error::Error as ServiceError, RpcHandlers, TaskExecutor, TaskManager, }; -use service::{BasePath, Configuration, Role, TFullBackend}; +use service::{BasePath, Configuration, Role}; use sp_arithmetic::traits::SaturatedConversion; use sp_blockchain::HeaderBackend; use sp_keyring::Sr25519Keyring; -use sp_runtime::{codec::Encode, generic}; +use sp_runtime::{codec::Encode, generic, traits::IdentifyAccount, MultiSigner}; use sp_state_machine::BasicExternalities; use std::sync::Arc; use substrate_test_client::{BlockchainEventsExt, RpcHandlersExt, RpcTransactionOutput, RpcTransactionError}; @@ -59,13 +59,18 @@ native_executor_instance!( frame_benchmarking::benchmarking::HostFunctions, ); +/// The client type being used by the test service. +pub type Client = FullClient; + +pub use polkadot_service::FullBackend; + /// Create a new Polkadot test service for a full node. #[sc_cli::prefix_logs_with(config.network.node_name.as_str())] pub fn polkadot_test_new_full( config: Configuration, authority_discovery_disabled: bool, ) -> Result< - NewFull>>, + NewFull>, ServiceError, > { new_full::( @@ -76,7 +81,7 @@ pub fn polkadot_test_new_full( } /// A wrapper for the test client that implements `ClientHandle`. -pub struct TestClient(pub Arc>); +pub struct TestClient(pub Arc); impl ClientHandle for TestClient { fn execute_with(&self, t: T) -> T::Output { @@ -195,10 +200,7 @@ pub fn run_test_node( key: Sr25519Keyring, storage_update_func: impl Fn(), boot_nodes: Vec, -) -> PolkadotTestNode< - TaskManager, - impl AbstractClient>, -> { +) -> PolkadotTestNode { let config = node_config(storage_update_func, task_executor, key, boot_nodes); let multiaddr = config.network.listen_addresses[0].clone(); let authority_discovery_disabled = true; @@ -219,11 +221,11 @@ pub fn run_test_node( } /// A Polkadot test node instance used for testing. -pub struct PolkadotTestNode { +pub struct PolkadotTestNode { /// TaskManager's instance. - pub task_manager: S, + pub task_manager: TaskManager, /// Client's instance. - pub client: Arc, + pub client: Arc, /// The overseer handler. pub overseer_handler: OverseerHandler, /// The `MultiaddrWithPeerId` to this node. This is useful if you want to pass it as "boot node" to other nodes. @@ -232,55 +234,14 @@ pub struct PolkadotTestNode { pub rpc_handlers: RpcHandlers, } -impl PolkadotTestNode -where - C: HeaderBackend, -{ - /// Send a transaction through RPCHandlers to call a function. - pub async fn call_function( +impl PolkadotTestNode { + /// Send an extrinsic to this node. + pub async fn send_extrinsic( &self, function: impl Into, caller: Sr25519Keyring, ) -> Result { - let function = function.into(); - let current_block_hash = self.client.info().best_hash; - let current_block = self.client.info().best_number.saturated_into(); - let genesis_block = self.client.hash(0).unwrap().unwrap(); - let nonce = 0; - let period = BlockHashCount::get() - .checked_next_power_of_two() - .map(|c| c / 2) - .unwrap_or(2) as u64; - let tip = 0; - let extra: SignedExtra = ( - frame_system::CheckSpecVersion::::new(), - frame_system::CheckTxVersion::::new(), - frame_system::CheckGenesis::::new(), - frame_system::CheckEra::::from(generic::Era::mortal(period, current_block)), - frame_system::CheckNonce::::from(nonce), - frame_system::CheckWeight::::new(), - pallet_transaction_payment::ChargeTransactionPayment::::from(tip), - ); - let raw_payload = SignedPayload::from_raw( - function.clone(), - extra.clone(), - ( - VERSION.spec_version, - VERSION.transaction_version, - genesis_block, - current_block_hash, - (), - (), - (), - ), - ); - let signature = raw_payload.using_encoded(|e| caller.sign(e)); - let extrinsic = polkadot_test_runtime::UncheckedExtrinsic::new_signed( - function.clone(), - polkadot_test_runtime::Address::Id(caller.public().into()), - polkadot_primitives::v0::Signature::Sr25519(signature.clone()), - extra.clone(), - ); + let extrinsic = construct_extrinsic(&*self.client, function, caller); self.rpc_handlers.send_transaction(extrinsic.into()).await } @@ -301,17 +262,76 @@ where }, ); - self.call_function(call, Sr25519Keyring::Alice).await.map(drop) + self.send_extrinsic(call, Sr25519Keyring::Alice).await.map(drop) } -} -impl PolkadotTestNode -where - C: BlockchainEvents, -{ /// 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 the test execution. pub fn wait_for_blocks(&self, count: usize) -> impl Future { self.client.wait_for_blocks(count) } } + +/// Construct an extrinsic that can be applied to the test runtime. +pub fn construct_extrinsic( + client: &Client, + function: impl Into, + caller: Sr25519Keyring, +) -> 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 period = BlockHashCount::get() + .checked_next_power_of_two() + .map(|c| c / 2) + .unwrap_or(2) as u64; + let tip = 0; + let extra: SignedExtra = ( + frame_system::CheckSpecVersion::::new(), + frame_system::CheckTxVersion::::new(), + frame_system::CheckGenesis::::new(), + frame_system::CheckEra::::from(generic::Era::mortal(period, current_block)), + frame_system::CheckNonce::::from(nonce), + frame_system::CheckWeight::::new(), + pallet_transaction_payment::ChargeTransactionPayment::::from(tip), + ); + let raw_payload = SignedPayload::from_raw( + function.clone(), + extra.clone(), + ( + VERSION.spec_version, + VERSION.transaction_version, + genesis_block, + current_block_hash, + (), + (), + (), + ), + ); + let signature = raw_payload.using_encoded(|e| caller.sign(e)); + UncheckedExtrinsic::new_signed( + function.clone(), + polkadot_test_runtime::Address::Id(caller.public().into()), + polkadot_primitives::v0::Signature::Sr25519(signature.clone()), + extra.clone(), + ) +} + +/// Construct a transfer extrinsic. +pub fn construct_transfer_extrinsic( + client: &Client, + origin: sp_keyring::AccountKeyring, + dest: sp_keyring::AccountKeyring, + value: Balance, +) -> UncheckedExtrinsic { + let function = polkadot_test_runtime::Call::Balances( + pallet_balances::Call::transfer( + MultiSigner::from(dest.public()).into_account().into(), + value, + ), + ); + + construct_extrinsic(client, function, origin) +} diff --git a/polkadot/node/test-service/tests/build-blocks.rs b/polkadot/node/test/service/tests/build-blocks.rs similarity index 100% rename from polkadot/node/test-service/tests/build-blocks.rs rename to polkadot/node/test/service/tests/build-blocks.rs diff --git a/polkadot/node/test-service/tests/call-function.rs b/polkadot/node/test/service/tests/call-function.rs similarity index 96% rename from polkadot/node/test-service/tests/call-function.rs rename to polkadot/node/test/service/tests/call-function.rs index af62bcf5db..184755627f 100644 --- a/polkadot/node/test-service/tests/call-function.rs +++ b/polkadot/node/test/service/tests/call-function.rs @@ -26,7 +26,7 @@ async fn call_function_actually_work(task_executor: TaskExecutor) { Default::default(), 1, )); - let output = alice.call_function(function, Bob).await.unwrap(); + let output = alice.send_extrinsic(function, Bob).await.unwrap(); let res = output.result.expect("return value expected"); let json = serde_json::from_str::(res.as_str()).expect("valid JSON"); diff --git a/polkadot/runtime/test-runtime/client/src/lib.rs b/polkadot/runtime/test-runtime/client/src/lib.rs deleted file mode 100644 index 698de3d3cf..0000000000 --- a/polkadot/runtime/test-runtime/client/src/lib.rs +++ /dev/null @@ -1,343 +0,0 @@ -// Copyright 2020 Parity Technologies (UK) Ltd. -// This file is part of Polkadot. - -// Polkadot 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. - -// Polkadot 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 Polkadot. If not, see . - -//! Client testing utilities. - -#![warn(missing_docs)] - -use std::sync::Arc; -use std::collections::BTreeMap; -use std::convert::TryFrom; -pub use substrate_test_client::*; -pub use polkadot_test_runtime as runtime; - -use sp_core::{ChangesTrieConfiguration, map, twox_128}; -use sp_core::storage::{ChildInfo, Storage, StorageChild}; -use polkadot_test_runtime::GenesisConfig; -use polkadot_test_service::polkadot_local_testnet_genesis; -use sp_runtime::{ - traits::{Block as BlockT, Header as HeaderT, Hash as HashT, HashFor}, - BuildStorage, -}; -use sc_consensus::LongestChain; -use sc_client_api::light::{RemoteCallRequest, RemoteBodyRequest}; -use sc_service::client::{ - genesis, Client as SubstrateClient, LocalCallExecutor -}; -use sc_light::{ - call_executor::GenesisCallExecutor, backend as light_backend, - new_light_blockchain, new_light_backend, -}; - -/// A prelude to import in tests. -pub mod prelude { - // Trait extensions - pub use super::{ClientExt, ClientBlockImportExt}; - // Client structs - pub use super::{ - TestClient, TestClientBuilder, Backend, LightBackend, - Executor, LightExecutor, LocalExecutor, NativeExecutor, WasmExecutionMethod, - }; - // Keyring - pub use super::{AccountKeyring, Sr25519Keyring}; -} - -sc_executor::native_executor_instance! { - pub LocalExecutor, - polkadot_test_runtime::api::dispatch, - polkadot_test_runtime::native_version, -} - -/// Test client database backend. -pub type Backend = substrate_test_client::Backend; - -/// Test client executor. -pub type Executor = LocalCallExecutor< - Backend, - NativeExecutor, ->; - -/// Test client light database backend. -pub type LightBackend = substrate_test_client::LightBackend; - -/// Test client light executor. -pub type LightExecutor = GenesisCallExecutor< - LightBackend, - LocalCallExecutor< - light_backend::Backend< - sc_client_db::light::LightStorage, - HashFor - >, - NativeExecutor - > ->; - -/// Parameters of test-client builder with test-runtime. -#[derive(Default)] -pub struct GenesisParameters { - changes_trie_config: Option, - extra_storage: Storage, -} - -impl GenesisParameters { - fn genesis_config(&self) -> GenesisConfig { - let config = polkadot_local_testnet_genesis(self.changes_trie_config.clone()); - config.assimilate_storage(&mut self.extra_storage.clone()).expect("Adding `system::GensisConfig` to the genesis"); - - config - } -} - -fn additional_storage_with_genesis(genesis_block: &polkadot_test_runtime::Block) -> BTreeMap, Vec> { - map![ - twox_128(&b"latest"[..]).to_vec() => genesis_block.hash().as_fixed_bytes().to_vec() - ] -} - -impl substrate_test_client::GenesisInit for GenesisParameters { - fn genesis_storage(&self) -> Storage { - use codec::Encode; - - let mut storage = self.genesis_config().build_storage().unwrap(); - - let child_roots = storage.children_default.iter().map(|(sk, child_content)| { - let state_root = <<::Header as HeaderT>::Hashing as HashT>::trie_root( - child_content.data.clone().into_iter().collect() - ); - (sk.clone(), state_root.encode()) - }); - let state_root = <<::Header as HeaderT>::Hashing as HashT>::trie_root( - storage.top.clone().into_iter().chain(child_roots).collect() - ); - let block: runtime::Block = genesis::construct_genesis_block(state_root); - storage.top.extend(additional_storage_with_genesis(&block)); - - storage - } -} - -/// A `TestClient` with `test-runtime` builder. -pub type TestClientBuilder = substrate_test_client::TestClientBuilder< - polkadot_test_runtime::Block, - E, - B, - GenesisParameters, ->; - -/// Test client type with `LocalExecutor` and generic Backend. -pub type Client = SubstrateClient< - B, - LocalCallExecutor>, - polkadot_test_runtime::Block, - polkadot_test_runtime::RuntimeApi, ->; - -/// A test client with default backend. -pub type TestClient = Client; - -/// A `TestClientBuilder` with default backend and executor. -pub trait DefaultTestClientBuilderExt: Sized { - /// Create new `TestClientBuilder` - fn new() -> Self; -} - -impl DefaultTestClientBuilderExt for TestClientBuilder { - fn new() -> Self { - Self::with_default_backend() - } -} - -/// A `test-runtime` extensions to `TestClientBuilder`. -pub trait TestClientBuilderExt: Sized { - /// Returns a mutable reference to the genesis parameters. - fn genesis_init_mut(&mut self) -> &mut GenesisParameters; - - /// Set changes trie configuration for genesis. - fn changes_trie_config(mut self, config: Option) -> Self { - self.genesis_init_mut().changes_trie_config = config; - self - } - - /// Add an extra value into the genesis storage. - /// - /// # Panics - /// - /// Panics if the key is empty. - fn add_extra_child_storage>, K: Into>, V: Into>>( - mut self, - storage_key: SK, - child_info: ChildInfo, - key: K, - value: V, - ) -> Self { - let storage_key = storage_key.into(); - let key = key.into(); - assert!(!storage_key.is_empty()); - assert!(!key.is_empty()); - self.genesis_init_mut().extra_storage.children_default - .entry(storage_key) - .or_insert_with(|| StorageChild { - data: Default::default(), - child_info: child_info.to_owned(), - }).data.insert(key, value.into()); - self - } - - /// Add an extra child value into the genesis storage. - /// - /// # Panics - /// - /// Panics if the key is empty. - fn add_extra_storage>, V: Into>>(mut self, key: K, value: V) -> Self { - let key = key.into(); - assert!(!key.is_empty()); - self.genesis_init_mut().extra_storage.top.insert(key, value.into()); - self - } - - /// Build the test client. - fn build(self) -> Client { - self.build_with_longest_chain().0 - } - - /// Build the test client and longest chain selector. - fn build_with_longest_chain(self) -> (Client, LongestChain); - - /// Build the test client and the backend. - fn build_with_backend(self) -> (Client, Arc); -} - -impl TestClientBuilderExt for TestClientBuilder< - LocalCallExecutor>, - Backend -> { - fn genesis_init_mut(&mut self) -> &mut GenesisParameters { - Self::genesis_init_mut(self) - } - - fn build_with_longest_chain(self) -> (Client, LongestChain) { - self.build_with_native_executor(None) - } - - fn build_with_backend(self) -> (Client, Arc) { - let backend = self.backend(); - (self.build_with_native_executor(None).0, backend) - } -} - -/// Type of optional fetch callback. -type MaybeFetcherCallback = Option Result + Send + Sync>>; - -/// Implementation of light client fetcher used in tests. -#[derive(Default)] -pub struct LightFetcher { - call: MaybeFetcherCallback, Vec>, - body: MaybeFetcherCallback, Vec>, -} - -impl LightFetcher { - /// Sets remote call callback. - pub fn with_remote_call( - self, - call: MaybeFetcherCallback, Vec>, - ) -> Self { - LightFetcher { - call, - body: self.body, - } - } - - /// Sets remote body callback. - pub fn with_remote_body( - self, - body: MaybeFetcherCallback, Vec>, - ) -> Self { - LightFetcher { - call: self.call, - body, - } - } -} - -/// Creates new client instance used for tests. -pub fn new() -> Client { - TestClientBuilder::new().build() -} - -/// Creates new light client instance used for tests. -pub fn new_light() -> ( - SubstrateClient< - LightBackend, - LightExecutor, - polkadot_test_runtime::Block, - polkadot_test_runtime::RuntimeApi - >, - Arc, -) { - - let storage = sc_client_db::light::LightStorage::new_test(); - let blockchain =new_light_blockchain(storage); - let backend = new_light_backend(blockchain.clone()); - let executor = new_native_executor(); - let local_call_executor = LocalCallExecutor::new( - backend.clone(), - executor, - Box::new(sp_core::testing::TaskExecutor::new()), - Default::default() - ); - let call_executor = LightExecutor::new( - backend.clone(), - local_call_executor, - ); - - ( - TestClientBuilder::with_backend(backend.clone()) - .build_with_executor(call_executor) - .0, - backend, - ) -} - -/// Creates new light client fetcher used for tests. -pub fn new_light_fetcher() -> LightFetcher { - LightFetcher::default() -} - -/// Create a new native executor. -pub fn new_native_executor() -> sc_executor::NativeExecutor { - sc_executor::NativeExecutor::new(sc_executor::WasmExecutionMethod::Interpreted, None, 8) -} - -/// Extrinsics that must be included in each block. -/// -/// The index of the block must be provided to calculate a valid timestamp for the block. The value starts at 0 and -/// should be incremented by one for every block produced. -pub fn needed_extrinsics( - i: u64, -) -> Vec { - let timestamp = std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH) - .expect("now always later than unix epoch; qed") - .as_millis() + (i * polkadot_test_runtime::constants::time::SLOT_DURATION / 2) as u128; - - vec![ - polkadot_test_runtime::UncheckedExtrinsic { - function: polkadot_test_runtime::Call::Timestamp(pallet_timestamp::Call::set( - u64::try_from(timestamp).expect("unexpected big timestamp"), - )), - signature: None, - } - ] -} diff --git a/polkadot/runtime/test-runtime/src/lib.rs b/polkadot/runtime/test-runtime/src/lib.rs index ee98b272c7..3656d3d728 100644 --- a/polkadot/runtime/test-runtime/src/lib.rs +++ b/polkadot/runtime/test-runtime/src/lib.rs @@ -104,6 +104,13 @@ pub fn native_version() -> NativeVersion { } } +sp_api::decl_runtime_apis! { + pub trait GetLastTimestamp { + /// Returns the last timestamp of a runtime. + fn get_last_timestamp() -> u64; + } +} + parameter_types! { pub const Version: RuntimeVersion = VERSION; } @@ -736,4 +743,10 @@ sp_api::impl_runtime_apis! { TransactionPayment::query_info(uxt, len) } } + + impl crate::GetLastTimestamp for Runtime { + fn get_last_timestamp() -> u64 { + Timestamp::now() + } + } }