From c047ec900ab2453dba860b4d977870e3db64cd6d Mon Sep 17 00:00:00 2001 From: Sebastian Kunert Date: Mon, 5 Jun 2023 16:41:20 +0200 Subject: [PATCH] Add benchmarks for for import/validation/production (#2579) * Add benchmarks * Remove endowed accounts * Fix group * Remove duplicate code * Comments * Use execute_block instead of block import * Apply suggestions from code review Co-authored-by: Anton * Add missing comments * Clippy * Reorganize utils * Remove unused import * Switch to compiled again * Apply suggestions from code review Co-authored-by: Davide Galassi * Refactor local_testnet * Do not write wasm blob to file * Remove unnecessary block * Add comment fixes * fmt * lock --------- Co-authored-by: Anton Co-authored-by: Davide Galassi --- cumulus/Cargo.lock | 526 +++++++++++++++++- cumulus/test/client/src/lib.rs | 43 +- cumulus/test/runtime/Cargo.toml | 1 + cumulus/test/runtime/src/lib.rs | 13 +- cumulus/test/service/Cargo.toml | 41 ++ cumulus/test/service/benches/block_import.rs | 79 +++ .../service/benches/block_import_glutton.rs | 93 ++++ .../test/service/benches/block_production.rs | 111 ++++ .../benches/block_production_glutton.rs | 114 ++++ .../test/service/benches/validate_block.rs | 163 ++++++ .../service/benches/validate_block_glutton.rs | 211 +++++++ cumulus/test/service/src/bench_utils.rs | 262 +++++++++ cumulus/test/service/src/chain_spec.rs | 63 ++- cumulus/test/service/src/lib.rs | 32 +- 14 files changed, 1711 insertions(+), 41 deletions(-) create mode 100644 cumulus/test/service/benches/block_import.rs create mode 100644 cumulus/test/service/benches/block_import_glutton.rs create mode 100644 cumulus/test/service/benches/block_production.rs create mode 100644 cumulus/test/service/benches/block_production_glutton.rs create mode 100644 cumulus/test/service/benches/validate_block.rs create mode 100644 cumulus/test/service/benches/validate_block_glutton.rs create mode 100644 cumulus/test/service/src/bench_utils.rs diff --git a/cumulus/Cargo.lock b/cumulus/Cargo.lock index d38e95464c..98b120fde4 100644 --- a/cumulus/Cargo.lock +++ b/cumulus/Cargo.lock @@ -1838,6 +1838,15 @@ dependencies = [ "strsim", ] +[[package]] +name = "clap_complete" +version = "4.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f6b5c519bab3ea61843a7923d074b04245624bb84a64a8c150f5deb014e388b" +dependencies = [ + "clap", +] + [[package]] name = "clap_derive" version = "4.3.1" @@ -3116,6 +3125,7 @@ dependencies = [ "frame-system", "frame-system-rpc-runtime-api", "pallet-balances", + "pallet-glutton", "pallet-sudo", "pallet-timestamp", "pallet-transaction-payment", @@ -3147,17 +3157,25 @@ dependencies = [ "cumulus-client-consensus-relay-chain", "cumulus-client-pov-recovery", "cumulus-client-service", + "cumulus-pallet-parachain-system", "cumulus-primitives-core", "cumulus-primitives-parachain-inherent", "cumulus-relay-chain-inprocess-interface", "cumulus-relay-chain-interface", "cumulus-relay-chain-minimal-node", + "cumulus-test-client", + "cumulus-test-relay-sproof-builder", "cumulus-test-relay-validation-worker-provider", "cumulus-test-runtime", "frame-system", "frame-system-rpc-runtime-api", "futures", "jsonrpsee", + "kitchensink-runtime", + "node-cli", + "node-primitives", + "pallet-im-online", + "pallet-timestamp", "pallet-transaction-payment", "parachains-common", "parity-scale-codec", @@ -3169,12 +3187,16 @@ dependencies = [ "polkadot-test-service", "portpicker", "rand 0.8.5", + "rococo-parachain-runtime", "sc-basic-authorship", + "sc-block-builder", "sc-chain-spec", "sc-cli", "sc-client-api", "sc-consensus", "sc-executor", + "sc-executor-common", + "sc-executor-wasmtime", "sc-network", "sc-service", "sc-telemetry", @@ -3182,8 +3204,12 @@ dependencies = [ "sc-transaction-pool", "sc-transaction-pool-api", "serde", + "sp-api", "sp-arithmetic", + "sp-authority-discovery", "sp-blockchain", + "sp-consensus", + "sp-consensus-grandpa", "sp-core", "sp-io", "sp-keyring", @@ -3194,6 +3220,7 @@ dependencies = [ "sp-trie", "substrate-test-client", "substrate-test-utils", + "tempfile", "tokio", "tracing", "url", @@ -4197,6 +4224,21 @@ dependencies = [ "thousands", ] +[[package]] +name = "frame-benchmarking-pallet-pov" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#31b57a1884b00ed979ba85343e308ed73756cb41" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "sp-io", + "sp-runtime", + "sp-std", +] + [[package]] name = "frame-election-provider-solution-type" version = "4.0.0-dev" @@ -5083,6 +5125,17 @@ dependencies = [ "parity-scale-codec", ] +[[package]] +name = "impl-num-traits" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "951641f13f873bff03d4bf19ae8bec531935ac0ac2cc775f84d7edfdcfed3f17" +dependencies = [ + "integer-sqrt", + "num-traits", + "uint", +] + [[package]] name = "impl-rlp" version = "0.3.0" @@ -5482,6 +5535,115 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67c21572b4949434e4fc1e1978b99c5f77064153c59d998bf13ecd96fb5ecba7" +[[package]] +name = "kitchensink-runtime" +version = "3.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#31b57a1884b00ed979ba85343e308ed73756cb41" +dependencies = [ + "frame-benchmarking", + "frame-benchmarking-pallet-pov", + "frame-election-provider-support", + "frame-executive", + "frame-support", + "frame-system", + "frame-system-benchmarking", + "frame-system-rpc-runtime-api", + "frame-try-runtime", + "log", + "node-primitives", + "pallet-alliance", + "pallet-asset-conversion", + "pallet-asset-rate", + "pallet-asset-tx-payment", + "pallet-assets", + "pallet-authority-discovery", + "pallet-authorship", + "pallet-babe", + "pallet-bags-list", + "pallet-balances", + "pallet-bounties", + "pallet-child-bounties", + "pallet-collective", + "pallet-contracts", + "pallet-contracts-primitives", + "pallet-conviction-voting", + "pallet-core-fellowship", + "pallet-democracy", + "pallet-election-provider-multi-phase", + "pallet-election-provider-support-benchmarking", + "pallet-elections-phragmen", + "pallet-fast-unstake", + "pallet-glutton", + "pallet-grandpa", + "pallet-identity", + "pallet-im-online", + "pallet-indices", + "pallet-insecure-randomness-collective-flip", + "pallet-lottery", + "pallet-membership", + "pallet-message-queue", + "pallet-mmr", + "pallet-multisig", + "pallet-nft-fractionalization", + "pallet-nfts", + "pallet-nfts-runtime-api", + "pallet-nis", + "pallet-nomination-pools", + "pallet-nomination-pools-benchmarking", + "pallet-nomination-pools-runtime-api", + "pallet-offences", + "pallet-offences-benchmarking", + "pallet-preimage", + "pallet-proxy", + "pallet-ranked-collective", + "pallet-recovery", + "pallet-referenda", + "pallet-remark", + "pallet-root-testing", + "pallet-salary", + "pallet-scheduler", + "pallet-session", + "pallet-session-benchmarking", + "pallet-society", + "pallet-staking", + "pallet-staking-reward-curve", + "pallet-staking-runtime-api", + "pallet-state-trie-migration", + "pallet-statement", + "pallet-sudo", + "pallet-timestamp", + "pallet-tips", + "pallet-transaction-payment", + "pallet-transaction-payment-rpc-runtime-api", + "pallet-transaction-storage", + "pallet-treasury", + "pallet-uniques", + "pallet-utility", + "pallet-vesting", + "pallet-whitelist", + "parity-scale-codec", + "primitive-types", + "scale-info", + "sp-api", + "sp-authority-discovery", + "sp-block-builder", + "sp-consensus-babe", + "sp-consensus-grandpa", + "sp-core", + "sp-inherents", + "sp-io", + "sp-offchain", + "sp-runtime", + "sp-session", + "sp-staking", + "sp-statement-store", + "sp-std", + "sp-transaction-pool", + "sp-version", + "static_assertions", + "substrate-wasm-builder", +] + [[package]] name = "kusama-runtime" version = "0.9.41" @@ -6800,6 +6962,158 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "node-cli" +version = "3.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#31b57a1884b00ed979ba85343e308ed73756cb41" +dependencies = [ + "array-bytes 4.2.0", + "clap", + "clap_complete", + "frame-benchmarking-cli", + "frame-system", + "frame-system-rpc-runtime-api", + "futures", + "jsonrpsee", + "kitchensink-runtime", + "log", + "node-executor", + "node-inspect", + "node-primitives", + "node-rpc", + "pallet-asset-conversion", + "pallet-asset-tx-payment", + "pallet-assets", + "pallet-balances", + "pallet-im-online", + "pallet-transaction-payment", + "parity-scale-codec", + "rand 0.8.5", + "sc-authority-discovery", + "sc-basic-authorship", + "sc-chain-spec", + "sc-cli", + "sc-client-api", + "sc-consensus", + "sc-consensus-babe", + "sc-consensus-grandpa", + "sc-consensus-slots", + "sc-executor", + "sc-network", + "sc-network-common", + "sc-network-statement", + "sc-network-sync", + "sc-rpc", + "sc-service", + "sc-statement-store", + "sc-storage-monitor", + "sc-sync-state-rpc", + "sc-sysinfo", + "sc-telemetry", + "sc-transaction-pool", + "sc-transaction-pool-api", + "serde", + "serde_json", + "sp-api", + "sp-authority-discovery", + "sp-consensus", + "sp-consensus-babe", + "sp-consensus-grandpa", + "sp-core", + "sp-inherents", + "sp-io", + "sp-keyring", + "sp-keystore", + "sp-runtime", + "sp-timestamp", + "sp-transaction-pool", + "sp-transaction-storage-proof", + "substrate-build-script-utils", + "substrate-frame-cli", + "try-runtime-cli", +] + +[[package]] +name = "node-executor" +version = "3.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#31b57a1884b00ed979ba85343e308ed73756cb41" +dependencies = [ + "frame-benchmarking", + "kitchensink-runtime", + "node-primitives", + "parity-scale-codec", + "sc-executor", + "scale-info", + "sp-core", + "sp-keystore", + "sp-state-machine", + "sp-statement-store", + "sp-tracing", + "sp-trie", +] + +[[package]] +name = "node-inspect" +version = "0.9.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#31b57a1884b00ed979ba85343e308ed73756cb41" +dependencies = [ + "clap", + "parity-scale-codec", + "sc-cli", + "sc-client-api", + "sc-executor", + "sc-service", + "sp-blockchain", + "sp-core", + "sp-runtime", + "thiserror", +] + +[[package]] +name = "node-primitives" +version = "2.0.0" +source = "git+https://github.com/paritytech/substrate?branch=master#31b57a1884b00ed979ba85343e308ed73756cb41" +dependencies = [ + "frame-system", + "parity-scale-codec", + "scale-info", + "sp-application-crypto", + "sp-core", + "sp-runtime", +] + +[[package]] +name = "node-rpc" +version = "3.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#31b57a1884b00ed979ba85343e308ed73756cb41" +dependencies = [ + "jsonrpsee", + "mmr-rpc", + "node-primitives", + "pallet-transaction-payment-rpc", + "sc-chain-spec", + "sc-client-api", + "sc-consensus-babe", + "sc-consensus-babe-rpc", + "sc-consensus-grandpa", + "sc-consensus-grandpa-rpc", + "sc-rpc", + "sc-rpc-api", + "sc-rpc-spec-v2", + "sc-sync-state-rpc", + "sc-transaction-pool-api", + "sp-api", + "sp-block-builder", + "sp-blockchain", + "sp-consensus", + "sp-consensus-babe", + "sp-keystore", + "sp-runtime", + "sp-statement-store", + "substrate-frame-rpc-system", + "substrate-state-trie-migration-rpc", +] + [[package]] name = "nohash-hasher" version = "0.2.0" @@ -7054,6 +7368,37 @@ dependencies = [ "sp-std", ] +[[package]] +name = "pallet-asset-conversion" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#31b57a1884b00ed979ba85343e308ed73756cb41" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "sp-api", + "sp-arithmetic", + "sp-io", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-asset-rate" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#31b57a1884b00ed979ba85343e308ed73756cb41" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "sp-runtime", + "sp-std", +] + [[package]] name = "pallet-asset-tx-payment" version = "4.0.0-dev" @@ -7472,6 +7817,24 @@ dependencies = [ "sp-std", ] +[[package]] +name = "pallet-core-fellowship" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#31b57a1884b00ed979ba85343e308ed73756cb41" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "sp-arithmetic", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + [[package]] name = "pallet-democracy" version = "4.0.0-dev" @@ -7671,6 +8034,20 @@ dependencies = [ "sp-std", ] +[[package]] +name = "pallet-lottery" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#31b57a1884b00ed979ba85343e308ed73756cb41" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "sp-runtime", + "sp-std", +] + [[package]] name = "pallet-membership" version = "4.0.0-dev" @@ -7990,6 +8367,56 @@ dependencies = [ "sp-std", ] +[[package]] +name = "pallet-remark" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#31b57a1884b00ed979ba85343e308ed73756cb41" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "serde", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-root-testing" +version = "1.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#31b57a1884b00ed979ba85343e308ed73756cb41" +dependencies = [ + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-salary" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#31b57a1884b00ed979ba85343e308ed73756cb41" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "sp-arithmetic", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + [[package]] name = "pallet-scheduler" version = "4.0.0-dev" @@ -8127,6 +8554,24 @@ dependencies = [ "sp-std", ] +[[package]] +name = "pallet-statement" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#31b57a1884b00ed979ba85343e308ed73756cb41" +dependencies = [ + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "sp-api", + "sp-core", + "sp-io", + "sp-runtime", + "sp-statement-store", + "sp-std", +] + [[package]] name = "pallet-sudo" version = "4.0.0-dev" @@ -8223,6 +8668,26 @@ dependencies = [ "sp-weights", ] +[[package]] +name = "pallet-transaction-storage" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#31b57a1884b00ed979ba85343e308ed73756cb41" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "pallet-balances", + "parity-scale-codec", + "scale-info", + "serde", + "sp-inherents", + "sp-io", + "sp-runtime", + "sp-std", + "sp-transaction-storage-proof", +] + [[package]] name = "pallet-treasury" version = "4.0.0-dev" @@ -10516,6 +10981,7 @@ checksum = "5cfd65aea0c5fa0bfcc7c9e7ca828c921ef778f43d325325ec84bda371bfa75a" dependencies = [ "fixed-hash", "impl-codec", + "impl-num-traits", "impl-rlp", "impl-serde", "scale-info", @@ -12129,6 +12595,26 @@ dependencies = [ "thiserror", ] +[[package]] +name = "sc-network-statement" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#31b57a1884b00ed979ba85343e308ed73756cb41" +dependencies = [ + "array-bytes 4.2.0", + "async-channel", + "futures", + "libp2p", + "log", + "parity-scale-codec", + "pin-project", + "sc-network", + "sc-network-common", + "sp-consensus", + "sp-runtime", + "sp-statement-store", + "substrate-prometheus-endpoint", +] + [[package]] name = "sc-network-sync" version = "0.10.0-dev" @@ -12388,6 +12874,29 @@ dependencies = [ "sp-core", ] +[[package]] +name = "sc-statement-store" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#31b57a1884b00ed979ba85343e308ed73756cb41" +dependencies = [ + "async-trait", + "futures", + "futures-timer", + "log", + "parity-db", + "parity-scale-codec", + "parking_lot 0.12.1", + "sc-client-api", + "sp-api", + "sp-blockchain", + "sp-core", + "sp-runtime", + "sp-statement-store", + "sp-tracing", + "substrate-prometheus-endpoint", + "tokio", +] + [[package]] name = "sc-storage-monitor" version = "0.1.0" @@ -14014,6 +14523,19 @@ dependencies = [ "platforms", ] +[[package]] +name = "substrate-frame-cli" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#31b57a1884b00ed979ba85343e308ed73756cb41" +dependencies = [ + "clap", + "frame-support", + "frame-system", + "sc-cli", + "sp-core", + "sp-runtime", +] + [[package]] name = "substrate-frame-rpc-system" version = "4.0.0-dev" @@ -14872,9 +15394,9 @@ checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c" [[package]] name = "uint" -version = "0.9.1" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6470ab50f482bde894a037a57064480a246dbfdd5960bd65a44824693f08da5f" +checksum = "76f64bba2c53b04fcab63c01a7d7427eadc821e3bc48c34dc9ba29c501164b52" dependencies = [ "byteorder", "crunchy", diff --git a/cumulus/test/client/src/lib.rs b/cumulus/test/client/src/lib.rs index cbf0459b81..4888e957d7 100644 --- a/cumulus/test/client/src/lib.rs +++ b/cumulus/test/client/src/lib.rs @@ -26,7 +26,7 @@ use sc_executor::{HeapAllocStrategy, WasmExecutionMethod, WasmExecutor}; use sc_executor_common::runtime_blob::RuntimeBlob; use sc_service::client; use sp_blockchain::HeaderBackend; -use sp_core::storage::Storage; +use sp_core::{sr25519, storage::Storage, Pair}; use sp_io::TestExternalities; use sp_runtime::{generic::Era, BuildStorage, SaturatedConversion}; @@ -77,11 +77,22 @@ pub type Client = client::Client; /// Parameters of test-client builder with test-runtime. #[derive(Default)] -pub struct GenesisParameters; +pub struct GenesisParameters { + pub endowed_accounts: Vec, +} impl substrate_test_client::GenesisInit for GenesisParameters { fn genesis_storage(&self) -> Storage { - genesis_config().build_storage().unwrap() + if self.endowed_accounts.is_empty() { + genesis_config().build_storage().unwrap() + } else { + cumulus_test_service::testnet_genesis( + cumulus_test_service::get_account_id_from_seed::("Alice"), + self.endowed_accounts.clone(), + ) + .build_storage() + .unwrap() + } } } @@ -115,19 +126,26 @@ impl DefaultTestClientBuilderExt for TestClientBuilder { } fn genesis_config() -> GenesisConfig { - cumulus_test_service::local_testnet_genesis() + cumulus_test_service::testnet_genesis_with_default_endowed(Default::default()) } -/// Generate an extrinsic from the provided function call, origin and [`Client`]. -pub fn generate_extrinsic( +/// Create an unsigned extrinsic from a runtime call. +pub fn generate_unsigned(function: impl Into) -> UncheckedExtrinsic { + UncheckedExtrinsic::new_unsigned(function.into()) +} + +/// Create a signed extrinsic from a runtime call and sign +/// with the given key pair. +pub fn generate_extrinsic_with_pair( client: &Client, - origin: sp_keyring::AccountKeyring, + origin: sp_core::sr25519::Pair, function: impl Into, + nonce: Option, ) -> UncheckedExtrinsic { 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_default(); let period = BlockHashCount::get().checked_next_power_of_two().map(|c| c / 2).unwrap_or(2) as u64; let tip = 0; @@ -158,6 +176,15 @@ pub fn generate_extrinsic( ) } +/// Generate an extrinsic from the provided function call, origin and [`Client`]. +pub fn generate_extrinsic( + client: &Client, + origin: sp_keyring::AccountKeyring, + function: impl Into, +) -> UncheckedExtrinsic { + generate_extrinsic_with_pair(client, origin.into(), function, None) +} + /// Transfer some token from one account to another using a provided test [`Client`]. pub fn transfer( client: &Client, diff --git a/cumulus/test/runtime/Cargo.toml b/cumulus/test/runtime/Cargo.toml index 6f829c2749..e4ddeea69b 100644 --- a/cumulus/test/runtime/Cargo.toml +++ b/cumulus/test/runtime/Cargo.toml @@ -16,6 +16,7 @@ frame-system-rpc-runtime-api = { git = "https://github.com/paritytech/substrate" 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-glutton = { 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" } diff --git a/cumulus/test/runtime/src/lib.rs b/cumulus/test/runtime/src/lib.rs index 7633da1ea5..25841acb11 100644 --- a/cumulus/test/runtime/src/lib.rs +++ b/cumulus/test/runtime/src/lib.rs @@ -57,8 +57,12 @@ pub use frame_support::{ }, StorageValue, }; -use frame_system::limits::{BlockLength, BlockWeights}; +use frame_system::{ + limits::{BlockLength, BlockWeights}, + EnsureRoot, +}; pub use pallet_balances::Call as BalancesCall; +pub use pallet_glutton::Call as GluttonCall; pub use pallet_sudo::Call as SudoCall; pub use pallet_timestamp::Call as TimestampCall; #[cfg(any(feature = "std", test))] @@ -265,6 +269,12 @@ impl pallet_sudo::Config for Runtime { type WeightInfo = pallet_sudo::weights::SubstrateWeight; } +impl pallet_glutton::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type AdminOrigin = EnsureRoot; + type WeightInfo = pallet_glutton::weights::SubstrateWeight; +} + impl cumulus_pallet_parachain_system::Config for Runtime { type SelfParaId = ParachainId; type RuntimeEvent = RuntimeEvent; @@ -296,6 +306,7 @@ construct_runtime! { Sudo: pallet_sudo, TransactionPayment: pallet_transaction_payment, TestPallet: test_pallet, + Glutton: pallet_glutton, } } diff --git a/cumulus/test/service/Cargo.toml b/cumulus/test/service/Cargo.toml index b0141ec96c..49544cc4ae 100644 --- a/cumulus/test/service/Cargo.toml +++ b/cumulus/test/service/Cargo.toml @@ -19,6 +19,7 @@ serde = { version = "1.0.163", features = ["derive"] } tokio = { version = "1.28.2", features = ["macros"] } tracing = "0.1.37" url = "2.3.1" +tempfile = "3.5.0" # Substrate frame-system = { git = "https://github.com/paritytech/substrate", branch = "master" } @@ -39,14 +40,19 @@ sp-arithmetic = { git = "https://github.com/paritytech/substrate", branch = "mas sp-blockchain = { git = "https://github.com/paritytech/substrate", branch = "master" } sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" } sp-io = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-api = { git = "https://github.com/paritytech/substrate", branch = "master" } sp-keyring = { git = "https://github.com/paritytech/substrate", branch = "master" } sp-runtime = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } sp-state-machine = { git = "https://github.com/paritytech/substrate", branch = "master" } sp-tracing = { 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-consensus = { git = "https://github.com/paritytech/substrate", branch = "master" } substrate-test-client = { git = "https://github.com/paritytech/substrate", branch = "master" } sc-cli = { git = "https://github.com/paritytech/substrate", branch = "master" } +sc-block-builder = { git = "https://github.com/paritytech/substrate", branch = "master" } +sc-executor-wasmtime = { git = "https://github.com/paritytech/substrate", branch = "master" } +sc-executor-common = { git = "https://github.com/paritytech/substrate", branch = "master" } # Polkadot polkadot-primitives = { git = "https://github.com/paritytech/polkadot", branch = "master" } @@ -70,10 +76,21 @@ cumulus-test-relay-validation-worker-provider = { path = "../relay-validation-wo cumulus-test-runtime = { path = "../runtime" } cumulus-relay-chain-minimal-node = { path = "../../client/relay-chain-minimal-node" } cumulus-client-pov-recovery = { path = "../../client/pov-recovery" } +cumulus-test-relay-sproof-builder = { path = "../relay-sproof-builder" } +cumulus-pallet-parachain-system = { path = "../../pallets/parachain-system", default-features = false } +pallet-timestamp = { git = "https://github.com/paritytech/substrate", branch = "master" } [dev-dependencies] futures = "0.3.28" portpicker = "0.1.1" +kitchensink-runtime = { git = "https://github.com/paritytech/substrate", branch = "master" } +rococo-parachain-runtime = { path = "../../parachains/runtimes/testing/rococo-parachain" } +node-cli = { git = "https://github.com/paritytech/substrate", branch = "master" } +pallet-im-online = { git = "https://github.com/paritytech/substrate", branch = "master" } +node-primitives = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-consensus-grandpa = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-authority-discovery = { git = "https://github.com/paritytech/substrate", branch = "master" } +cumulus-test-client = { path = "../client" } # Polkadot dependencies polkadot-test-service = { git = "https://github.com/paritytech/polkadot", branch = "master" } @@ -88,3 +105,27 @@ runtime-benchmarks = ["polkadot-test-service/runtime-benchmarks"] [[bench]] name = "transaction_throughput" harness = false + +[[bench]] +name = "block_import" +harness = false + +[[bench]] +name = "block_production" +harness = false + +[[bench]] +name = "block_production_glutton" +harness = false + +[[bench]] +name = "block_import_glutton" +harness = false + +[[bench]] +name = "validate_block" +harness = false + +[[bench]] +name = "validate_block_glutton" +harness = false diff --git a/cumulus/test/service/benches/block_import.rs b/cumulus/test/service/benches/block_import.rs new file mode 100644 index 0000000000..b79598b153 --- /dev/null +++ b/cumulus/test/service/benches/block_import.rs @@ -0,0 +1,79 @@ +// This file is part of Cumulus. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use criterion::{criterion_group, criterion_main, BatchSize, Criterion, Throughput}; + +use sc_client_api::UsageProvider; + +use core::time::Duration; +use cumulus_primitives_core::ParaId; + +use sc_block_builder::{BlockBuilderProvider, RecordProof}; +use sp_api::{Core, ProvideRuntimeApi}; +use sp_keyring::Sr25519Keyring::Alice; + +use cumulus_test_service::bench_utils as utils; + +fn benchmark_block_import(c: &mut Criterion) { + sp_tracing::try_init_simple(); + + let runtime = tokio::runtime::Runtime::new().expect("creating tokio runtime doesn't fail; qed"); + let para_id = ParaId::from(100); + let tokio_handle = runtime.handle(); + + // Create enough accounts to fill the block with transactions. + // Each account should only be included in one transfer. + let (src_accounts, dst_accounts, account_ids) = utils::create_benchmark_accounts(); + + let alice = runtime.block_on( + cumulus_test_service::TestNodeBuilder::new(para_id, tokio_handle.clone(), Alice) + // Preload all accounts with funds for the transfers + .endowed_accounts(account_ids) + .build(), + ); + + let client = alice.client; + + let (max_transfer_count, extrinsics) = + utils::create_benchmarking_transfer_extrinsics(&client, &src_accounts, &dst_accounts); + + let parent_hash = client.usage_info().chain.best_hash; + let mut block_builder = + client.new_block_at(parent_hash, Default::default(), RecordProof::No).unwrap(); + for extrinsic in extrinsics { + block_builder.push(extrinsic).unwrap(); + } + let benchmark_block = block_builder.build().unwrap(); + + let mut group = c.benchmark_group("Block import"); + group.sample_size(20); + group.measurement_time(Duration::from_secs(120)); + group.throughput(Throughput::Elements(max_transfer_count as u64)); + + group.bench_function(format!("(transfers = {}) block import", max_transfer_count), |b| { + b.iter_batched( + || benchmark_block.block.clone(), + |block| { + client.runtime_api().execute_block(parent_hash, block).unwrap(); + }, + BatchSize::SmallInput, + ) + }); +} + +criterion_group!(benches, benchmark_block_import); +criterion_main!(benches); diff --git a/cumulus/test/service/benches/block_import_glutton.rs b/cumulus/test/service/benches/block_import_glutton.rs new file mode 100644 index 0000000000..48fc3dee76 --- /dev/null +++ b/cumulus/test/service/benches/block_import_glutton.rs @@ -0,0 +1,93 @@ +// This file is part of Cumulus. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use criterion::{criterion_group, criterion_main, BatchSize, Criterion}; + +use sc_client_api::UsageProvider; +use sp_api::{Core, ProvideRuntimeApi}; +use sp_arithmetic::Perbill; + +use core::time::Duration; +use cumulus_primitives_core::ParaId; + +use sc_block_builder::{BlockBuilderProvider, RecordProof}; +use sp_keyring::Sr25519Keyring::Alice; + +use cumulus_test_service::bench_utils as utils; + +fn benchmark_block_import(c: &mut Criterion) { + sp_tracing::try_init_simple(); + + let runtime = tokio::runtime::Runtime::new().expect("creating tokio runtime doesn't fail; qed"); + let para_id = ParaId::from(100); + let tokio_handle = runtime.handle(); + + let alice = runtime.block_on( + cumulus_test_service::TestNodeBuilder::new(para_id, tokio_handle.clone(), Alice).build(), + ); + let client = alice.client; + + let mut group = c.benchmark_group("Block import"); + group.sample_size(20); + group.measurement_time(Duration::from_secs(120)); + + let mut initialize_glutton_pallet = true; + for (compute_percent, storage_percent) in &[ + (Perbill::from_percent(100), Perbill::from_percent(0)), + (Perbill::from_percent(100), Perbill::from_percent(100)), + ] { + let block = utils::set_glutton_parameters( + &client, + initialize_glutton_pallet, + compute_percent, + storage_percent, + ); + initialize_glutton_pallet = false; + + runtime.block_on(utils::import_block(&client, &block, false)); + + // Build the block we will use for benchmarking + let parent_hash = client.usage_info().chain.best_hash; + let parent_header = client.header(parent_hash).expect("Just fetched this hash.").unwrap(); + let mut block_builder = + client.new_block_at(parent_hash, Default::default(), RecordProof::No).unwrap(); + block_builder + .push(utils::extrinsic_set_validation_data(parent_header.clone()).clone()) + .unwrap(); + block_builder.push(utils::extrinsic_set_time(&client)).unwrap(); + let benchmark_block = block_builder.build().unwrap(); + + group.bench_function( + format!( + "(compute = {:?}, storage = {:?}) block import", + compute_percent, storage_percent + ), + |b| { + b.iter_batched( + || benchmark_block.block.clone(), + |block| { + client.runtime_api().execute_block(parent_hash, block).unwrap(); + }, + BatchSize::SmallInput, + ) + }, + ); + } +} + +criterion_group!(benches, benchmark_block_import); +criterion_main!(benches); diff --git a/cumulus/test/service/benches/block_production.rs b/cumulus/test/service/benches/block_production.rs new file mode 100644 index 0000000000..1b868d7363 --- /dev/null +++ b/cumulus/test/service/benches/block_production.rs @@ -0,0 +1,111 @@ +// This file is part of Cumulus. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use criterion::{criterion_group, criterion_main, BatchSize, Criterion, Throughput}; + +use sc_client_api::UsageProvider; + +use core::time::Duration; +use cumulus_primitives_core::ParaId; +use sc_block_builder::{BlockBuilderProvider, RecordProof}; + +use sp_keyring::Sr25519Keyring::Alice; + +use cumulus_test_service::bench_utils as utils; + +fn benchmark_block_production(c: &mut Criterion) { + sp_tracing::try_init_simple(); + + let runtime = tokio::runtime::Runtime::new().expect("creating tokio runtime doesn't fail; qed"); + let tokio_handle = runtime.handle(); + + // Create enough accounts to fill the block with transactions. + // Each account should only be included in one transfer. + let (src_accounts, dst_accounts, account_ids) = utils::create_benchmark_accounts(); + + let para_id = ParaId::from(100); + let alice = runtime.block_on( + cumulus_test_service::TestNodeBuilder::new(para_id, tokio_handle.clone(), Alice) + // Preload all accounts with funds for the transfers + .endowed_accounts(account_ids) + .build(), + ); + let client = alice.client; + + let parent_hash = client.usage_info().chain.best_hash; + let parent_header = client.header(parent_hash).expect("Just fetched this hash.").unwrap(); + let set_validation_data_extrinsic = utils::extrinsic_set_validation_data(parent_header); + + let mut block_builder = client.new_block(Default::default()).unwrap(); + block_builder.push(utils::extrinsic_set_time(&client)).unwrap(); + block_builder.push(set_validation_data_extrinsic).unwrap(); + let built_block = block_builder.build().unwrap(); + + runtime.block_on(utils::import_block(&client, &built_block.block, false)); + + let (max_transfer_count, extrinsics) = + utils::create_benchmarking_transfer_extrinsics(&client, &src_accounts, &dst_accounts); + + let mut group = c.benchmark_group("Block production"); + + group.sample_size(20); + group.measurement_time(Duration::from_secs(120)); + group.throughput(Throughput::Elements(max_transfer_count as u64)); + + let best_hash = client.chain_info().best_hash; + + group.bench_function( + format!("(proof = true, transfers = {}) block production", max_transfer_count), + |b| { + b.iter_batched( + || extrinsics.clone(), + |extrinsics| { + let mut block_builder = client + .new_block_at(best_hash, Default::default(), RecordProof::Yes) + .unwrap(); + for extrinsic in extrinsics { + block_builder.push(extrinsic).unwrap(); + } + block_builder.build().unwrap() + }, + BatchSize::SmallInput, + ) + }, + ); + + group.bench_function( + format!("(proof = false, transfers = {}) block production", max_transfer_count), + |b| { + b.iter_batched( + || extrinsics.clone(), + |extrinsics| { + let mut block_builder = client + .new_block_at(best_hash, Default::default(), RecordProof::No) + .unwrap(); + for extrinsic in extrinsics { + block_builder.push(extrinsic).unwrap(); + } + block_builder.build().unwrap() + }, + BatchSize::SmallInput, + ) + }, + ); +} + +criterion_group!(benches, benchmark_block_production); +criterion_main!(benches); diff --git a/cumulus/test/service/benches/block_production_glutton.rs b/cumulus/test/service/benches/block_production_glutton.rs new file mode 100644 index 0000000000..aa05b4e461 --- /dev/null +++ b/cumulus/test/service/benches/block_production_glutton.rs @@ -0,0 +1,114 @@ +// This file is part of Cumulus. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use criterion::{criterion_group, criterion_main, BatchSize, Criterion}; + +use sc_client_api::UsageProvider; +use sp_arithmetic::Perbill; + +use core::time::Duration; +use cumulus_primitives_core::ParaId; + +use sc_block_builder::{BlockBuilderProvider, RecordProof}; + +use sp_keyring::Sr25519Keyring::Alice; + +use cumulus_test_service::bench_utils as utils; + +fn benchmark_block_production_compute(c: &mut Criterion) { + sp_tracing::try_init_simple(); + + let runtime = tokio::runtime::Runtime::new().expect("creating tokio runtime doesn't fail; qed"); + let tokio_handle = runtime.handle(); + + let para_id = ParaId::from(100); + let alice = runtime.block_on( + cumulus_test_service::TestNodeBuilder::new(para_id, tokio_handle.clone(), Alice).build(), + ); + let client = alice.client; + + let mut group = c.benchmark_group("Block production"); + + group.sample_size(20); + group.measurement_time(Duration::from_secs(120)); + + let mut initialize_glutton_pallet = true; + for (compute_level, storage_level) in &[ + (Perbill::from_percent(100), Perbill::from_percent(0)), + (Perbill::from_percent(100), Perbill::from_percent(100)), + ] { + let block = utils::set_glutton_parameters( + &client, + initialize_glutton_pallet, + compute_level, + storage_level, + ); + runtime.block_on(utils::import_block(&client, &block, false)); + initialize_glutton_pallet = false; + + let parent_hash = client.usage_info().chain.best_hash; + let parent_header = client.header(parent_hash).expect("Just fetched this hash.").unwrap(); + let set_validation_data_extrinsic = utils::extrinsic_set_validation_data(parent_header); + let set_time_extrinsic = utils::extrinsic_set_time(&client); + let best_hash = client.chain_info().best_hash; + + group.bench_function( + format!( + "(compute = {:?}, storage = {:?}, proof = true) block production", + compute_level, storage_level + ), + |b| { + b.iter_batched( + || (set_validation_data_extrinsic.clone(), set_time_extrinsic.clone()), + |(validation_data, time)| { + let mut block_builder = client + .new_block_at(best_hash, Default::default(), RecordProof::Yes) + .unwrap(); + block_builder.push(validation_data).unwrap(); + block_builder.push(time).unwrap(); + block_builder.build().unwrap() + }, + BatchSize::SmallInput, + ) + }, + ); + + group.bench_function( + format!( + "(compute = {:?}, storage = {:?}, proof = false) block production", + compute_level, storage_level + ), + |b| { + b.iter_batched( + || (set_validation_data_extrinsic.clone(), set_time_extrinsic.clone()), + |(validation_data, time)| { + let mut block_builder = client + .new_block_at(best_hash, Default::default(), RecordProof::No) + .unwrap(); + block_builder.push(validation_data).unwrap(); + block_builder.push(time).unwrap(); + block_builder.build().unwrap() + }, + BatchSize::SmallInput, + ) + }, + ); + } +} + +criterion_group!(benches, benchmark_block_production_compute); +criterion_main!(benches); diff --git a/cumulus/test/service/benches/validate_block.rs b/cumulus/test/service/benches/validate_block.rs new file mode 100644 index 0000000000..3f4c39e5aa --- /dev/null +++ b/cumulus/test/service/benches/validate_block.rs @@ -0,0 +1,163 @@ +// This file is part of Cumulus. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use codec::{Decode, Encode}; +use core::time::Duration; +use criterion::{criterion_group, criterion_main, BatchSize, Criterion, Throughput}; +use cumulus_primitives_core::{relay_chain::AccountId, PersistedValidationData, ValidationParams}; +use cumulus_test_client::{ + generate_extrinsic_with_pair, BuildParachainBlockData, InitBlockBuilder, TestClientBuilder, + ValidationResult, +}; +use cumulus_test_relay_sproof_builder::RelayStateSproofBuilder; +use cumulus_test_runtime::{BalancesCall, Block, Header, UncheckedExtrinsic}; +use cumulus_test_service::bench_utils as utils; +use polkadot_primitives::HeadData; +use sc_block_builder::BlockBuilderProvider; +use sc_client_api::UsageProvider; +use sc_executor_common::wasm_runtime::WasmModule; + +use sp_blockchain::{ApplyExtrinsicFailed::Validity, Error::ApplyExtrinsicFailed}; + +use sp_core::{sr25519, Pair}; + +use sp_runtime::{ + traits::Header as HeaderT, + transaction_validity::{InvalidTransaction, TransactionValidityError}, +}; + +fn create_extrinsics( + client: &cumulus_test_client::Client, + src_accounts: &[sr25519::Pair], + dst_accounts: &[sr25519::Pair], +) -> (usize, Vec) { + // Add as many tranfer extrinsics as possible into a single block. + let mut block_builder = client.new_block(Default::default()).unwrap(); + let mut max_transfer_count = 0; + let mut extrinsics = Vec::new(); + + for (src, dst) in src_accounts.iter().zip(dst_accounts.iter()) { + let extrinsic: UncheckedExtrinsic = generate_extrinsic_with_pair( + client, + src.clone(), + BalancesCall::transfer_keep_alive { dest: AccountId::from(dst.public()), value: 10000 }, + None, + ); + + match block_builder.push(extrinsic.clone()) { + Ok(_) => {}, + Err(ApplyExtrinsicFailed(Validity(TransactionValidityError::Invalid( + InvalidTransaction::ExhaustsResources, + )))) => break, + Err(error) => panic!("{}", error), + } + + extrinsics.push(extrinsic); + max_transfer_count += 1; + } + + (max_transfer_count, extrinsics) +} + +fn benchmark_block_validation(c: &mut Criterion) { + sp_tracing::try_init_simple(); + // Create enough accounts to fill the block with transactions. + // Each account should only be included in one transfer. + let (src_accounts, dst_accounts, account_ids) = utils::create_benchmark_accounts(); + + let mut test_client_builder = TestClientBuilder::with_default_backend() + .set_execution_strategy(sc_client_api::ExecutionStrategy::AlwaysWasm); + let genesis_init = test_client_builder.genesis_init_mut(); + *genesis_init = cumulus_test_client::GenesisParameters { endowed_accounts: account_ids }; + let client = test_client_builder.build_with_native_executor(None).0; + + let (max_transfer_count, extrinsics) = create_extrinsics(&client, &src_accounts, &dst_accounts); + + let parent_hash = client.usage_info().chain.best_hash; + let parent_header = client.header(parent_hash).expect("Just fetched this hash.").unwrap(); + let validation_data = PersistedValidationData { + relay_parent_number: 1, + parent_head: parent_header.encode().into(), + ..Default::default() + }; + + let mut block_builder = client.init_block_builder(Some(validation_data), Default::default()); + for extrinsic in extrinsics { + block_builder.push(extrinsic).unwrap(); + } + + let parachain_block = block_builder.build_parachain_block(*parent_header.state_root()); + + let proof_size_in_kb = parachain_block.storage_proof().encode().len() as f64 / 1024f64; + let runtime = utils::get_wasm_module(); + + let sproof_builder: RelayStateSproofBuilder = Default::default(); + let (relay_parent_storage_root, _) = sproof_builder.into_state_root_and_proof(); + let encoded_params = ValidationParams { + block_data: cumulus_test_client::BlockData(parachain_block.encode()), + parent_head: HeadData(parent_header.encode()), + relay_parent_number: 1, + relay_parent_storage_root, + } + .encode(); + + // This is not strictly necessary for this benchmark, but + // let us make sure that the result of `validate_block` is what + // we expect. + verify_expected_result(&runtime, &encoded_params, parachain_block.into_block()); + + let mut group = c.benchmark_group("Block validation"); + group.sample_size(20); + group.measurement_time(Duration::from_secs(120)); + group.throughput(Throughput::Elements(max_transfer_count as u64)); + + group.bench_function( + format!( + "(transfers = {}, proof_size = {}kb) block validation", + max_transfer_count, proof_size_in_kb + ), + |b| { + b.iter_batched( + || runtime.new_instance().unwrap(), + |mut instance| { + instance.call_export("validate_block", &encoded_params).unwrap(); + }, + BatchSize::SmallInput, + ) + }, + ); +} + +fn verify_expected_result( + runtime: &Box, + encoded_params: &[u8], + parachain_block: Block, +) { + let res = runtime + .new_instance() + .unwrap() + .call_export("validate_block", encoded_params) + .expect("Call `validate_block`."); + let validation_result = + ValidationResult::decode(&mut &res[..]).expect("Decode `ValidationResult`."); + let header = + Header::decode(&mut &validation_result.head_data.0[..]).expect("Decodes `Header`."); + assert_eq!(parachain_block.header, header); +} + +criterion_group!(benches, benchmark_block_validation); +criterion_main!(benches); diff --git a/cumulus/test/service/benches/validate_block_glutton.rs b/cumulus/test/service/benches/validate_block_glutton.rs new file mode 100644 index 0000000000..ceeff736ab --- /dev/null +++ b/cumulus/test/service/benches/validate_block_glutton.rs @@ -0,0 +1,211 @@ +// This file is part of Cumulus. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use codec::{Decode, Encode}; +use core::time::Duration; +use criterion::{criterion_group, criterion_main, BatchSize, Criterion}; +use cumulus_primitives_core::{relay_chain::AccountId, PersistedValidationData, ValidationParams}; +use cumulus_test_client::{ + generate_extrinsic_with_pair, BuildParachainBlockData, Client, InitBlockBuilder, + ParachainBlockData, TestClientBuilder, ValidationResult, +}; +use cumulus_test_relay_sproof_builder::RelayStateSproofBuilder; +use cumulus_test_runtime::{Block, GluttonCall, Header, SudoCall}; +use polkadot_primitives::HeadData; +use sc_client_api::UsageProvider; +use sc_consensus::{BlockImport, BlockImportParams, ForkChoiceStrategy, ImportResult, StateAction}; +use sc_executor_common::wasm_runtime::WasmModule; +use sp_api::ProvideRuntimeApi; + +use frame_system_rpc_runtime_api::AccountNonceApi; +use sp_arithmetic::Perbill; +use sp_consensus::BlockOrigin; +use sp_keyring::Sr25519Keyring::Alice; +use sp_runtime::traits::Header as HeaderT; + +use cumulus_test_service::bench_utils as utils; + +async fn import_block( + mut client: &cumulus_test_client::Client, + built: cumulus_test_runtime::Block, + import_existing: bool, +) { + let mut params = BlockImportParams::new(BlockOrigin::File, built.header.clone()); + params.body = Some(built.extrinsics.clone()); + params.state_action = StateAction::Execute; + params.fork_choice = Some(ForkChoiceStrategy::LongestChain); + params.import_existing = import_existing; + let import_result = client.import_block(params).await; + assert!(matches!(import_result, Ok(ImportResult::Imported(_)))); +} + +fn benchmark_block_validation(c: &mut Criterion) { + sp_tracing::try_init_simple(); + let runtime = tokio::runtime::Runtime::new().expect("creating tokio runtime doesn't fail; qed"); + + let endowed_accounts = vec![AccountId::from(Alice.public())]; + let mut test_client_builder = TestClientBuilder::with_default_backend() + .set_execution_strategy(sc_client_api::ExecutionStrategy::NativeElseWasm); + let genesis_init = test_client_builder.genesis_init_mut(); + *genesis_init = cumulus_test_client::GenesisParameters { endowed_accounts }; + + let client = test_client_builder.build_with_native_executor(None).0; + + let mut group = c.benchmark_group("Block validation"); + group.sample_size(20); + group.measurement_time(Duration::from_secs(120)); + + // In the first iteration we want to initialie the glutton pallet + let mut is_first = true; + for (compute_percent, storage_percent) in &[ + (Perbill::from_percent(100), Perbill::from_percent(0)), + (Perbill::from_percent(100), Perbill::from_percent(100)), + ] { + let parachain_block = + set_glutton_parameters(&client, is_first, compute_percent, storage_percent); + is_first = false; + + runtime.block_on(import_block(&client, parachain_block.clone().into_block(), false)); + + // Build benchmark block + let parent_hash = client.usage_info().chain.best_hash; + let parent_header = client.header(parent_hash).expect("Just fetched this hash.").unwrap(); + let validation_data = PersistedValidationData { + relay_parent_number: 1, + parent_head: parent_header.encode().into(), + ..Default::default() + }; + let block_builder = client.init_block_builder(Some(validation_data), Default::default()); + let parachain_block = block_builder.build_parachain_block(*parent_header.state_root()); + + let proof_size_in_kb = parachain_block.storage_proof().encode().len() as f64 / 1024f64; + runtime.block_on(import_block(&client, parachain_block.clone().into_block(), false)); + let runtime = utils::get_wasm_module(); + + let sproof_builder: RelayStateSproofBuilder = Default::default(); + let (relay_parent_storage_root, _) = sproof_builder.clone().into_state_root_and_proof(); + let encoded_params = ValidationParams { + block_data: cumulus_test_client::BlockData(parachain_block.clone().encode()), + parent_head: HeadData(parent_header.encode()), + relay_parent_number: 1, + relay_parent_storage_root, + } + .encode(); + + // This is not strictly necessary for this benchmark, but + // let us make sure that the result of `validate_block` is what + // we expect. + verify_expected_result(&runtime, &encoded_params, parachain_block.into_block()); + + group.bench_function( + format!( + "(compute = {:?}, storage = {:?}, proof_size = {}kb) block validation", + compute_percent, storage_percent, proof_size_in_kb + ), + |b| { + b.iter_batched( + || runtime.new_instance().unwrap(), + |mut instance| { + instance.call_export("validate_block", &encoded_params).unwrap(); + }, + BatchSize::SmallInput, + ) + }, + ); + } +} + +fn verify_expected_result(runtime: &Box, encoded_params: &[u8], block: Block) { + let res = runtime + .new_instance() + .unwrap() + .call_export("validate_block", encoded_params) + .expect("Call `validate_block`."); + let validation_result = + ValidationResult::decode(&mut &res[..]).expect("Decode `ValidationResult`."); + let header = + Header::decode(&mut &validation_result.head_data.0[..]).expect("Decodes `Header`."); + assert_eq!(block.header, header); +} + +fn set_glutton_parameters( + client: &Client, + initialize: bool, + compute_percent: &Perbill, + storage_percent: &Perbill, +) -> ParachainBlockData { + let parent_hash = client.usage_info().chain.best_hash; + let parent_header = client.header(parent_hash).expect("Just fetched this hash.").unwrap(); + + let mut last_nonce = client + .runtime_api() + .account_nonce(parent_hash, Alice.into()) + .expect("Fetching account nonce works; qed"); + + let validation_data = PersistedValidationData { + relay_parent_number: 1, + parent_head: parent_header.encode().into(), + ..Default::default() + }; + + let mut extrinsics = vec![]; + if initialize { + extrinsics.push(generate_extrinsic_with_pair( + client, + Alice.into(), + SudoCall::sudo { + call: Box::new( + GluttonCall::initialize_pallet { new_count: 5000, witness_count: None }.into(), + ), + }, + Some(last_nonce), + )); + last_nonce += 1; + } + + let set_compute = generate_extrinsic_with_pair( + client, + Alice.into(), + SudoCall::sudo { + call: Box::new(GluttonCall::set_compute { compute: *compute_percent }.into()), + }, + Some(last_nonce), + ); + last_nonce += 1; + extrinsics.push(set_compute); + + let set_storage = generate_extrinsic_with_pair( + client, + Alice.into(), + SudoCall::sudo { + call: Box::new(GluttonCall::set_storage { storage: *storage_percent }.into()), + }, + Some(last_nonce), + ); + extrinsics.push(set_storage); + + let mut block_builder = client.init_block_builder(Some(validation_data), Default::default()); + + for extrinsic in extrinsics { + block_builder.push(extrinsic).unwrap(); + } + + block_builder.build_parachain_block(*parent_header.state_root()) +} + +criterion_group!(benches, benchmark_block_validation); +criterion_main!(benches); diff --git a/cumulus/test/service/src/bench_utils.rs b/cumulus/test/service/src/bench_utils.rs new file mode 100644 index 0000000000..3794f74d04 --- /dev/null +++ b/cumulus/test/service/src/bench_utils.rs @@ -0,0 +1,262 @@ +// This file is part of Cumulus. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use codec::Encode; + +use crate::{construct_extrinsic, Client as TestClient}; +use cumulus_primitives_core::{relay_chain::AccountId, PersistedValidationData}; +use cumulus_primitives_parachain_inherent::ParachainInherentData; +use cumulus_test_relay_sproof_builder::RelayStateSproofBuilder; +use cumulus_test_runtime::{ + BalancesCall, GluttonCall, NodeBlock, SudoCall, UncheckedExtrinsic, WASM_BINARY, +}; +use frame_system_rpc_runtime_api::AccountNonceApi; +use polkadot_primitives::HeadData; +use sc_block_builder::BlockBuilderProvider; +use sc_client_api::UsageProvider; +use sc_consensus::{ + block_import::{BlockImportParams, ForkChoiceStrategy}, + BlockImport, ImportResult, StateAction, +}; +use sc_executor::DEFAULT_HEAP_ALLOC_STRATEGY; +use sc_executor_common::runtime_blob::RuntimeBlob; +use sp_api::ProvideRuntimeApi; +use sp_arithmetic::Perbill; +use sp_blockchain::{ApplyExtrinsicFailed::Validity, Error::ApplyExtrinsicFailed}; +use sp_consensus::BlockOrigin; +use sp_core::{sr25519, Pair}; +use sp_keyring::Sr25519Keyring::Alice; +use sp_runtime::{ + transaction_validity::{InvalidTransaction, TransactionValidityError}, + AccountId32, OpaqueExtrinsic, +}; + +/// Accounts to use for transfer transactions. Enough for 5000 transactions. +const NUM_ACCOUNTS: usize = 10000; + +/// Create accounts by deriving from Alice +pub fn create_benchmark_accounts() -> (Vec, Vec, Vec) { + let accounts: Vec = (0..NUM_ACCOUNTS) + .map(|idx| { + Pair::from_string(&format!("{}/{}", Alice.to_seed(), idx), None) + .expect("Creates account pair") + }) + .collect(); + let account_ids = accounts + .iter() + .map(|account| AccountId::from(account.public())) + .collect::>(); + let (src_accounts, dst_accounts) = accounts.split_at(NUM_ACCOUNTS / 2); + (src_accounts.to_vec(), dst_accounts.to_vec(), account_ids) +} + +/// Create a timestamp extrinsic ahead by `MinimumPeriod` of the last known timestamp +pub fn extrinsic_set_time(client: &TestClient) -> OpaqueExtrinsic { + let best_number = client.usage_info().chain.best_number; + + let timestamp = best_number as u64 * cumulus_test_runtime::MinimumPeriod::get(); + cumulus_test_runtime::UncheckedExtrinsic { + signature: None, + function: cumulus_test_runtime::RuntimeCall::Timestamp(pallet_timestamp::Call::set { + now: timestamp, + }), + } + .into() +} + +/// Create a set validation data extrinsic +pub fn extrinsic_set_validation_data( + parent_header: cumulus_test_runtime::Header, +) -> OpaqueExtrinsic { + let sproof_builder = RelayStateSproofBuilder { para_id: 100.into(), ..Default::default() }; + let parent_head = HeadData(parent_header.encode()); + let (relay_parent_storage_root, relay_chain_state) = sproof_builder.into_state_root_and_proof(); + let data = ParachainInherentData { + validation_data: PersistedValidationData { + parent_head, + relay_parent_number: 10, + relay_parent_storage_root, + max_pov_size: 10000, + }, + relay_chain_state, + downward_messages: Default::default(), + horizontal_messages: Default::default(), + }; + + cumulus_test_runtime::UncheckedExtrinsic { + signature: None, + function: cumulus_test_runtime::RuntimeCall::ParachainSystem( + cumulus_pallet_parachain_system::Call::set_validation_data { data }, + ), + } + .into() +} + +/// Import block into the given client and make sure the import was successful +pub async fn import_block(mut client: &TestClient, block: &NodeBlock, import_existing: bool) { + let mut params = BlockImportParams::new(BlockOrigin::File, block.header.clone()); + params.body = Some(block.extrinsics.clone()); + params.state_action = StateAction::Execute; + params.fork_choice = Some(ForkChoiceStrategy::LongestChain); + params.import_existing = import_existing; + let import_result = client.import_block(params).await; + assert!( + matches!(import_result, Ok(ImportResult::Imported(_))), + "Unexpected block import result: {:?}!", + import_result + ); +} + +/// Creates transfer extrinsics pair-wise from elements of `src_accounts` to `dst_accounts`. +pub fn create_benchmarking_transfer_extrinsics( + client: &TestClient, + src_accounts: &[sr25519::Pair], + dst_accounts: &[sr25519::Pair], +) -> (usize, Vec) { + // Add as many tranfer extrinsics as possible into a single block. + let mut block_builder = client.new_block(Default::default()).unwrap(); + let mut max_transfer_count = 0; + let mut extrinsics = Vec::new(); + // Every block needs one timestamp extrinsic. + let time_ext = extrinsic_set_time(client); + extrinsics.push(time_ext); + + // Every block needs tone set_validation_data extrinsic. + let parent_hash = client.usage_info().chain.best_hash; + let parent_header = client.header(parent_hash).expect("Just fetched this hash.").unwrap(); + let set_validation_data_extrinsic = extrinsic_set_validation_data(parent_header); + extrinsics.push(set_validation_data_extrinsic); + + for (src, dst) in src_accounts.iter().zip(dst_accounts.iter()) { + let extrinsic: UncheckedExtrinsic = construct_extrinsic( + client, + BalancesCall::transfer_keep_alive { dest: AccountId::from(dst.public()), value: 10000 }, + src.clone(), + Some(0), + ); + + match block_builder.push(extrinsic.clone().into()) { + Ok(_) => {}, + Err(ApplyExtrinsicFailed(Validity(TransactionValidityError::Invalid( + InvalidTransaction::ExhaustsResources, + )))) => break, + Err(error) => panic!("{}", error), + } + + extrinsics.push(extrinsic.into()); + max_transfer_count += 1; + } + + if max_transfer_count >= src_accounts.len() { + panic!("Block could fit more transfers, increase NUM_ACCOUNTS to generate more accounts."); + } + + (max_transfer_count, extrinsics) +} + +/// Prepare cumulus test runtime for execution +pub fn get_wasm_module() -> Box { + let blob = RuntimeBlob::uncompress_if_needed( + WASM_BINARY.expect("You need to build the WASM binaries to run the benchmark!"), + ) + .unwrap(); + + let config = sc_executor_wasmtime::Config { + allow_missing_func_imports: true, + cache_path: None, + semantics: sc_executor_wasmtime::Semantics { + heap_alloc_strategy: DEFAULT_HEAP_ALLOC_STRATEGY, + instantiation_strategy: sc_executor::WasmtimeInstantiationStrategy::PoolingCopyOnWrite, + deterministic_stack_limit: None, + canonicalize_nans: false, + parallel_compilation: true, + wasm_multi_value: false, + wasm_bulk_memory: false, + wasm_reference_types: false, + wasm_simd: false, + }, + }; + Box::new( + sc_executor_wasmtime::create_runtime::(blob, config) + .expect("Unable to create wasm module."), + ) +} + +/// Create a block containing setup extrinsics for the glutton pallet. +pub fn set_glutton_parameters( + client: &TestClient, + initialize: bool, + compute_percent: &Perbill, + storage_percent: &Perbill, +) -> NodeBlock { + let parent_hash = client.usage_info().chain.best_hash; + let parent_header = client.header(parent_hash).expect("Just fetched this hash.").unwrap(); + + let mut last_nonce = client + .runtime_api() + .account_nonce(parent_hash, Alice.into()) + .expect("Fetching account nonce works; qed"); + + let mut extrinsics = vec![]; + if initialize { + // Initialize the pallet + extrinsics.push(construct_extrinsic( + client, + SudoCall::sudo { + call: Box::new( + GluttonCall::initialize_pallet { new_count: 5000, witness_count: None }.into(), + ), + }, + Alice.into(), + Some(last_nonce), + )); + last_nonce += 1; + } + + // Set compute weight that should be consumed per block + let set_compute = construct_extrinsic( + client, + SudoCall::sudo { + call: Box::new(GluttonCall::set_compute { compute: *compute_percent }.into()), + }, + Alice.into(), + Some(last_nonce), + ); + last_nonce += 1; + extrinsics.push(set_compute); + + // Set storage weight that should be consumed per block + let set_storage = construct_extrinsic( + client, + SudoCall::sudo { + call: Box::new(GluttonCall::set_storage { storage: *storage_percent }.into()), + }, + Alice.into(), + Some(last_nonce), + ); + extrinsics.push(set_storage); + + let mut block_builder = client.new_block(Default::default()).unwrap(); + block_builder.push(extrinsic_set_time(client)).unwrap(); + block_builder.push(extrinsic_set_validation_data(parent_header)).unwrap(); + for extrinsic in extrinsics { + block_builder.push(extrinsic.into()).unwrap(); + } + + let built_block = block_builder.build().unwrap(); + built_block.block +} diff --git a/cumulus/test/service/src/chain_spec.rs b/cumulus/test/service/src/chain_spec.rs index 5ae77084b6..84604dffc6 100644 --- a/cumulus/test/service/src/chain_spec.rs +++ b/cumulus/test/service/src/chain_spec.rs @@ -80,12 +80,22 @@ where } /// Get the chain spec for a specific parachain ID. -pub fn get_chain_spec(id: ParaId) -> ChainSpec { +/// The given accounts are initialized with funds in addition +/// to the default known accounts. +pub fn get_chain_spec_with_extra_endowed( + id: ParaId, + extra_endowed_accounts: Vec, +) -> ChainSpec { ChainSpec::from_genesis( "Local Testnet", "local_testnet", ChainType::Local, - move || GenesisExt { runtime_genesis_config: local_testnet_genesis(), para_id: id }, + move || GenesisExt { + runtime_genesis_config: testnet_genesis_with_default_endowed( + extra_endowed_accounts.clone(), + ), + para_id: id, + }, Vec::new(), None, None, @@ -95,28 +105,36 @@ pub fn get_chain_spec(id: ParaId) -> ChainSpec { ) } -/// Local testnet genesis for testing. -pub fn local_testnet_genesis() -> cumulus_test_runtime::GenesisConfig { - testnet_genesis( - get_account_id_from_seed::("Alice"), - vec![ - get_account_id_from_seed::("Alice"), - get_account_id_from_seed::("Bob"), - get_account_id_from_seed::("Charlie"), - get_account_id_from_seed::("Dave"), - get_account_id_from_seed::("Eve"), - get_account_id_from_seed::("Ferdie"), - get_account_id_from_seed::("Alice//stash"), - get_account_id_from_seed::("Bob//stash"), - get_account_id_from_seed::("Charlie//stash"), - get_account_id_from_seed::("Dave//stash"), - get_account_id_from_seed::("Eve//stash"), - get_account_id_from_seed::("Ferdie//stash"), - ], - ) +/// Get the chain spec for a specific parachain ID. +pub fn get_chain_spec(id: ParaId) -> ChainSpec { + get_chain_spec_with_extra_endowed(id, Default::default()) } -fn testnet_genesis( +/// Local testnet genesis for testing. +pub fn testnet_genesis_with_default_endowed( + mut extra_endowed_accounts: Vec, +) -> cumulus_test_runtime::GenesisConfig { + let mut endowed = vec![ + get_account_id_from_seed::("Alice"), + get_account_id_from_seed::("Bob"), + get_account_id_from_seed::("Charlie"), + get_account_id_from_seed::("Dave"), + get_account_id_from_seed::("Eve"), + get_account_id_from_seed::("Ferdie"), + get_account_id_from_seed::("Alice//stash"), + get_account_id_from_seed::("Bob//stash"), + get_account_id_from_seed::("Charlie//stash"), + get_account_id_from_seed::("Dave//stash"), + get_account_id_from_seed::("Eve//stash"), + get_account_id_from_seed::("Ferdie//stash"), + ]; + endowed.append(&mut extra_endowed_accounts); + + testnet_genesis(get_account_id_from_seed::("Alice"), endowed) +} + +/// Creates a local testnet genesis with endowed accounts. +pub fn testnet_genesis( root_key: AccountId, endowed_accounts: Vec, ) -> cumulus_test_runtime::GenesisConfig { @@ -126,6 +144,7 @@ fn testnet_genesis( .expect("WASM binary was not build, please build it!") .to_vec(), }, + glutton: Default::default(), parachain_system: Default::default(), balances: cumulus_test_runtime::BalancesConfig { balances: endowed_accounts.iter().cloned().map(|k| (k, 1 << 60)).collect(), diff --git a/cumulus/test/service/src/lib.rs b/cumulus/test/service/src/lib.rs index c97339f7fe..20e7d7f194 100644 --- a/cumulus/test/service/src/lib.rs +++ b/cumulus/test/service/src/lib.rs @@ -18,9 +18,13 @@ #![warn(missing_docs)] +/// Utilities used for benchmarking +pub mod bench_utils; + pub mod chain_spec; mod genesis; +use runtime::AccountId; use sc_executor::{HeapAllocStrategy, WasmExecutor, DEFAULT_HEAP_ALLOC_STRATEGY}; use std::{ future::Future, @@ -501,6 +505,7 @@ pub struct TestNodeBuilder { storage_update_func_relay_chain: Option>, consensus: Consensus, relay_chain_full_node_url: Vec, + endowed_accounts: Vec, } impl TestNodeBuilder { @@ -523,6 +528,7 @@ impl TestNodeBuilder { storage_update_func_relay_chain: None, consensus: Consensus::RelayChain, relay_chain_full_node_url: vec![], + endowed_accounts: Default::default(), } } @@ -629,6 +635,12 @@ impl TestNodeBuilder { self } + /// Accounts which will have an initial balance. + pub fn endowed_accounts(mut self, accounts: Vec) -> TestNodeBuilder { + self.endowed_accounts = accounts; + self + } + /// Build the [`TestNode`]. pub async fn build(self) -> TestNode { let parachain_config = node_config( @@ -639,6 +651,7 @@ impl TestNodeBuilder { self.parachain_nodes_exclusive, self.para_id, self.collator_key.is_some(), + self.endowed_accounts, ) .expect("could not generate Configuration"); @@ -692,12 +705,14 @@ pub fn node_config( nodes_exlusive: bool, para_id: ParaId, is_collator: bool, + endowed_accounts: Vec, ) -> Result { let base_path = BasePath::new_temp_dir()?; let root = base_path.path().join(format!("cumulus_test_service_{}", key)); let role = if is_collator { Role::Authority } else { Role::Full }; let key_seed = key.to_seed(); - let mut spec = Box::new(chain_spec::get_chain_spec(para_id)); + let mut spec = + Box::new(chain_spec::get_chain_spec_with_extra_endowed(para_id, endowed_accounts)); let mut storage = spec.as_storage_builder().build_storage().expect("could not build storage"); @@ -740,14 +755,15 @@ pub fn node_config( state_pruning: Some(PruningMode::ArchiveAll), blocks_pruning: BlocksPruning::KeepAll, chain_spec: spec, - wasm_method: WasmExecutionMethod::default(), - // NOTE: we enforce the use of the native runtime to make the errors more debuggable + wasm_method: WasmExecutionMethod::Compiled { + instantiation_strategy: sc_executor_wasmtime::InstantiationStrategy::PoolingCopyOnWrite, + }, execution_strategies: ExecutionStrategies { - syncing: sc_client_api::ExecutionStrategy::NativeWhenPossible, - importing: sc_client_api::ExecutionStrategy::NativeWhenPossible, - block_construction: sc_client_api::ExecutionStrategy::NativeWhenPossible, - offchain_worker: sc_client_api::ExecutionStrategy::NativeWhenPossible, - other: sc_client_api::ExecutionStrategy::NativeWhenPossible, + syncing: sc_client_api::ExecutionStrategy::AlwaysWasm, + importing: sc_client_api::ExecutionStrategy::AlwaysWasm, + block_construction: sc_client_api::ExecutionStrategy::AlwaysWasm, + offchain_worker: sc_client_api::ExecutionStrategy::AlwaysWasm, + other: sc_client_api::ExecutionStrategy::AlwaysWasm, }, rpc_addr: None, rpc_max_connections: Default::default(),