diff --git a/Cargo.lock b/Cargo.lock index 97bdc9351357..aa780a7a695b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1615,6 +1615,22 @@ dependencies = [ "syn 2.0.77", ] +[[package]] +name = "bip32" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa13fae8b6255872fd86f7faf4b41168661d7d78609f7bfe6771b85c6739a15b" +dependencies = [ + "bs58", + "hmac 0.12.1", + "k256", + "rand_core 0.6.4", + "ripemd", + "sha2 0.10.8", + "subtle 2.5.0", + "zeroize", +] + [[package]] name = "bip39" version = "2.0.0" @@ -2563,6 +2579,7 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4" dependencies = [ + "sha2 0.10.8", "tinyvec", ] @@ -4776,6 +4793,7 @@ dependencies = [ "pallet-transaction-payment", "parity-scale-codec", "scale-info", + "serde_json", "sp-api 26.0.0", "sp-block-builder", "sp-consensus-aura", @@ -4783,6 +4801,7 @@ dependencies = [ "sp-genesis-builder", "sp-inherents", "sp-io 30.0.0", + "sp-keyring", "sp-offchain", "sp-runtime 31.0.1", "sp-session", @@ -4859,6 +4878,7 @@ dependencies = [ "sp-consensus", "sp-consensus-aura", "sp-core 28.0.0", + "sp-genesis-builder", "sp-io 30.0.0", "sp-keyring", "sp-runtime 31.0.1", @@ -6204,16 +6224,21 @@ dependencies = [ "chrono", "clap 4.5.13", "comfy-table", + "cumulus-client-parachain-inherent", + "cumulus-primitives-proof-size-hostfunction", "frame-benchmarking", "frame-support", "frame-system", "gethostname", "handlebars", + "hex", "itertools 0.11.0", "lazy_static", "linked-hash-map", "log", "parity-scale-codec", + "polkadot-parachain-primitives", + "polkadot-primitives", "rand", "rand_pcg", "sc-block-builder", @@ -6227,8 +6252,10 @@ dependencies = [ "serde", "serde_json", "sp-api 26.0.0", + "sp-block-builder", "sp-blockchain", "sp-core 28.0.0", + "sp-crypto-hashing 0.1.0", "sp-database", "sp-externalities 0.25.0", "sp-genesis-builder", @@ -6238,8 +6265,13 @@ dependencies = [ "sp-runtime 31.0.1", "sp-state-machine 0.35.0", "sp-storage 19.0.0", + "sp-timestamp", + "sp-transaction-pool", "sp-trie 29.0.0", + "sp-version 29.0.0", "sp-wasm-interface 20.0.0", + "subxt", + "subxt-signer", "thiserror", "thousands", ] @@ -8348,6 +8380,16 @@ dependencies = [ "cpufeatures", ] +[[package]] +name = "keccak-hash" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b286e6b663fb926e1eeb68528e69cb70ed46c6d65871a21b2215ae8154c6d3c" +dependencies = [ + "primitive-types", + "tiny-keccak", +] + [[package]] name = "keccak-hasher" version = "0.16.0" @@ -17670,6 +17712,15 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "ripemd" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd124222d17ad93a644ed9d011a40f4fb64aa54275c08cc216524a9ea82fb09f" +dependencies = [ + "digest 0.10.7", +] + [[package]] name = "rle-decode-fast" version = "1.0.3" @@ -24285,10 +24336,12 @@ version = "0.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f49888ae6ae90fe01b471193528eea5bd4ed52d8eecd2d13f4a2333b87388850" dependencies = [ + "bip32", "bip39", "cfg-if", "hex", "hmac 0.12.1", + "keccak-hash", "parity-scale-codec", "pbkdf2", "regex", diff --git a/cumulus/client/parachain-inherent/src/mock.rs b/cumulus/client/parachain-inherent/src/mock.rs index a3f881e6ef9d..950cba2aaa7d 100644 --- a/cumulus/client/parachain-inherent/src/mock.rs +++ b/cumulus/client/parachain-inherent/src/mock.rs @@ -45,6 +45,7 @@ pub const RELAY_CHAIN_SLOT_DURATION_MILLIS: u32 = 6000; /// in addition to the messages themselves, you must provide some information about /// your parachain's configuration in order to mock the MQC heads properly. /// See [`MockXcmConfig`] for more information +#[derive(Default)] pub struct MockValidationDataInherentDataProvider { /// The current block number of the local block chain (the parachain). pub current_para_block: u32, diff --git a/cumulus/polkadot-parachain/polkadot-parachain-lib/Cargo.toml b/cumulus/polkadot-parachain/polkadot-parachain-lib/Cargo.toml index 066cbfae53ae..733ea09ef2b8 100644 --- a/cumulus/polkadot-parachain/polkadot-parachain-lib/Cargo.toml +++ b/cumulus/polkadot-parachain/polkadot-parachain-lib/Cargo.toml @@ -93,27 +93,27 @@ wait-timeout = { workspace = true } [features] default = [] rococo-native = [ - "polkadot-cli/rococo-native", + "polkadot-cli/rococo-native", ] westend-native = [ - "polkadot-cli/westend-native", + "polkadot-cli/westend-native", ] runtime-benchmarks = [ - "cumulus-primitives-core/runtime-benchmarks", - "frame-benchmarking-cli/runtime-benchmarks", - "frame-benchmarking/runtime-benchmarks", - "frame-support/runtime-benchmarks", - "parachains-common/runtime-benchmarks", - "polkadot-cli/runtime-benchmarks", - "polkadot-primitives/runtime-benchmarks", - "sc-client-db/runtime-benchmarks", - "sc-service/runtime-benchmarks", - "sp-runtime/runtime-benchmarks", + "cumulus-primitives-core/runtime-benchmarks", + "frame-benchmarking-cli/runtime-benchmarks", + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "parachains-common/runtime-benchmarks", + "polkadot-cli/runtime-benchmarks", + "polkadot-primitives/runtime-benchmarks", + "sc-client-db/runtime-benchmarks", + "sc-service/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", ] try-runtime = [ - "frame-support/try-runtime", - "frame-try-runtime/try-runtime", - "pallet-transaction-payment/try-runtime", - "polkadot-cli/try-runtime", - "sp-runtime/try-runtime", + "frame-support/try-runtime", + "frame-try-runtime/try-runtime", + "pallet-transaction-payment/try-runtime", + "polkadot-cli/try-runtime", + "sp-runtime/try-runtime", ] diff --git a/cumulus/polkadot-parachain/polkadot-parachain-lib/src/command.rs b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/command.rs index 63562f192aee..7e6fd564b1c7 100644 --- a/cumulus/polkadot-parachain/polkadot-parachain-lib/src/command.rs +++ b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/command.rs @@ -33,7 +33,7 @@ use crate::{ #[cfg(feature = "runtime-benchmarks")] use cumulus_client_service::storage_proof_size::HostFunctions as ReclaimHostFunctions; use cumulus_primitives_core::ParaId; -use frame_benchmarking_cli::{BenchmarkCmd, SUBSTRATE_REFERENCE_HARDWARE}; +use frame_benchmarking_cli::{BenchmarkCmd, ExtrinsicBuilder, SUBSTRATE_REFERENCE_HARDWARE}; use log::info; use sc_cli::{Result, SubstrateCli}; use sp_runtime::traits::AccountIdConversion; @@ -47,6 +47,21 @@ pub struct RunConfig { pub chain_spec_loader: Box, /// A custom runtime resolver. pub runtime_resolver: Box, + /// Extrinsic builder to use in benchmarks. + /// + /// Some benchmarks attempt to build blocks. This extrinsic builder + /// is used to populate these blocks with extrinsics. + pub extrinsic_builder: Option>, +} + +impl RunConfig { + /// Create a new `RunConfig` + pub fn new( + runtime_resolver: Box, + chain_spec_loader: Box, + ) -> Self { + RunConfig { chain_spec_loader, runtime_resolver, extrinsic_builder: None } + } } pub fn new_aura_node_spec( @@ -199,6 +214,14 @@ pub fn run(cmd_config: RunConfig) -> Result<() }), BenchmarkCmd::Machine(cmd) => runner.sync_run(|config| cmd.run(&config, SUBSTRATE_REFERENCE_HARDWARE.clone())), + BenchmarkCmd::Overhead(cmd) => runner.sync_run(|config| { + let node = new_node_spec( + &config, + &cmd_config.runtime_resolver, + &cli.node_extra_args(), + )?; + node.run_benchmark_overhead_cmd(cmd, cmd_config.extrinsic_builder) + }), #[allow(unreachable_patterns)] _ => Err("Benchmarking sub-command unsupported or compilation feature missing. \ Make sure to compile with --features=runtime-benchmarks \ diff --git a/cumulus/polkadot-parachain/polkadot-parachain-lib/src/common/command.rs b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/common/command.rs index e2826826d40e..969ccd280005 100644 --- a/cumulus/polkadot-parachain/polkadot-parachain-lib/src/common/command.rs +++ b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/common/command.rs @@ -16,9 +16,9 @@ use crate::common::spec::NodeSpec; use cumulus_client_cli::ExportGenesisHeadCommand; -use frame_benchmarking_cli::BlockCmd; #[cfg(any(feature = "runtime-benchmarks"))] use frame_benchmarking_cli::StorageCmd; +use frame_benchmarking_cli::{BlockCmd, ExtrinsicBuilder, OverheadCmd}; use sc_cli::{CheckBlockCmd, ExportBlocksCmd, ExportStateCmd, ImportBlocksCmd, RevertCmd}; use sc_service::{Configuration, TaskManager}; use std::{future::Future, pin::Pin}; @@ -77,6 +77,12 @@ pub trait NodeCommandRunner { config: Configuration, cmd: &StorageCmd, ) -> SyncCmdResult; + + fn run_benchmark_overhead_cmd( + self: Box, + cmd: &OverheadCmd, + ext_builder: Option>, + ) -> SyncCmdResult; } impl NodeCommandRunner for T @@ -158,4 +164,12 @@ where cmd.run(config, partial.client, db, storage) } + + fn run_benchmark_overhead_cmd( + self: Box, + cmd: &OverheadCmd, + ext_builder: Option>, + ) -> SyncCmdResult { + cmd.run_with_extrinsic_builder::<::Block, ()>(ext_builder) + } } diff --git a/cumulus/polkadot-parachain/src/main.rs b/cumulus/polkadot-parachain/src/main.rs index f2dce552c51a..c5c9997875fe 100644 --- a/cumulus/polkadot-parachain/src/main.rs +++ b/cumulus/polkadot-parachain/src/main.rs @@ -46,9 +46,6 @@ impl CliConfigT for CliConfig { fn main() -> color_eyre::eyre::Result<()> { color_eyre::install()?; - let config = RunConfig { - chain_spec_loader: Box::new(chain_spec::ChainSpecLoader), - runtime_resolver: Box::new(chain_spec::RuntimeResolver), - }; + let config = RunConfig::new(Box::new(chain_spec::RuntimeResolver), Box::new(chain_spec::ChainSpecLoader)); Ok(run::(config)?) } diff --git a/cumulus/test/client/src/lib.rs b/cumulus/test/client/src/lib.rs index f26413e441e7..5615bb6eba5d 100644 --- a/cumulus/test/client/src/lib.rs +++ b/cumulus/test/client/src/lib.rs @@ -39,7 +39,7 @@ use sp_consensus_aura::{AuraApi, Slot}; use sp_core::Pair; use sp_io::TestExternalities; use sp_keystore::testing::MemoryKeystore; -use sp_runtime::{generic::Era, traits::Header, BuildStorage, SaturatedConversion}; +use sp_runtime::{generic::Era, traits::Header, BuildStorage, MultiAddress, SaturatedConversion}; use std::sync::Arc; pub use substrate_test_client::*; @@ -157,7 +157,7 @@ pub fn generate_extrinsic_with_pair( UncheckedExtrinsic::new_signed( function, - origin.public().into(), + MultiAddress::Id(origin.public().into()), Signature::Sr25519(signature), extra, ) @@ -180,7 +180,7 @@ pub fn transfer( value: Balance, ) -> UncheckedExtrinsic { let function = RuntimeCall::Balances(pallet_balances::Call::transfer_allow_death { - dest: dest.public().into(), + dest: MultiAddress::Id(dest.public().into()), value, }); diff --git a/cumulus/test/runtime/Cargo.toml b/cumulus/test/runtime/Cargo.toml index 54b83e2dfeda..f6a95bf16010 100644 --- a/cumulus/test/runtime/Cargo.toml +++ b/cumulus/test/runtime/Cargo.toml @@ -11,6 +11,7 @@ workspace = true [dependencies] codec = { features = ["derive"], workspace = true } scale-info = { features = ["derive"], workspace = true } +serde_json = { workspace = true } # Substrate frame-executive = { workspace = true } @@ -38,6 +39,7 @@ sp-session = { workspace = true } sp-consensus-aura = { workspace = true } sp-transaction-pool = { workspace = true } sp-version = { workspace = true } +sp-keyring = { workspace = true } # Cumulus cumulus-pallet-parachain-system = { workspace = true } @@ -89,6 +91,8 @@ std = [ "sp-transaction-pool/std", "sp-version/std", "substrate-wasm-builder", + "serde_json/std", + "sp-keyring/std" ] increment-spec-version = [] elastic-scaling = [] diff --git a/cumulus/test/runtime/src/genesis_config_presets.rs b/cumulus/test/runtime/src/genesis_config_presets.rs new file mode 100644 index 000000000000..327e8437f258 --- /dev/null +++ b/cumulus/test/runtime/src/genesis_config_presets.rs @@ -0,0 +1,88 @@ +// This file is part of Substrate. + +// 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 super::{ + AccountId, AuraConfig, AuraId, BalancesConfig, ParachainInfoConfig, RuntimeGenesisConfig, + SudoConfig, +}; +use alloc::{vec, vec::Vec}; + +use cumulus_primitives_core::ParaId; +use sp_genesis_builder::PresetId; +use sp_keyring::Sr25519Keyring; + +fn cumulus_test_runtime( + invulnerables: Vec, + endowed_accounts: Vec, + id: ParaId, +) -> serde_json::Value { + let config = RuntimeGenesisConfig { + system: Default::default(), + balances: BalancesConfig { + balances: endowed_accounts.iter().cloned().map(|k| (k, 1 << 60)).collect(), + }, + sudo: SudoConfig { key: Some(Sr25519Keyring::Alice.public().into()) }, + transaction_payment: Default::default(), + test_pallet: Default::default(), + parachain_info: ParachainInfoConfig { parachain_id: id, ..Default::default() }, + // no need to pass anything to aura, in fact it will panic if we do. Session will take care + // of this. `aura: Default::default()` + aura: AuraConfig { authorities: invulnerables }, + aura_ext: Default::default(), + parachain_system: Default::default(), + glutton: Default::default(), + }; + + serde_json::to_value(config).expect("Could not build genesis config.") +} + +pub fn testnet_genesis_with_default_endowed(self_para_id: ParaId) -> serde_json::Value { + let endowed = Sr25519Keyring::iter().map(|x| x.to_account_id()).collect::>(); + + let invulnerables = vec![ + Sr25519Keyring::Alice, + Sr25519Keyring::Bob, + Sr25519Keyring::Charlie, + Sr25519Keyring::Dave, + Sr25519Keyring::Eve, + Sr25519Keyring::Ferdie, + ] + .into_iter() + .map(|x| x.public().into()) + .collect::>(); + cumulus_test_runtime(invulnerables, endowed, self_para_id) +} + +pub fn preset_names() -> Vec { + vec![ + PresetId::from(sp_genesis_builder::DEV_RUNTIME_PRESET), + PresetId::from(sp_genesis_builder::LOCAL_TESTNET_RUNTIME_PRESET), + ] +} +pub fn get_preset(id: &PresetId) -> Option> { + let patch = match id.try_into() { + Ok(sp_genesis_builder::DEV_RUNTIME_PRESET) | + Ok(sp_genesis_builder::LOCAL_TESTNET_RUNTIME_PRESET) => + testnet_genesis_with_default_endowed(100.into()), + _ => return None, + }; + Some( + serde_json::to_string(&patch) + .expect("serialization to json is expected to work. qed.") + .into_bytes(), + ) +} diff --git a/cumulus/test/runtime/src/lib.rs b/cumulus/test/runtime/src/lib.rs index 861d55c77cdc..45cbed6ded6e 100644 --- a/cumulus/test/runtime/src/lib.rs +++ b/cumulus/test/runtime/src/lib.rs @@ -32,6 +32,7 @@ pub mod elastic_scaling { include!(concat!(env!("OUT_DIR"), "/wasm_binary_elastic_scaling.rs")); } +mod genesis_config_presets; mod test_pallet; extern crate alloc; @@ -45,9 +46,9 @@ use sp_core::{ConstBool, ConstU32, ConstU64, OpaqueMetadata}; use cumulus_primitives_core::{ClaimQueueOffset, CoreSelector}; use sp_runtime::{ create_runtime_str, generic, impl_opaque_keys, - traits::{BlakeTwo256, Block as BlockT, IdentifyAccount, IdentityLookup, Verify}, + traits::{BlakeTwo256, Block as BlockT, IdentifyAccount, Verify}, transaction_validity::{TransactionSource, TransactionValidity}, - ApplyExtrinsicResult, MultiSignature, + ApplyExtrinsicResult, MultiAddress, MultiSignature, }; #[cfg(feature = "std")] use sp_version::NativeVersion; @@ -208,8 +209,6 @@ parameter_types! { impl frame_system::Config for Runtime { /// The identifier used to distinguish between accounts. type AccountId = AccountId; - /// The lookup mechanism to get account ID from whatever is passed in dispatchers. - type Lookup = IdentityLookup; /// The index type for storing how many extrinsics an account has signed. type Nonce = Nonce; /// The type for hashing blocks and tries. @@ -361,7 +360,7 @@ pub type AccountId = <::Signer as IdentifyAccount>::Account pub type NodeBlock = generic::Block; /// The address format for describing accounts. -pub type Address = AccountId; +pub type Address = MultiAddress; /// Block header type as expected by this runtime. pub type Header = generic::Header; /// Block type as expected by this runtime. @@ -542,11 +541,11 @@ impl_runtime_apis! { } fn get_preset(id: &Option) -> Option> { - get_preset::(id, |_| None) + get_preset::(id, genesis_config_presets::get_preset) } fn preset_names() -> Vec { - vec![] + genesis_config_presets::preset_names() } } } diff --git a/cumulus/test/service/Cargo.toml b/cumulus/test/service/Cargo.toml index a1b70c523952..e9cd1277d564 100644 --- a/cumulus/test/service/Cargo.toml +++ b/cumulus/test/service/Cargo.toml @@ -49,6 +49,7 @@ sp-core = { workspace = true, default-features = true } sp-io = { workspace = true, default-features = true } sp-api = { workspace = true, default-features = true } sp-keyring = { workspace = true, default-features = true } +sp-genesis-builder = { workspace = true, default-features = true } sp-runtime = { workspace = true } sp-state-machine = { workspace = true, default-features = true } sp-tracing = { workspace = true, default-features = true } diff --git a/cumulus/test/service/src/bench_utils.rs b/cumulus/test/service/src/bench_utils.rs index 67ffbdd1d212..ce1428dee88c 100644 --- a/cumulus/test/service/src/bench_utils.rs +++ b/cumulus/test/service/src/bench_utils.rs @@ -41,7 +41,7 @@ use sp_core::{sr25519, Pair}; use sp_keyring::Sr25519Keyring::Alice; use sp_runtime::{ transaction_validity::{InvalidTransaction, TransactionValidityError}, - AccountId32, FixedU64, OpaqueExtrinsic, + AccountId32, FixedU64, MultiAddress, OpaqueExtrinsic, }; /// Accounts to use for transfer transactions. Enough for 5000 transactions. @@ -153,7 +153,10 @@ pub fn create_benchmarking_transfer_extrinsics( 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 }, + BalancesCall::transfer_keep_alive { + dest: MultiAddress::Id(AccountId::from(dst.public())), + value: 10000, + }, src.clone(), Some(0), ); diff --git a/cumulus/test/service/src/chain_spec.rs b/cumulus/test/service/src/chain_spec.rs index ae71028ad486..b55c8a025c4c 100644 --- a/cumulus/test/service/src/chain_spec.rs +++ b/cumulus/test/service/src/chain_spec.rs @@ -16,25 +16,17 @@ #![allow(missing_docs)] +use cumulus_client_service::ParachainHostFunctions; use cumulus_primitives_core::ParaId; -use cumulus_test_runtime::{AccountId, Signature}; -use parachains_common::AuraId; -use sc_chain_spec::{ChainSpecExtension, ChainSpecGroup}; +use cumulus_test_runtime::AccountId; +use sc_chain_spec::{ChainSpecExtension, ChainSpecGroup, GenesisConfigBuilderRuntimeCaller}; use sc_service::ChainType; use serde::{Deserialize, Serialize}; -use sp_core::{sr25519, Pair, Public}; -use sp_runtime::traits::{IdentifyAccount, Verify}; +use serde_json::json; /// Specialized `ChainSpec` for the normal parachain runtime. pub type ChainSpec = sc_service::GenericChainSpec; -/// Helper function to generate a crypto pair from seed -pub fn get_from_seed(seed: &str) -> ::Public { - TPublic::Pair::from_string(&format!("//{}", seed), None) - .expect("static values are valid; qed") - .public() -} - /// The extensions for the [`ChainSpec`]. #[derive(Debug, Clone, PartialEq, Serialize, Deserialize, ChainSpecGroup, ChainSpecExtension)] #[serde(deny_unknown_fields)] @@ -50,16 +42,6 @@ impl Extensions { } } -type AccountPublic = ::Signer; - -/// Helper function to generate an account ID from seed. -pub fn get_account_id_from_seed(seed: &str) -> AccountId -where - AccountPublic: From<::Public>, -{ - AccountPublic::from(get_from_seed::(seed)).into_account() -} - /// Get the chain spec for a specific parachain ID. /// The given accounts are initialized with funds in addition /// to the default known accounts. @@ -68,17 +50,39 @@ pub fn get_chain_spec_with_extra_endowed( extra_endowed_accounts: Vec, code: &[u8], ) -> ChainSpec { + let mut patch_json = json!({ + "balances": { + "balances": extra_endowed_accounts.into_iter().map(|a| (a, 1u64 << 60)).collect::>(), + } + }); + + if let Some(id) = id { + // Merge parachain ID if given, otherwise use the one from the preset. + sc_chain_spec::json_merge( + &mut patch_json, + json!({ + "parachainInfo": { + "parachainId": id, + }, + }), + ); + }; + + let runtime_caller = GenesisConfigBuilderRuntimeCaller::::new(code); + let mut development_preset = runtime_caller + .get_named_preset(Some(&sp_genesis_builder::LOCAL_TESTNET_RUNTIME_PRESET.to_string())) + .expect("development preset is available on test runtime; qed"); + + sc_chain_spec::json_merge(&mut development_preset, patch_json.into()); + ChainSpec::builder( code, Extensions { para_id: id.unwrap_or(cumulus_test_runtime::PARACHAIN_ID.into()).into() }, ) .with_name("Local Testnet") - .with_id("local_testnet") + .with_id(sp_genesis_builder::LOCAL_TESTNET_RUNTIME_PRESET) .with_chain_type(ChainType::Local) - .with_genesis_config_patch(testnet_genesis_with_default_endowed( - extra_endowed_accounts.clone(), - id, - )) + .with_genesis_config(development_preset) .build() } @@ -100,66 +104,3 @@ pub fn get_elastic_scaling_chain_spec(id: Option) -> ChainSpec { .expect("WASM binary was not built, please build it!"), ) } - -/// Local testnet genesis for testing. -pub fn testnet_genesis_with_default_endowed( - mut extra_endowed_accounts: Vec, - self_para_id: Option, -) -> serde_json::Value { - 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); - let invulnerables = vec![ - get_collator_keys_from_seed::("Alice"), - get_collator_keys_from_seed::("Bob"), - get_collator_keys_from_seed::("Charlie"), - get_collator_keys_from_seed::("Dave"), - get_collator_keys_from_seed::("Eve"), - get_collator_keys_from_seed::("Ferdie"), - ]; - testnet_genesis( - get_account_id_from_seed::("Alice"), - invulnerables, - endowed, - self_para_id, - ) -} - -/// Generate collator keys from seed. -/// -/// This function's return type must always match the session keys of the chain in tuple format. -pub fn get_collator_keys_from_seed(seed: &str) -> ::Public { - get_from_seed::(seed) -} - -/// Creates a local testnet genesis with endowed accounts. -pub fn testnet_genesis( - root_key: AccountId, - invulnerables: Vec, - endowed_accounts: Vec, - self_para_id: Option, -) -> serde_json::Value { - let self_para_id = self_para_id.unwrap_or(cumulus_test_runtime::PARACHAIN_ID.into()); - serde_json::json!({ - "balances": cumulus_test_runtime::BalancesConfig { - balances: endowed_accounts.iter().cloned().map(|k| (k, 1 << 60)).collect(), - }, - "sudo": cumulus_test_runtime::SudoConfig { key: Some(root_key) }, - "parachainInfo": { - "parachainId": self_para_id, - }, - "aura": cumulus_test_runtime::AuraConfig { authorities: invulnerables } - }) -} diff --git a/cumulus/test/service/src/lib.rs b/cumulus/test/service/src/lib.rs index 9f93572e9cea..a18e70b43b8a 100644 --- a/cumulus/test/service/src/lib.rs +++ b/cumulus/test/service/src/lib.rs @@ -90,7 +90,7 @@ use sp_arithmetic::traits::SaturatedConversion; use sp_blockchain::HeaderBackend; use sp_core::Pair; use sp_keyring::Sr25519Keyring; -use sp_runtime::{codec::Encode, generic}; +use sp_runtime::{codec::Encode, generic, MultiAddress}; use sp_state_machine::BasicExternalities; use std::sync::Arc; use substrate_test_client::{ @@ -987,7 +987,7 @@ pub fn construct_extrinsic( let signature = raw_payload.using_encoded(|e| caller.sign(e)); runtime::UncheckedExtrinsic::new_signed( function, - caller.public().into(), + MultiAddress::Id(caller.public().into()), runtime::Signature::Sr25519(signature), extra, ) diff --git a/cumulus/zombienet/examples/small_network.toml b/cumulus/zombienet/examples/small_network.toml index ab7265712308..64765566471a 100644 --- a/cumulus/zombienet/examples/small_network.toml +++ b/cumulus/zombienet/examples/small_network.toml @@ -3,23 +3,23 @@ default_image = "parity/polkadot:latest" default_command = "polkadot" chain = "rococo-local" - [[relaychain.nodes]] - name = "alice" - validator = true +[[relaychain.nodes]] +name = "alice" +validator = true - [[relaychain.nodes]] - name = "bob" - validator = true +[[relaychain.nodes]] +name = "bob" +validator = true [[parachains]] id = 2000 cumulus_based = true chain = "asset-hub-rococo-local" - # run charlie as parachain collator - [[parachains.collators]] - name = "charlie" - validator = true - image = "parity/polkadot-parachain:latest" - command = "polkadot-parachain" - args = ["--force-authoring"] +# run charlie as parachain collator +[[parachains.collators]] +name = "charlie" +validator = true +image = "parity/polkadot-parachain:latest" +command = "polkadot-parachain" +args = ["--force-authoring"] diff --git a/polkadot/cli/src/command.rs b/polkadot/cli/src/command.rs index 34ada58bbcdb..a67791b462a6 100644 --- a/polkadot/cli/src/command.rs +++ b/polkadot/cli/src/command.rs @@ -15,12 +15,14 @@ // along with Polkadot. If not, see . use crate::cli::{Cli, Subcommand, NODE_VERSION}; -use frame_benchmarking_cli::{BenchmarkCmd, ExtrinsicFactory, SUBSTRATE_REFERENCE_HARDWARE}; +use frame_benchmarking_cli::{ + BenchmarkCmd, ExtrinsicFactory, SubstrateRemarkBuilder, SUBSTRATE_REFERENCE_HARDWARE, +}; use futures::future::TryFutureExt; use log::info; use polkadot_service::{ self, - benchmarking::{benchmark_inherent_data, RemarkBuilder, TransferKeepAliveBuilder}, + benchmarking::{benchmark_inherent_data, TransferKeepAliveBuilder}, HeaderBackend, IdentifyVariant, }; use sc_cli::SubstrateCli; @@ -404,44 +406,30 @@ pub fn run() -> Result<()> { cmd.run(client.clone()).map_err(Error::SubstrateCli) }), + BenchmarkCmd::Overhead(cmd) => cmd + .run_with_extrinsic_builder::(None) + .map_err(Error::SubstrateCli), // These commands are very similar and can be handled in nearly the same way. - BenchmarkCmd::Extrinsic(_) | BenchmarkCmd::Overhead(_) => - runner.sync_run(|mut config| { - let (client, _, _, _) = polkadot_service::new_chain_ops(&mut config, None)?; - let header = client.header(client.info().genesis_hash).unwrap().unwrap(); - let inherent_data = benchmark_inherent_data(header) - .map_err(|e| format!("generating inherent data: {:?}", e))?; - let remark_builder = - RemarkBuilder::new(client.clone(), config.chain_spec.identify_chain()); - - match cmd { - BenchmarkCmd::Extrinsic(cmd) => { - let tka_builder = TransferKeepAliveBuilder::new( - client.clone(), - Sr25519Keyring::Alice.to_account_id(), - config.chain_spec.identify_chain(), - ); - - let ext_factory = ExtrinsicFactory(vec![ - Box::new(remark_builder), - Box::new(tka_builder), - ]); - - cmd.run(client.clone(), inherent_data, Vec::new(), &ext_factory) - .map_err(Error::SubstrateCli) - }, - BenchmarkCmd::Overhead(cmd) => cmd - .run( - config, - client.clone(), - inherent_data, - Vec::new(), - &remark_builder, - ) - .map_err(Error::SubstrateCli), - _ => unreachable!("Ensured by the outside match; qed"), - } - }), + BenchmarkCmd::Extrinsic(cmd) => runner.sync_run(|mut config| { + let (client, _, _, _) = polkadot_service::new_chain_ops(&mut config, None)?; + let header = client.header(client.info().genesis_hash).unwrap().unwrap(); + let inherent_data = benchmark_inherent_data(header) + .map_err(|e| format!("generating inherent data: {:?}", e))?; + + let remark_builder = SubstrateRemarkBuilder::new_from_client(client.clone())?; + + let tka_builder = TransferKeepAliveBuilder::new( + client.clone(), + Sr25519Keyring::Alice.to_account_id(), + config.chain_spec.identify_chain(), + ); + + let ext_factory = + ExtrinsicFactory(vec![Box::new(remark_builder), Box::new(tka_builder)]); + + cmd.run(client.clone(), inherent_data, Vec::new(), &ext_factory) + .map_err(Error::SubstrateCli) + }), BenchmarkCmd::Pallet(cmd) => { set_default_ss58_version(chain_spec); diff --git a/polkadot/node/service/src/benchmarking.rs b/polkadot/node/service/src/benchmarking.rs index 4dcff2078419..9b38a696e0e8 100644 --- a/polkadot/node/service/src/benchmarking.rs +++ b/polkadot/node/service/src/benchmarking.rs @@ -79,53 +79,6 @@ macro_rules! identify_chain { }; } -/// Generates `System::Remark` extrinsics for the benchmarks. -/// -/// Note: Should only be used for benchmarking. -pub struct RemarkBuilder { - client: Arc, - chain: Chain, -} - -impl RemarkBuilder { - /// Creates a new [`Self`] from the given client. - pub fn new(client: Arc, chain: Chain) -> Self { - Self { client, chain } - } -} - -impl frame_benchmarking_cli::ExtrinsicBuilder for RemarkBuilder { - fn pallet(&self) -> &str { - "system" - } - - fn extrinsic(&self) -> &str { - "remark" - } - - fn build(&self, nonce: u32) -> std::result::Result { - // We apply the extrinsic directly, so let's take some random period. - let period = 128; - let genesis = self.client.usage_info().chain.best_hash; - let signer = Sr25519Keyring::Bob.pair(); - let current_block = 0; - - identify_chain! { - self.chain, - nonce, - current_block, - period, - genesis, - signer, - { - runtime::RuntimeCall::System( - runtime::SystemCall::remark { remark: vec![] } - ) - }, - } - } -} - /// Generates `Balances::TransferKeepAlive` extrinsics for the benchmarks. /// /// Note: Should only be used for benchmarking. diff --git a/substrate/client/chain-spec/src/genesis_block.rs b/substrate/client/chain-spec/src/genesis_block.rs index 3c7b9f64dcd6..d41211cd6206 100644 --- a/substrate/client/chain-spec/src/genesis_block.rs +++ b/substrate/client/chain-spec/src/genesis_block.rs @@ -108,6 +108,15 @@ impl, E: RuntimeVersionOf> GenesisBlockBuilder< ) -> sp_blockchain::Result { let genesis_storage = build_genesis_storage.build_storage().map_err(sp_blockchain::Error::Storage)?; + Self::new_with_storage(genesis_storage, commit_genesis_state, backend, executor) + } + + pub fn new_with_storage( + genesis_storage: Storage, + commit_genesis_state: bool, + backend: Arc, + executor: E, + ) -> sp_blockchain::Result { Ok(Self { genesis_storage, commit_genesis_state, diff --git a/substrate/utils/frame/benchmarking-cli/Cargo.toml b/substrate/utils/frame/benchmarking-cli/Cargo.toml index 4e88e3360e39..08ba1728b74f 100644 --- a/substrate/utils/frame/benchmarking-cli/Cargo.toml +++ b/substrate/utils/frame/benchmarking-cli/Cargo.toml @@ -52,13 +52,25 @@ sp-externalities = { workspace = true, default-features = true } sp-genesis-builder = { workspace = true, default-features = true } sp-inherents = { workspace = true, default-features = true } sp-keystore = { workspace = true, default-features = true } +sp-crypto-hashing = { workspace = true, default-features = true } sp-runtime = { workspace = true, default-features = true } sp-state-machine = { workspace = true, default-features = true } sp-storage = { workspace = true, default-features = true } sp-trie = { workspace = true, default-features = true } +sp-block-builder = { workspace = true, default-features = true } +sp-transaction-pool = { workspace = true, default-features = true } +sp-version = { workspace = true, default-features = true } +sp-timestamp = { workspace = true, default-features = true } sp-io = { workspace = true, default-features = true } sp-wasm-interface = { workspace = true, default-features = true } +subxt = { workspace = true, features = ["substrate-compat", "native"] } +subxt-signer = { workspace = true, features = ["unstable-eth"] } +cumulus-primitives-proof-size-hostfunction = { workspace = true, default-features = true } +cumulus-client-parachain-inherent = { workspace = true, default-features = true } +polkadot-parachain-primitives = { workspace = true, default-features = true } +polkadot-primitives = { workspace = true, default-features = true } gethostname = { workspace = true } +hex = "0.4.3" [features] default = ["rocksdb"] @@ -69,5 +81,7 @@ runtime-benchmarks = [ "sc-client-db/runtime-benchmarks", "sc-service/runtime-benchmarks", "sp-runtime/runtime-benchmarks", + "polkadot-parachain-primitives/runtime-benchmarks", + "polkadot-primitives/runtime-benchmarks" ] rocksdb = ["sc-cli/rocksdb", "sc-client-db/rocksdb"] diff --git a/substrate/utils/frame/benchmarking-cli/src/extrinsic/bench.rs b/substrate/utils/frame/benchmarking-cli/src/extrinsic/bench.rs index f0a7436dc729..dcd8d1530aef 100644 --- a/substrate/utils/frame/benchmarking-cli/src/extrinsic/bench.rs +++ b/substrate/utils/frame/benchmarking-cli/src/extrinsic/bench.rs @@ -31,14 +31,14 @@ use sp_runtime::{ Digest, DigestItem, OpaqueExtrinsic, }; +use super::ExtrinsicBuilder; +use crate::shared::{StatSelect, Stats}; use clap::Args; use log::info; use serde::Serialize; +use sp_trie::proof_size_extension::ProofSizeExt; use std::{marker::PhantomData, sync::Arc, time::Instant}; -use super::ExtrinsicBuilder; -use crate::shared::{StatSelect, Stats}; - /// Parameters to configure an *overhead* benchmark. #[derive(Debug, Default, Serialize, Clone, PartialEq, Args)] pub struct BenchmarkParams { @@ -66,6 +66,7 @@ pub(crate) struct Benchmark { params: BenchmarkParams, inherent_data: sp_inherents::InherentData, digest_items: Vec, + record_proof: bool, _p: PhantomData, } @@ -84,15 +85,19 @@ where params: BenchmarkParams, inherent_data: sp_inherents::InherentData, digest_items: Vec, + record_proof: bool, ) -> Self { - Self { client, params, inherent_data, digest_items, _p: PhantomData } + Self { client, params, inherent_data, digest_items, record_proof, _p: PhantomData } } /// Benchmark a block with only inherents. - pub fn bench_block(&self) -> Result { - let (block, _) = self.build_block(None)?; + /// + /// Returns the Ref time stats and the proof size. + pub fn bench_block(&self) -> Result<(Stats, u64)> { + let (block, _, proof_size) = self.build_block(None)?; let record = self.measure_block(&block)?; - Stats::new(&record) + + Ok((Stats::new(&record)?, proof_size)) } /// Benchmark the time of an extrinsic in a full block. @@ -100,13 +105,14 @@ where /// First benchmarks an empty block, analogous to `bench_block` and use it as baseline. /// Then benchmarks a full block built with the given `ext_builder` and subtracts the baseline /// from the result. - /// This is necessary to account for the time the inherents use. - pub fn bench_extrinsic(&self, ext_builder: &dyn ExtrinsicBuilder) -> Result { - let (block, _) = self.build_block(None)?; + /// This is necessary to account for the time the inherents use. Returns ref time stats and the + /// proof size. + pub fn bench_extrinsic(&self, ext_builder: &dyn ExtrinsicBuilder) -> Result<(Stats, u64)> { + let (block, _, base_proof_size) = self.build_block(None)?; let base = self.measure_block(&block)?; let base_time = Stats::new(&base)?.select(StatSelect::Average); - let (block, num_ext) = self.build_block(Some(ext_builder))?; + let (block, num_ext, proof_size) = self.build_block(Some(ext_builder))?; let num_ext = num_ext.ok_or_else(|| Error::Input("Block was empty".into()))?; let mut records = self.measure_block(&block)?; @@ -117,23 +123,24 @@ where *r = ((*r as f64) / (num_ext as f64)).ceil() as u64; } - Stats::new(&records) + Ok((Stats::new(&records)?, proof_size.saturating_sub(base_proof_size))) } /// Builds a block with some optional extrinsics. /// /// Returns the block and the number of extrinsics in the block - /// that are not inherents. + /// that are not inherents together with the proof size. /// Returns a block with only inherents if `ext_builder` is `None`. fn build_block( &self, ext_builder: Option<&dyn ExtrinsicBuilder>, - ) -> Result<(Block, Option)> { + ) -> Result<(Block, Option, u64)> { let chain = self.client.usage_info().chain; let mut builder = BlockBuilderBuilder::new(&*self.client) .on_parent_block(chain.best_hash) .with_parent_block_number(chain.best_number) .with_inherent_digests(Digest { logs: self.digest_items.clone() }) + .enable_proof_recording() .build()?; // Create and insert the inherents. @@ -146,7 +153,8 @@ where let ext_builder = if let Some(ext_builder) = ext_builder { ext_builder } else { - return Ok((builder.build()?.block, None)) + let proof_size = builder.estimate_block_size(true) - builder.estimate_block_size(false); + return Ok((builder.build()?.block, None, proof_size as u64)) }; // Put as many extrinsics into the block as possible and count them. @@ -167,9 +175,10 @@ where return Err("A Block must hold at least one extrinsic".into()) } info!("Extrinsics per block: {}", num_ext); + let proof_size = builder.estimate_block_size(true) - builder.estimate_block_size(false); let block = builder.build()?.block; - Ok((block, Some(num_ext))) + Ok((block, Some(num_ext), proof_size as u64)) } /// Measures the time that it take to execute a block or an extrinsic. @@ -179,8 +188,15 @@ where info!("Running {} warmups...", self.params.warmup); for _ in 0..self.params.warmup { - self.client - .runtime_api() + let mut runtime_api = self.client.runtime_api(); + if self.record_proof { + runtime_api.record_proof(); + let recorder = runtime_api + .proof_recorder() + .expect("Proof recording is enabled in the line above; qed."); + runtime_api.register_extension(ProofSizeExt::new(recorder)); + } + runtime_api .execute_block(genesis, block.clone()) .map_err(|e| Error::Client(RuntimeApiError(e)))?; } @@ -190,7 +206,14 @@ where // Execute a block multiple times and record each execution time. for _ in 0..self.params.repeat { let block = block.clone(); - let runtime_api = self.client.runtime_api(); + let mut runtime_api = self.client.runtime_api(); + if self.record_proof { + runtime_api.record_proof(); + let recorder = runtime_api + .proof_recorder() + .expect("Proof recording is enabled in the line above; qed."); + runtime_api.register_extension(ProofSizeExt::new(recorder)); + } let start = Instant::now(); runtime_api diff --git a/substrate/utils/frame/benchmarking-cli/src/extrinsic/cmd.rs b/substrate/utils/frame/benchmarking-cli/src/extrinsic/cmd.rs index 99c0230617cb..949b8211556a 100644 --- a/substrate/utils/frame/benchmarking-cli/src/extrinsic/cmd.rs +++ b/substrate/utils/frame/benchmarking-cli/src/extrinsic/cmd.rs @@ -118,7 +118,8 @@ impl ExtrinsicCmd { return Err("Unknown pallet or extrinsic. Use --list for a complete list.".into()), }; - let bench = Benchmark::new(client, self.params.bench.clone(), inherent_data, digest_items); + let bench = + Benchmark::new(client, self.params.bench.clone(), inherent_data, digest_items, false); let stats = bench.bench_extrinsic(ext_builder)?; info!( "Executing a {}::{} extrinsic takes[ns]:\n{:?}", diff --git a/substrate/utils/frame/benchmarking-cli/src/lib.rs b/substrate/utils/frame/benchmarking-cli/src/lib.rs index 0ef2c299de63..36b856927f34 100644 --- a/substrate/utils/frame/benchmarking-cli/src/lib.rs +++ b/substrate/utils/frame/benchmarking-cli/src/lib.rs @@ -28,7 +28,10 @@ mod storage; pub use block::BlockCmd; pub use extrinsic::{ExtrinsicBuilder, ExtrinsicCmd, ExtrinsicFactory}; pub use machine::{MachineCmd, SUBSTRATE_REFERENCE_HARDWARE}; -pub use overhead::OverheadCmd; +pub use overhead::{ + remark_builders::{DynamicRemarkBuilder, SubstrateRemarkBuilder}, + OpaqueBlock, OverheadCmd, +}; pub use pallet::PalletCmd; pub use sc_service::BasePath; pub use storage::StorageCmd; diff --git a/substrate/utils/frame/benchmarking-cli/src/overhead/cmd.rs b/substrate/utils/frame/benchmarking-cli/src/overhead/cmd.rs index 4fa8cecf2f7d..f20b14727ca1 100644 --- a/substrate/utils/frame/benchmarking-cli/src/overhead/cmd.rs +++ b/substrate/utils/frame/benchmarking-cli/src/overhead/cmd.rs @@ -18,26 +18,50 @@ //! Contains the [`OverheadCmd`] as entry point for the CLI to execute //! the *overhead* benchmarks. -use sc_block_builder::BlockBuilderApi; -use sc_cli::{CliConfiguration, ImportParams, Result, SharedParams}; -use sc_client_api::UsageProvider; -use sc_service::Configuration; -use sp_api::{ApiExt, CallApiAt, ProvideRuntimeApi}; -use sp_runtime::{traits::Block as BlockT, DigestItem, OpaqueExtrinsic}; - -use clap::{Args, Parser}; -use log::info; -use serde::Serialize; -use std::{fmt::Debug, path::PathBuf, sync::Arc}; - +use super::remark_builders::*; use crate::{ extrinsic::{ bench::{Benchmark, BenchmarkParams as ExtrinsicBenchmarkParams}, ExtrinsicBuilder, }, - overhead::template::TemplateData, - shared::{HostInfoParams, WeightParams}, + overhead::{ + cmd::ChainType::{Parachain, Relaychain, Unknown}, + fake_runtime_api, + template::TemplateData, + }, + shared::{self, GenesisBuilderPolicy, HostInfoParams, WeightParams}, }; +use clap::{Args, Parser}; +use codec::{Decode, Encode}; +use cumulus_client_parachain_inherent::MockValidationDataInherentDataProvider; +use fake_runtime_api::RuntimeApi as FakeRuntimeApi; +use frame_support::Deserialize; +use log::info; +use polkadot_parachain_primitives::primitives::Id as ParaId; +use sc_block_builder::BlockBuilderApi; +use sc_chain_spec::{ChainSpec, ChainSpecExtension, GenericChainSpec, GenesisBlockBuilder}; +use sc_cli::{CliConfiguration, ImportParams, Result, SharedParams}; +use sc_client_api::{execution_extensions::ExecutionExtensions, UsageProvider}; +use sc_client_db::{BlocksPruning, DatabaseSettings, DatabaseSource}; +use sc_executor::WasmExecutor; +use sc_service::{new_client, new_db_backend, BasePath, ClientConfig, TFullClient, TaskManager}; +use serde::Serialize; +use serde_json::{json, Value}; +use sp_api::{ApiExt, CallApiAt, Core, ProvideRuntimeApi}; +use sp_blockchain::HeaderBackend; +use sp_core::crypto::AccountId32; +use sp_inherents::{InherentData, InherentDataProvider}; +use sp_runtime::{ + generic, + traits::{BlakeTwo256, Block as BlockT}, + DigestItem, OpaqueExtrinsic, +}; +use sp_wasm_interface::HostFunctions; +use std::{fmt::Debug, path::PathBuf, sync::Arc}; +use subxt::{ext::futures, utils::H256, Metadata}; +use subxt_signer::{eth::Keypair as EthKeypair, DeriveJunction}; + +const DEFAULT_PARA_ID: u32 = 100; /// Benchmark the execution overhead per-block and per-extrinsic. #[derive(Debug, Parser)] @@ -81,6 +105,48 @@ pub struct OverheadParams { /// This should only be used for performance analysis and not for final results. #[arg(long)] pub enable_trie_cache: bool, + + /// Optional runtime blob to use instead of the one from the genesis config. + #[arg(long, value_name = "PATH")] + pub runtime: Option, + + /// The preset that we expect to find in the GenesisBuilder runtime API. + /// + /// This can be useful when a runtime has a dedicated benchmarking preset instead of using the + /// default one. + #[arg(long, default_value = sp_genesis_builder::DEV_RUNTIME_PRESET)] + pub genesis_builder_preset: String, + + /// How to construct the genesis state. + /// + /// Uses `GenesisBuilderPolicy::Spec` by default and `GenesisBuilderPolicy::Runtime` if + /// `runtime` is set. + #[arg(long, value_enum)] + pub genesis_builder: Option, + + /// Account type to generate. + #[arg(long)] + pub account_type: Option, + + /// Inflate the genesis state by generating accounts. + /// + /// Can be used together with `--account_type` to specify the type of accounts to generate. + #[arg(long)] + pub generate_num_accounts: Option, + + /// Parachain Id to use for parachains. If not specified, the benchmark code will choose + /// a para-id and patch the state accordingly. + #[arg(long)] + pub para_id: Option, +} + +/// Enum representing the type of account to generate. +#[derive(Debug, Clone, PartialEq, clap::ValueEnum, Serialize)] +pub enum AccountType { + /// Sr25519 account type. + Sr25519, + /// ECDSA account type. + ECDSA, } /// Type of a benchmark. @@ -92,18 +158,319 @@ pub(crate) enum BenchmarkType { Block, } +/// Hostfunctions that are typically used by parachains. +pub type ParachainHostFunctions = ( + cumulus_primitives_proof_size_hostfunction::storage_proof_size::HostFunctions, + sp_io::SubstrateHostFunctions, +); + +pub type BlockNumber = u32; + +/// Typical block type using `OpaqueExtrinsic`. +pub type Header = generic::Header; + +/// Typical block type using `OpaqueExtrinsic`. +pub type OpaqueBlock = generic::Block; + +/// Client type used throughout the benchmarking code. +type OverheadClient = TFullClient>; + +/// Creates inherent data for a given parachain ID. +/// +/// This function constructs the inherent data required for block execution, +/// including the relay chain state and validation data. Not all of these +/// inherents are required for every chain. The runtime will pick the ones +/// it requires based on their identifier. +fn create_inherent_data + HeaderBackend, Block: BlockT>( + client: &Arc, + chain_type: &ChainType, +) -> InherentData { + let genesis = client.usage_info().chain.best_hash; + let header = client.header(genesis).unwrap().unwrap(); + + let mut inherent_data = sp_inherents::InherentData::new(); + + // Para inherent can only makes sense when we are handling a parachain. + if let Parachain(para_id) = chain_type { + let parachain_validation_data_provider = MockValidationDataInherentDataProvider::<()> { + para_id: ParaId::from(*para_id), + current_para_block_head: Some(header.encode().into()), + relay_offset: 1, + ..Default::default() + }; + let _ = futures::executor::block_on( + parachain_validation_data_provider.provide_inherent_data(&mut inherent_data), + ); + } + + // Parachain inherent that is used on relay chains to perform parachain validation. + let para_inherent = polkadot_primitives::InherentData { + bitfields: Vec::new(), + backed_candidates: Vec::new(), + disputes: Vec::new(), + parent_header: header, + }; + + // Timestamp inherent that is very common in substrate chains. + let timestamp = sp_timestamp::InherentDataProvider::new(std::time::Duration::default().into()); + + let _ = futures::executor::block_on(timestamp.provide_inherent_data(&mut inherent_data)); + let _ = + inherent_data.put_data(polkadot_primitives::PARACHAINS_INHERENT_IDENTIFIER, ¶_inherent); + + inherent_data +} + +#[derive(Deserialize, Serialize, Clone, ChainSpecExtension)] +pub struct ParachainExtension { + /// The id of the Parachain. + pub para_id: Option, +} + +fn generate_balances_sr25519(num_accounts: u64) -> Vec { + let alice_pair = subxt_signer::sr25519::dev::alice(); + (0..num_accounts) + .into_iter() + .map(|path| { + let derived = alice_pair.derive([DeriveJunction::hard(path.to_string())]); + json!((AccountId32::from(derived.public_key().0), 1u64 << 60,)) + }) + .collect() +} + +fn generate_balances_ecdsa(num_accounts: u64) -> Vec { + let alice_pair = subxt_signer::ecdsa::dev::alice(); + (0..num_accounts) + .into_iter() + .map(|path| { + let derived = alice_pair.derive([DeriveJunction::hard(path.to_string())]).unwrap(); + json!(( + "0x".to_string() + &hex::encode(EthKeypair::from(derived).account_id()), + 1u64 << 60, + )) + }) + .collect() +} + +/// Patch the genesis state. +/// +/// We perform two actions: +/// 1. Inflate the genesis state by generating accounts. +/// 2. Patch the parachain id into the genesis config. This is necessary since the inherents +/// also contain a parachain id and they need to match. +fn patch_genesis( + mut input_value: Value, + num_accounts: Option, + chain_type: ChainType, + generate_accounts: Option, +) -> Value { + // If we identified a parachain we should patch a parachain id into the genesis config. + // This ensures compatibility with the inherents that we provide to successfully build a + // block. + if let Parachain(para_id) = chain_type { + sc_chain_spec::json_patch::merge( + &mut input_value, + json!({ + "parachainInfo": { + "parachainId": para_id, + } + }), + ); + log::debug!("Genesis Config Json"); + log::debug!("{}", input_value); + } + + if let Some(num_accounts) = num_accounts { + // Attempt to patch balances + if let Some(balances) = input_value + .get_mut("balances") + .and_then(|info| info.get_mut("balances")) + .and_then(|balance| balance.as_array_mut()) + { + let generated = match &generate_accounts { + None => Default::default(), + Some(AccountType::Sr25519) => generate_balances_sr25519(num_accounts), + Some(AccountType::ECDSA) => generate_balances_ecdsa(num_accounts), + }; + balances.extend(generated); + } else { + log::warn!("No balances found."); + } + } + input_value +} + impl OverheadCmd { + /// Make an educated guess what kind of chain we are dealing with. + fn identify_chain(&self, metadata: &Metadata, para_id: Option) -> Result { + let parachain_info_exists = metadata.pallet_by_name("ParachainInfo").is_some(); + let parachain_system_exists = metadata.pallet_by_name("ParachainSystem").is_some(); + let para_inherent_exists = metadata.pallet_by_name("ParaInherent").is_some(); + + log::info!("Identifying chain type based on metadata."); + log::info!("{} ParachainSystem", if parachain_system_exists { "✅" } else { "❌" }); + log::info!("{} ParachainInfo", if parachain_info_exists { "✅" } else { "❌" }); + log::info!("{} ParaInherent", if para_inherent_exists { "✅" } else { "❌" }); + + if parachain_system_exists && parachain_info_exists { + log::info!("Parachain Identified"); + Ok(Parachain(para_id.or(self.params.para_id).unwrap_or(DEFAULT_PARA_ID))) + } else if para_inherent_exists { + log::info!("Relaychain Identified"); + Ok(Relaychain) + } else { + log::info!("Found Custom chain"); + Ok(Unknown) + } + } + fn chain_spec_from_path( + &self, + ) -> Result<(Option>, Option)> { + let chain_spec = self + .shared_params + .chain + .clone() + .map(|path| { + GenericChainSpec::::from_json_file(path.into()) + .map_err(|e| format!("Unable to load chain spec: {:?}", e)) + }) + .transpose()?; + + let para_id_from_chain_spec = + chain_spec.as_ref().and_then(|spec| spec.extensions().para_id); + Ok((chain_spec.map(|c| Box::new(c) as Box<_>), para_id_from_chain_spec)) + } + + /// Run the benchmark overhead command. + pub fn run_with_extrinsic_builder( + &self, + ext_builder: Option>, + ) -> Result<()> + where + Block: BlockT, + ExtraHF: HostFunctions, + { + let (chain_spec, para_id_from_chain_spec) = + self.chain_spec_from_path::<(ParachainHostFunctions, ExtraHF)>()?; + let code_bytes = shared::genesis_state::get_code_bytes(&chain_spec, &self.params.runtime)?; + + let executor = WasmExecutor::<(ParachainHostFunctions, ExtraHF)>::builder() + .with_allow_missing_host_functions(true) + .build(); + + let opaque_metadata = fetch_latest_metadata_from_blob(&executor, &code_bytes)?; + let metadata = Metadata::decode(&mut (*opaque_metadata).as_slice())?; + let chain_type = self.identify_chain(&metadata, para_id_from_chain_spec)?; + + let client = self.build_client_components::( + chain_spec, + &code_bytes, + executor, + &chain_type, + )?; + + let inherent_data = create_inherent_data(&client, &chain_type); + + let ext_builder = ext_builder.unwrap_or_else(|| { + let genesis = client.usage_info().chain.best_hash; + let version = client.runtime_api().version(genesis).unwrap(); + let runtime_version = subxt::client::RuntimeVersion { + spec_version: version.spec_version, + transaction_version: version.transaction_version, + }; + Box::new(SubstrateRemarkBuilder::new(metadata, genesis, runtime_version)) + }); + + self.run( + "Overhead Benchmark".to_string(), + client, + inherent_data, + Default::default(), + &*ext_builder, + chain_type.requires_proof_recording(), + ) + } + + fn build_client_components( + &self, + chain_spec: Option>, + code_bytes: &Vec, + executor: WasmExecutor, + chain_type: &ChainType, + ) -> Result>> { + let extensions = ExecutionExtensions::new(None, Arc::new(executor.clone())); + + let backend = new_db_backend(DatabaseSettings { + trie_cache_maximum_size: self.trie_cache_maximum_size()?, + state_pruning: None, + blocks_pruning: BlocksPruning::KeepAll, + source: DatabaseSource::RocksDb { + cache_size: self.database_cache_size()?.unwrap_or(1024), + path: BasePath::new_temp_dir()?.path().into(), + }, + })?; + + //let storage = self.storage(&code_bytes, chain_spec, &chain_type)?; + let chain_type_for_closure = chain_type.clone(); + let num_accounts = self.params.generate_num_accounts; + let account_type = self.params.account_type.clone(); + let storage = shared::genesis_state::genesis_storage::( + self.params.genesis_builder, + &self.params.runtime, + Some(&code_bytes), + &self.params.genesis_builder_preset, + &chain_spec, + Some(Box::new(move |val| { + patch_genesis(val, num_accounts, chain_type_for_closure, account_type) + })), + )?; + + let genesis_block_builder = GenesisBlockBuilder::new_with_storage( + storage, + true, + backend.clone(), + executor.clone(), + )?; + + let tokio_runtime = sc_cli::build_runtime()?; + let task_manager = TaskManager::new(tokio_runtime.handle().clone(), None) + .map_err(|_| "Unable to build task manager")?; + + let client: Arc> = Arc::new(new_client( + backend.clone(), + executor, + genesis_block_builder, + Default::default(), + Default::default(), + extensions, + Box::new(task_manager.spawn_handle()), + None, + None, + ClientConfig { + offchain_worker_enabled: false, + offchain_indexing_api: false, + wasm_runtime_overrides: None, + no_genesis: false, + wasm_runtime_substitutes: Default::default(), + enable_import_proof_recording: chain_type.requires_proof_recording(), + }, + )?); + + Ok(client) + } + /// Measure the per-block and per-extrinsic execution overhead. /// /// Writes the results to console and into two instances of the /// `weights.hbs` template, one for each benchmark. pub fn run( &self, - cfg: Configuration, + chain_name: String, client: Arc, inherent_data: sp_inherents::InherentData, digest_items: Vec, ext_builder: &dyn ExtrinsicBuilder, + should_record_proof: bool, ) -> Result<()> where Block: BlockT, @@ -116,20 +483,39 @@ impl OverheadCmd { if ext_builder.pallet() != "system" || ext_builder.extrinsic() != "remark" { return Err(format!("The extrinsic builder is required to build `System::Remark` extrinsics but builds `{}` extrinsics instead", ext_builder.name()).into()); } - let bench = Benchmark::new(client, self.params.bench.clone(), inherent_data, digest_items); + + let bench = Benchmark::new( + client, + self.params.bench.clone(), + inherent_data, + digest_items, + should_record_proof, + ); // per-block execution overhead { - let stats = bench.bench_block()?; + let (stats, proof_size) = bench.bench_block()?; info!("Per-block execution overhead [ns]:\n{:?}", stats); - let template = TemplateData::new(BenchmarkType::Block, &cfg, &self.params, &stats)?; + let template = TemplateData::new( + BenchmarkType::Block, + &chain_name, + &self.params, + &stats, + proof_size, + )?; template.write(&self.params.weight.weight_path)?; } // per-extrinsic execution overhead { - let stats = bench.bench_extrinsic(ext_builder)?; + let (stats, proof_size) = bench.bench_extrinsic(ext_builder)?; info!("Per-extrinsic execution overhead [ns]:\n{:?}", stats); - let template = TemplateData::new(BenchmarkType::Extrinsic, &cfg, &self.params, &stats)?; + let template = TemplateData::new( + BenchmarkType::Extrinsic, + &chain_name, + &self.params, + &stats, + proof_size, + )?; template.write(&self.params.weight.weight_path)?; } @@ -155,6 +541,23 @@ impl BenchmarkType { } } +#[derive(Clone)] +enum ChainType { + Parachain(u32), + Relaychain, + Unknown, +} + +impl ChainType { + fn requires_proof_recording(&self) -> bool { + match self { + Parachain(_) => true, + Relaychain => false, + Unknown => false, + } + } +} + // Boilerplate impl CliConfiguration for OverheadCmd { fn shared_params(&self) -> &SharedParams { @@ -172,4 +575,8 @@ impl CliConfiguration for OverheadCmd { Ok(None) } } + + fn base_path(&self) -> Result> { + Ok(Some(BasePath::new_temp_dir()?)) + } } diff --git a/substrate/utils/frame/benchmarking-cli/src/overhead/fake_runtime_api.rs b/substrate/utils/frame/benchmarking-cli/src/overhead/fake_runtime_api.rs new file mode 100644 index 000000000000..10187383d972 --- /dev/null +++ b/substrate/utils/frame/benchmarking-cli/src/overhead/fake_runtime_api.rs @@ -0,0 +1,108 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Cumulus. + +// Cumulus 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. + +// Cumulus 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 Cumulus. If not, see . + +//! A fake runtime struct that allows us to instantiate a client. +//! Has all the required runtime APIs implemented to satisfy trait bounds, +//! but the methods are never called since we use WASM exclusively. + +use sp_core::OpaqueMetadata; +use sp_runtime::{ + generic, + traits::{BlakeTwo256, Block as BlockT}, + transaction_validity::{TransactionSource, TransactionValidity}, + ApplyExtrinsicResult, OpaqueExtrinsic, +}; + +/// Block number +type BlockNumber = u32; +/// Opaque block header type. +type Header = generic::Header; +/// Opaque block type. +type Block = generic::Block; + +#[allow(unused)] +pub struct Runtime; + +sp_api::impl_runtime_apis! { + impl sp_api::Core for Runtime { + fn version() -> sp_version::RuntimeVersion { + unimplemented!() + } + + fn execute_block(_: Block) { + unimplemented!() + } + + fn initialize_block(_: &::Header) -> sp_runtime::ExtrinsicInclusionMode { + unimplemented!() + } + } + + impl sp_api::Metadata for Runtime { + fn metadata() -> OpaqueMetadata { + unimplemented!() + } + + fn metadata_at_version(_: u32) -> Option { + unimplemented!() + } + + fn metadata_versions() -> Vec { + unimplemented!() + } + } + impl sp_block_builder::BlockBuilder for Runtime { + fn apply_extrinsic(_: ::Extrinsic) -> ApplyExtrinsicResult { + unimplemented!() + } + + fn finalize_block() -> ::Header { + unimplemented!() + } + + fn inherent_extrinsics(_: sp_inherents::InherentData) -> Vec<::Extrinsic> { + unimplemented!() + } + + fn check_inherents(_: Block, _: sp_inherents::InherentData) -> sp_inherents::CheckInherentsResult { + unimplemented!() + } + } + + impl sp_transaction_pool::runtime_api::TaggedTransactionQueue for Runtime { + fn validate_transaction( + _: TransactionSource, + _: ::Extrinsic, + _: ::Hash, + ) -> TransactionValidity { + unimplemented!() + } + } + + impl sp_genesis_builder::GenesisBuilder for Runtime { + fn build_state(_: Vec) -> sp_genesis_builder::Result { + unimplemented!() + } + + fn get_preset(_id: &Option) -> Option> { + unimplemented!() + } + + fn preset_names() -> Vec { + unimplemented!() + } + } +} diff --git a/substrate/utils/frame/benchmarking-cli/src/overhead/mod.rs b/substrate/utils/frame/benchmarking-cli/src/overhead/mod.rs index 00cde66fd722..1d8a78dc5bfb 100644 --- a/substrate/utils/frame/benchmarking-cli/src/overhead/mod.rs +++ b/substrate/utils/frame/benchmarking-cli/src/overhead/mod.rs @@ -18,4 +18,6 @@ pub mod cmd; pub mod template; -pub use cmd::OverheadCmd; +pub use cmd::{OpaqueBlock, OverheadCmd}; +mod fake_runtime_api; +pub mod remark_builders; diff --git a/substrate/utils/frame/benchmarking-cli/src/overhead/remark_builders.rs b/substrate/utils/frame/benchmarking-cli/src/overhead/remark_builders.rs new file mode 100644 index 000000000000..0664e5b9470f --- /dev/null +++ b/substrate/utils/frame/benchmarking-cli/src/overhead/remark_builders.rs @@ -0,0 +1,156 @@ +use crate::extrinsic::ExtrinsicBuilder; +use codec::{Decode, Encode}; +use sc_client_api::UsageProvider; +use sc_executor::WasmExecutor; +use sp_api::{Core, Metadata, ProvideRuntimeApi}; +use sp_core::{ + traits::{CallContext, CodeExecutor, FetchRuntimeCode, RuntimeCode}, + OpaqueMetadata, +}; +use sp_runtime::{traits::Block as BlockT, OpaqueExtrinsic}; +use sp_state_machine::BasicExternalities; +use sp_wasm_interface::HostFunctions; +use std::{borrow::Cow, sync::Arc}; +use subxt::{ + client::RuntimeVersion, config::substrate::SubstrateExtrinsicParamsBuilder, Config, + OfflineClient, SubstrateConfig, +}; + +pub type SubstrateRemarkBuilder = DynamicRemarkBuilder; + +pub struct DynamicRemarkBuilder { + offline_client: OfflineClient, +} + +impl DynamicRemarkBuilder { + pub fn new_from_client(client: Arc) -> sc_cli::Result + where + Block: BlockT, + Client: UsageProvider + ProvideRuntimeApi, + Client::Api: Metadata + Core, + { + let genesis = client.usage_info().chain.best_hash; + let api = client.runtime_api(); + let mut supported_metadata_versions = api.metadata_versions(genesis).unwrap(); + let latest = supported_metadata_versions + .pop() + .ok_or("No runtime version supported".to_string())?; + let version = api.version(genesis).unwrap(); + let runtime_version = RuntimeVersion { + spec_version: version.spec_version, + transaction_version: version.transaction_version, + }; + let metadata = api + .metadata_at_version(genesis, latest) + .map_err(|e| format!("Unable to fetch metadata: {:?}", e))? + .ok_or("Unable to decode metadata".to_string())?; + let metadata = subxt::Metadata::decode(&mut (*metadata).as_slice())?; + + Ok(Self { offline_client: OfflineClient::new(genesis, runtime_version, metadata) }) + } +} + +impl DynamicRemarkBuilder { + pub fn new( + metadata: subxt::Metadata, + genesis_hash: C::Hash, + runtime_version: RuntimeVersion, + ) -> Self { + Self { offline_client: OfflineClient::new(genesis_hash, runtime_version, metadata) } + } +} + +impl ExtrinsicBuilder for DynamicRemarkBuilder { + fn pallet(&self) -> &str { + "system" + } + + fn extrinsic(&self) -> &str { + "remark" + } + + fn build(&self, nonce: u32) -> std::result::Result { + let signer = subxt_signer::sr25519::dev::alice(); + let dynamic_tx = subxt::dynamic::tx("System", "remark", vec![Vec::::new()]); + + let params = SubstrateExtrinsicParamsBuilder::new().nonce(nonce.into()).build(); + + // Default transaction parameters assume a nonce of 0. + let transaction = self + .offline_client + .tx() + .create_signed_offline(&dynamic_tx, &signer, params) + .unwrap(); + let mut encoded = transaction.into_encoded(); + + OpaqueExtrinsic::from_bytes(&mut encoded).map_err(|_| "Unable to construct OpaqueExtrinsic") + } +} + +struct BasicCodeFetcher<'a>(Cow<'a, [u8]>); +impl<'a> FetchRuntimeCode for BasicCodeFetcher<'a> { + fn fetch_runtime_code(&self) -> Option> { + Some(self.0.clone()) + } +} +impl<'a> BasicCodeFetcher<'a> { + pub fn runtime_code(&'a self) -> RuntimeCode<'a> { + RuntimeCode { + code_fetcher: self as &'a dyn FetchRuntimeCode, + heap_pages: None, + hash: sp_crypto_hashing::blake2_256(&self.0).to_vec(), + } + } +} + +pub fn fetch_latest_metadata_from_blob( + executor: &WasmExecutor, + code_bytes: &Vec, +) -> sc_cli::Result { + let mut ext = BasicExternalities::default(); + let fetcher = BasicCodeFetcher(code_bytes.into()); + let version_result = executor + .call( + &mut ext, + &fetcher.runtime_code(), + "Metadata_metadata_versions", + &[], + CallContext::Offchain, + ) + .0; + + let opaque_metadata: Option = match version_result { + Ok(supported_versions) => { + let versions = Vec::::decode(&mut supported_versions.as_slice()) + .map_err(|e| format!("Error {e}"))?; + let version_to_use = versions.last().ok_or("No versions available.")?; + let parameters = (*version_to_use).encode(); + let encoded = executor + .call( + &mut ext, + &fetcher.runtime_code(), + "Metadata_metadata_at_version", + ¶meters, + CallContext::Offchain, + ) + .0 + .map_err(|e| format!("Unable to fetch metadata from blob: {e}"))?; + Decode::decode(&mut encoded.as_slice())? + }, + Err(_) => { + let encoded = executor + .call( + &mut ext, + &fetcher.runtime_code(), + "Metadata_metadata", + &[], + CallContext::Offchain, + ) + .0 + .map_err(|e| format!("Unable to fetch metadata from blob: {e}"))?; + Decode::decode(&mut encoded.as_slice())? + }, + }; + + opaque_metadata.ok_or_else(|| "Metadata not found".into()) +} diff --git a/substrate/utils/frame/benchmarking-cli/src/overhead/template.rs b/substrate/utils/frame/benchmarking-cli/src/overhead/template.rs index 7c8c92b07d74..a51529596842 100644 --- a/substrate/utils/frame/benchmarking-cli/src/overhead/template.rs +++ b/substrate/utils/frame/benchmarking-cli/src/overhead/template.rs @@ -19,7 +19,6 @@ //! it into the `weights.hbs` template. use sc_cli::Result; -use sc_service::Configuration; use handlebars::Handlebars; use log::info; @@ -59,19 +58,22 @@ pub(crate) struct TemplateData { params: OverheadParams, /// Stats about the benchmark result. stats: Stats, - /// The resulting weight in ns. - weight: u64, + /// The resulting ref time weight. + ref_time: u64, + /// The size of the proof weight. + proof_size: u64, } impl TemplateData { /// Returns a new [`Self`] from the given params. pub(crate) fn new( t: BenchmarkType, - cfg: &Configuration, + chain_name: &String, params: &OverheadParams, stats: &Stats, + proof_size: u64, ) -> Result { - let weight = params.weight.calc_weight(stats)?; + let ref_time = params.weight.calc_weight(stats)?; let header = params .header .as_ref() @@ -82,7 +84,7 @@ impl TemplateData { Ok(TemplateData { short_name: t.short_name().into(), long_name: t.long_name().into(), - runtime_name: cfg.chain_spec.name().into(), + runtime_name: chain_name.to_owned(), version: VERSION.into(), date: chrono::Utc::now().format("%Y-%m-%d (Y/M/D)").to_string(), hostname: params.hostinfo.hostname(), @@ -91,7 +93,8 @@ impl TemplateData { args: env::args().collect::>(), params: params.clone(), stats: stats.clone(), - weight, + ref_time, + proof_size, }) } diff --git a/substrate/utils/frame/benchmarking-cli/src/overhead/weights.hbs b/substrate/utils/frame/benchmarking-cli/src/overhead/weights.hbs index 6e364facc12f..8c48baaf79c0 100644 --- a/substrate/utils/frame/benchmarking-cli/src/overhead/weights.hbs +++ b/substrate/utils/frame/benchmarking-cli/src/overhead/weights.hbs @@ -18,9 +18,9 @@ use sp_weights::{constants::WEIGHT_REF_TIME_PER_NANOS, Weight}; parameter_types! { {{#if (eq short_name "block")}} - /// Time to execute an empty block. + /// Weight fo executing an empty block. {{else}} - /// Time to execute a NO-OP extrinsic, for example `System::remark`. + /// Weight of executing a NO-OP extrinsic, for example `System::remark`. {{/if}} /// Calculated by multiplying the *{{params.weight.weight_metric}}* with `{{params.weight.weight_mul}}` and adding `{{params.weight.weight_add}}`. /// @@ -35,7 +35,7 @@ parameter_types! { /// 95th: {{underscore stats.p95}} /// 75th: {{underscore stats.p75}} pub const {{long_name}}Weight: Weight = - Weight::from_parts(WEIGHT_REF_TIME_PER_NANOS.saturating_mul({{underscore weight}}), 0); + Weight::from_parts(WEIGHT_REF_TIME_PER_NANOS.saturating_mul({{underscore ref_time}}), {{underscore proof_size}}); } #[cfg(test)] diff --git a/substrate/utils/frame/benchmarking-cli/src/pallet/command.rs b/substrate/utils/frame/benchmarking-cli/src/pallet/command.rs index f1499f41c2ea..fd07e12b64fa 100644 --- a/substrate/utils/frame/benchmarking-cli/src/pallet/command.rs +++ b/substrate/utils/frame/benchmarking-cli/src/pallet/command.rs @@ -19,7 +19,10 @@ use super::{ types::{ComponentRange, ComponentRangeMap}, writer, ListOutput, PalletCmd, }; -use crate::pallet::{types::FetchedCode, GenesisBuilderPolicy}; +use crate::{ + pallet::types::FetchedCode, + shared::genesis_state::{genesis_storage, get_code_bytes}, +}; use codec::{Decode, Encode}; use frame_benchmarking::{ Analysis, BenchmarkBatch, BenchmarkBatchSplitResults, BenchmarkList, BenchmarkParameter, @@ -27,7 +30,6 @@ use frame_benchmarking::{ }; use frame_support::traits::StorageInfo; use linked_hash_map::LinkedHashMap; -use sc_chain_spec::GenesisConfigBuilderRuntimeCaller; use sc_cli::{execution_method_from_cli, ChainSpec, CliConfiguration, Result, SharedParams}; use sc_client_db::BenchmarkingState; use sc_executor::{HeapAllocStrategy, WasmExecutor, DEFAULT_HEAP_ALLOC_STRATEGY}; @@ -43,7 +45,6 @@ use sp_externalities::Extensions; use sp_keystore::{testing::MemoryKeystore, KeystoreExt}; use sp_runtime::traits::Hash; use sp_state_machine::StateMachine; -use sp_storage::{well_known_keys::CODE, Storage}; use sp_trie::{proof_size_extension::ProofSizeExt, recorder::Recorder}; use sp_wasm_interface::HostFunctions; use std::{ @@ -57,6 +58,11 @@ use std::{ /// Logging target const LOG_TARGET: &'static str = "polkadot_sdk_frame::benchmark::pallet"; +type SubstrateAndExtraHF = ( + sp_io::SubstrateHostFunctions, + frame_benchmarking::benchmarking::HostFunctions, + T, +); /// How the PoV size of a storage item should be estimated. #[derive(clap::ValueEnum, Debug, Eq, PartialEq, Clone, Copy)] pub enum PovEstimationMode { @@ -149,18 +155,6 @@ This could mean that you either did not build the node correctly with the \ `--features runtime-benchmarks` flag, or the chain spec that you are using was \ not created by a node that was compiled with the flag"; -/// When the runtime could not build the genesis storage. -const ERROR_CANNOT_BUILD_GENESIS: &str = "The runtime returned \ -an error when trying to build the genesis storage. Please ensure that all pallets \ -define a genesis config that can be built. This can be tested with: \ -https://github.com/paritytech/polkadot-sdk/pull/3412"; - -/// Warn when using the chain spec to generate the genesis state. -const WARN_SPEC_GENESIS_CTOR: &'static str = "Using the chain spec instead of the runtime to \ -generate the genesis state is deprecated. Please remove the `--chain`/`--dev`/`--local` argument, \ -point `--runtime` to your runtime blob and set `--genesis-builder=runtime`. This warning may \ -become a hard error any time after December 2024."; - impl PalletCmd { /// Runs the command and benchmarks a pallet. #[deprecated( @@ -210,7 +204,15 @@ impl PalletCmd { return self.output_from_results(&batches) } - let genesis_storage = self.genesis_storage::(&chain_spec)?; + let code_bytes = get_code_bytes(&chain_spec, &self.runtime)?; + let genesis_storage = genesis_storage::>( + self.genesis_builder, + &self.runtime, + Some(&code_bytes), + &self.genesis_builder_preset, + &chain_spec, + None + )?; let cache_size = Some(self.database_cache_size as usize); let state_with_tracking = BenchmarkingState::::new( @@ -239,11 +241,7 @@ impl PalletCmd { let runtime_code = runtime.code()?; let alloc_strategy = self.alloc_strategy(runtime_code.heap_pages); - let executor = WasmExecutor::<( - sp_io::SubstrateHostFunctions, - frame_benchmarking::benchmarking::HostFunctions, - ExtraHostFunctions, - )>::builder() + let executor = WasmExecutor::>::builder() .with_execution_method(method) .with_allow_missing_host_functions(self.allow_missing_host_functions) .with_onchain_heap_alloc_strategy(alloc_strategy) @@ -558,97 +556,6 @@ impl PalletCmd { Ok(benchmarks_to_run) } - /// Build the genesis storage by either the Genesis Builder API, chain spec or nothing. - /// - /// Behaviour can be controlled by the `--genesis-builder` flag. - fn genesis_storage( - &self, - chain_spec: &Option>, - ) -> Result { - Ok(match (self.genesis_builder, self.runtime.as_ref()) { - (Some(GenesisBuilderPolicy::None), Some(_)) => return Err("Cannot use `--genesis-builder=none` with `--runtime` since the runtime would be ignored.".into()), - (Some(GenesisBuilderPolicy::None), None) => Storage::default(), - (Some(GenesisBuilderPolicy::SpecGenesis | GenesisBuilderPolicy::Spec), Some(_)) => - return Err("Cannot use `--genesis-builder=spec-genesis` with `--runtime` since the runtime would be ignored.".into()), - (Some(GenesisBuilderPolicy::SpecGenesis | GenesisBuilderPolicy::Spec), None) | (None, None) => { - log::warn!("{WARN_SPEC_GENESIS_CTOR}"); - let Some(chain_spec) = chain_spec else { - return Err("No chain spec specified to generate the genesis state".into()); - }; - - let storage = chain_spec - .build_storage() - .map_err(|e| format!("{ERROR_CANNOT_BUILD_GENESIS}\nError: {e}"))?; - - storage - }, - (Some(GenesisBuilderPolicy::SpecRuntime), Some(_)) => - return Err("Cannot use `--genesis-builder=spec` with `--runtime` since the runtime would be ignored.".into()), - (Some(GenesisBuilderPolicy::SpecRuntime), None) => { - let Some(chain_spec) = chain_spec else { - return Err("No chain spec specified to generate the genesis state".into()); - }; - - self.genesis_from_spec_runtime::(chain_spec.as_ref())? - }, - (Some(GenesisBuilderPolicy::Runtime), None) => return Err("Cannot use `--genesis-builder=runtime` without `--runtime`".into()), - (Some(GenesisBuilderPolicy::Runtime), Some(runtime)) | (None, Some(runtime)) => { - log::info!("Loading WASM from {}", runtime.display()); - - let code = fs::read(&runtime).map_err(|e| { - format!( - "Could not load runtime file from path: {}, error: {}", - runtime.display(), - e - ) - })?; - - self.genesis_from_code::(&code)? - } - }) - } - - /// Setup the genesis state by calling the runtime APIs of the chain-specs genesis runtime. - fn genesis_from_spec_runtime( - &self, - chain_spec: &dyn ChainSpec, - ) -> Result { - log::info!("Building genesis state from chain spec runtime"); - let storage = chain_spec - .build_storage() - .map_err(|e| format!("{ERROR_CANNOT_BUILD_GENESIS}\nError: {e}"))?; - - let code: &Vec = - storage.top.get(CODE).ok_or("No runtime code in the genesis storage")?; - - self.genesis_from_code::(code) - } - - fn genesis_from_code(&self, code: &[u8]) -> Result { - let genesis_config_caller = GenesisConfigBuilderRuntimeCaller::<( - sp_io::SubstrateHostFunctions, - frame_benchmarking::benchmarking::HostFunctions, - EHF, - )>::new(code); - let preset = Some(&self.genesis_builder_preset); - - let mut storage = - genesis_config_caller.get_storage_for_named_preset(preset).inspect_err(|e| { - let presets = genesis_config_caller.preset_names().unwrap_or_default(); - log::error!( - "Please pick one of the available presets with \ - `--genesis-builder-preset=`. Available presets ({}): {:?}. Error: {:?}", - presets.len(), - presets, - e - ); - })?; - - storage.top.insert(CODE.into(), code.into()); - - Ok(storage) - } - /// Execute a state machine and decode its return value as `R`. fn exec_state_machine( mut machine: StateMachine, H, Exec>, diff --git a/substrate/utils/frame/benchmarking-cli/src/pallet/mod.rs b/substrate/utils/frame/benchmarking-cli/src/pallet/mod.rs index a507c1d1f548..873a1b9eb04e 100644 --- a/substrate/utils/frame/benchmarking-cli/src/pallet/mod.rs +++ b/substrate/utils/frame/benchmarking-cli/src/pallet/mod.rs @@ -19,7 +19,7 @@ mod command; mod types; mod writer; -use crate::{pallet::types::GenesisBuilderPolicy, shared::HostInfoParams}; +use crate::shared::{GenesisBuilderPolicy, HostInfoParams}; use clap::ValueEnum; use sc_cli::{ WasmExecutionMethod, WasmtimeInstantiationStrategy, DEFAULT_WASMTIME_INSTANTIATION_STRATEGY, diff --git a/substrate/utils/frame/benchmarking-cli/src/pallet/types.rs b/substrate/utils/frame/benchmarking-cli/src/pallet/types.rs index ce37be455e8a..6b1c0639e3e6 100644 --- a/substrate/utils/frame/benchmarking-cli/src/pallet/types.rs +++ b/substrate/utils/frame/benchmarking-cli/src/pallet/types.rs @@ -21,25 +21,6 @@ use sc_cli::Result; use sp_core::traits::RuntimeCode; use sp_runtime::traits::Hash; -/// How the genesis state for benchmarking should be build. -#[derive(clap::ValueEnum, Debug, Eq, PartialEq, Clone, Copy)] -#[clap(rename_all = "kebab-case")] -pub enum GenesisBuilderPolicy { - /// Do not provide any genesis state. - /// - /// Benchmarks are advised to function with this, since they should setup their own required - /// state. However, to keep backwards compatibility, this is not the default. - None, - /// Let the runtime build the genesis state through its `BuildGenesisConfig` runtime API. - Runtime, - // Use the runtime from the Spec file to build the genesis state. - SpecRuntime, - /// Use the spec file to build the genesis state. This fails when there is no spec. - SpecGenesis, - /// Same as `SpecGenesis` - only here for backwards compatibility. - Spec, -} - /// A runtime blob that was either fetched from genesis storage or loaded from a file. // NOTE: This enum is only needed for the annoying lifetime bounds on `RuntimeCode`. Otherwise we // could just directly return the blob. diff --git a/substrate/utils/frame/benchmarking-cli/src/shared/genesis_state.rs b/substrate/utils/frame/benchmarking-cli/src/shared/genesis_state.rs new file mode 100644 index 000000000000..308096895f2d --- /dev/null +++ b/substrate/utils/frame/benchmarking-cli/src/shared/genesis_state.rs @@ -0,0 +1,138 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +use crate::shared::GenesisBuilderPolicy; +use sc_chain_spec::{ChainSpec, GenesisConfigBuilderRuntimeCaller}; +use sc_cli::Result; +use serde_json::Value; +use sp_storage::{well_known_keys::CODE, Storage}; +use sp_wasm_interface::HostFunctions; +use std::{fs, path::PathBuf}; + +// 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. +/// Build the genesis storage by either the Genesis Builder API, chain spec or nothing. +/// +/// Behaviour can be controlled by the `genesis_builder` parameter. + +/// When the runtime could not build the genesis storage. +const ERROR_CANNOT_BUILD_GENESIS: &str = "The runtime returned \ +an error when trying to build the genesis storage. Please ensure that all pallets \ +define a genesis config that can be built. This can be tested with: \ +https://github.com/paritytech/polkadot-sdk/pull/3412"; + +/// Warn when using the chain spec to generate the genesis state. +const WARN_SPEC_GENESIS_CTOR: &'static str = "Using the chain spec instead of the runtime to \ +generate the genesis state is deprecated. Please remove the `--chain`/`--dev`/`--local` argument, \ +point `--runtime` to your runtime blob and set `--genesis-builder=runtime`. This warning may \ +become a hard error any time after December 2024."; + +pub fn get_code_bytes( + chain_spec: &Option>, + runtime: &Option, +) -> Result> { + match (chain_spec, runtime) { + (None, Some(runtime_code_path)) => Ok(fs::read(runtime_code_path)?), + // Get the code blob from the chain spec. + // First transform the chain_spec to storage, then extract the code. + (Some(chain_spec), None) => { + let mut storage = chain_spec + .as_storage_builder() + .build_storage() + .map_err(|e| format!("Can not transform chain-spec to storage {}", e))?; + Ok(storage.top.remove(CODE).ok_or("chain spec genesis does not contain code")?) + }, + (Some(_), Some(_)) => + Err("Both runtime and chain spec provided, please only provide one of both.".into()), + (_, _) => Err("Please provide either a runtime or a chain spec.".into()), + } +} +pub fn genesis_storage( + genesis_builder: Option, + runtime: &Option, + code_bytes: Option<&Vec>, + genesis_builder_preset: &String, + chain_spec: &Option>, + storage_patcher: Option Value + 'static>>, +) -> Result { + Ok(match (genesis_builder, runtime) { + (Some(GenesisBuilderPolicy::None), Some(_)) => return Err("Cannot use `--genesis-builder=none` with `--runtime` since the runtime would be ignored.".into()), + (Some(GenesisBuilderPolicy::None), None) => Storage::default(), + (Some(GenesisBuilderPolicy::SpecGenesis | GenesisBuilderPolicy::Spec), Some(_)) => + return Err("Cannot use `--genesis-builder=spec-genesis` with `--runtime` since the runtime would be ignored.".into()), + (Some(GenesisBuilderPolicy::SpecGenesis | GenesisBuilderPolicy::Spec), None) | (None, None) => { + log::warn!("{WARN_SPEC_GENESIS_CTOR}"); + let Some(chain_spec) = chain_spec else { + return Err("No chain spec specified to generate the genesis state".into()); + }; + + let storage = chain_spec + .build_storage() + .map_err(|e| format!("{ERROR_CANNOT_BUILD_GENESIS}\nError: {e}"))?; + + storage + }, + (Some(GenesisBuilderPolicy::SpecRuntime), Some(_)) => + return Err("Cannot use `--genesis-builder=spec` with `--runtime` since the runtime would be ignored.".into()), + (Some(GenesisBuilderPolicy::SpecRuntime), None) => { + let Some(code) = code_bytes else { + return Err("Can not build genesis from runtime. Please provide a runtime.".into()); + }; + + genesis_from_code::(code.as_slice(), genesis_builder_preset, storage_patcher)? + }, + (Some(GenesisBuilderPolicy::Runtime), None) => return Err("Cannot use `--genesis-builder=runtime` without `--runtime`".into()), + (Some(GenesisBuilderPolicy::Runtime), Some(_)) | (None, Some(_)) => { + let Some(code) = code_bytes else { + return Err("Can not build genesis from runtime. Please provide a runtime.".into()); + }; + + genesis_from_code::(code.as_slice(), genesis_builder_preset, storage_patcher)? + } + }) +} + +fn genesis_from_code( + code: &[u8], + genesis_builder_preset: &String, + storage_patcher: Option Value>>, +) -> Result { + let genesis_config_caller = GenesisConfigBuilderRuntimeCaller::<( + sp_io::SubstrateHostFunctions, + frame_benchmarking::benchmarking::HostFunctions, + EHF, + )>::new(code); + + let mut preset_json = + genesis_config_caller.get_named_preset(Some(&genesis_builder_preset.to_string()))?; + if let Some(patcher) = storage_patcher { + preset_json = patcher(preset_json); + } + + let mut storage = + genesis_config_caller.get_storage_for_patch(preset_json).inspect_err(|e| { + let presets = genesis_config_caller.preset_names().unwrap_or_default(); + log::error!( + "Please pick one of the available presets with \ + `--genesis-builder-preset=`. Available presets ({}): {:?}. Error: {:?}", + presets.len(), + presets, + e + ); + })?; + + storage.top.insert(CODE.into(), code.into()); + + Ok(storage) +} diff --git a/substrate/utils/frame/benchmarking-cli/src/shared/mod.rs b/substrate/utils/frame/benchmarking-cli/src/shared/mod.rs index f8aa49b867f7..d727e89c3dc9 100644 --- a/substrate/utils/frame/benchmarking-cli/src/shared/mod.rs +++ b/substrate/utils/frame/benchmarking-cli/src/shared/mod.rs @@ -17,6 +17,7 @@ //! Code that is shared among all benchmarking sub-commands. +pub mod genesis_state; pub mod record; pub mod stats; pub mod weight_params; @@ -132,3 +133,22 @@ impl HostInfoParams { gather_sysinfo().cpu.unwrap_or(self.cpuname_fallback.clone()) } } + +/// How the genesis state for benchmarking should be build. +#[derive(clap::ValueEnum, Debug, Eq, PartialEq, Clone, Copy, Serialize)] +#[clap(rename_all = "kebab-case")] +pub enum GenesisBuilderPolicy { + /// Do not provide any genesis state. + /// + /// Benchmarks are advised to function with this, since they should setup their own required + /// state. However, to keep backwards compatibility, this is not the default. + None, + /// Let the runtime build the genesis state through its `BuildGenesisConfig` runtime API. + Runtime, + /// Use the runtime from the Spec file to build the genesis state. + SpecRuntime, + /// Use the spec file to build the genesis state. This fails when there is no spec. + SpecGenesis, + /// Same as `SpecGenesis` - only here for backwards compatibility. + Spec, +} diff --git a/substrate/utils/frame/omni-bencher/src/command.rs b/substrate/utils/frame/omni-bencher/src/command.rs index 19177ed549b7..51fd79668b0d 100644 --- a/substrate/utils/frame/omni-bencher/src/command.rs +++ b/substrate/utils/frame/omni-bencher/src/command.rs @@ -16,7 +16,7 @@ // limitations under the License. use clap::Parser; -use frame_benchmarking_cli::BenchmarkCmd; +use frame_benchmarking_cli::{BenchmarkCmd, OpaqueBlock}; use sc_cli::Result; use sp_runtime::traits::BlakeTwo256; @@ -129,27 +129,29 @@ impl Command { } } } - +// impl V1SubCommand { pub fn run(self) -> Result<()> { - let pallet = match self { + match self { V1SubCommand::Benchmark(V1BenchmarkCommand { sub }) => match sub { - BenchmarkCmd::Pallet(pallet) => pallet, + BenchmarkCmd::Pallet(pallet) => { + if let Some(spec) = pallet.shared_params.chain { + return Err(format!( + "Chain specs are not supported. Please remove `--chain={spec}` and use \ + `--runtime=` instead" + ) + .into()); + } + + pallet.run_with_spec::(None) + }, + BenchmarkCmd::Overhead(overhead_cmd) => + overhead_cmd.run_with_extrinsic_builder::(None), _ => return Err( "Only the `v1 benchmark pallet` command is currently supported".into() ), }, - }; - - if let Some(spec) = pallet.shared_params.chain { - return Err(format!( - "Chain specs are not supported. Please remove `--chain={spec}` and use \ - `--runtime=` instead" - ) - .into()) } - - pallet.run_with_spec::(None) } }