diff --git a/substrate/Cargo.lock b/substrate/Cargo.lock
index f2cfaae187..757958f249 100644
--- a/substrate/Cargo.lock
+++ b/substrate/Cargo.lock
@@ -1578,6 +1578,12 @@ dependencies = [
"winapi 0.3.8",
]
+[[package]]
+name = "fs_extra"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5f2a4a2034423744d2cc7ca2068453168dcdb82c438419e639a26bd87839c674"
+
[[package]]
name = "fuchsia-cprng"
version = "0.1.1"
@@ -3602,8 +3608,12 @@ dependencies = [
name = "node-testing"
version = "2.0.0"
dependencies = [
+ "criterion 0.3.1",
"frame-support",
"frame-system",
+ "fs_extra",
+ "hex-literal",
+ "log 0.4.8",
"node-executor",
"node-primitives",
"node-runtime",
@@ -3618,13 +3628,24 @@ dependencies = [
"pallet-transaction-payment",
"pallet-treasury",
"parity-scale-codec",
+ "sc-cli",
"sc-client",
+ "sc-client-api",
+ "sc-client-db",
"sc-executor",
+ "sp-api",
+ "sp-block-builder",
+ "sp-blockchain",
+ "sp-consensus",
"sp-core",
+ "sp-finality-tracker",
+ "sp-inherents",
"sp-io",
"sp-keyring",
"sp-runtime",
+ "sp-timestamp",
"substrate-test-client",
+ "tempdir",
"wabt",
]
@@ -7715,6 +7736,16 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c63f48baada5c52e65a29eef93ab4f8982681b67f9e8d29c7b05abcfec2b9ffe"
+[[package]]
+name = "tempdir"
+version = "0.3.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8"
+dependencies = [
+ "rand 0.4.6",
+ "remove_dir_all",
+]
+
[[package]]
name = "tempfile"
version = "3.1.0"
diff --git a/substrate/bin/node/runtime/src/lib.rs b/substrate/bin/node/runtime/src/lib.rs
index 2f7aee7f8f..b7065b1854 100644
--- a/substrate/bin/node/runtime/src/lib.rs
+++ b/substrate/bin/node/runtime/src/lib.rs
@@ -27,7 +27,8 @@ use frame_support::{
traits::{SplitTwoWays, Currency, Randomness},
};
use sp_core::u32_trait::{_1, _2, _3, _4};
-use node_primitives::{AccountId, AccountIndex, Balance, BlockNumber, Hash, Index, Moment, Signature};
+pub use node_primitives::{AccountId, Signature};
+use node_primitives::{AccountIndex, Balance, BlockNumber, Hash, Index, Moment};
use sp_api::impl_runtime_apis;
use sp_runtime::{
Permill, Perbill, Percent, ApplyExtrinsicResult, impl_opaque_keys, generic, create_runtime_str,
diff --git a/substrate/bin/node/testing/Cargo.toml b/substrate/bin/node/testing/Cargo.toml
index 051460e839..e41af71571 100644
--- a/substrate/bin/node/testing/Cargo.toml
+++ b/substrate/bin/node/testing/Cargo.toml
@@ -9,6 +9,8 @@ license = "GPL-3.0"
[dependencies]
pallet-balances = { version = "2.0.0", path = "../../../frame/balances" }
sc-client = { version = "0.8", path = "../../../client/" }
+sc-client-db = { version = "0.8", path = "../../../client/db/", features = ["kvdb-rocksdb"] }
+sc-client-api = { version = "2.0", path = "../../../client/api/" }
codec = { package = "parity-scale-codec", version = "1.0.0" }
pallet-contracts = { version = "2.0.0", path = "../../../frame/contracts" }
pallet-grandpa = { version = "2.0.0", path = "../../../frame/grandpa" }
@@ -24,10 +26,29 @@ pallet-session = { version = "2.0.0", path = "../../../frame/session" }
pallet-society = { version = "2.0.0", path = "../../../frame/society" }
sp-runtime = { version = "2.0.0", path = "../../../primitives/runtime" }
pallet-staking = { version = "2.0.0", path = "../../../frame/staking" }
-sc-executor = { version = "0.8", path = "../../../client/executor" }
+sc-executor = { version = "0.8", path = "../../../client/executor", features = ["wasmtime"] }
+sp-consensus = { version = "0.8", path = "../../../primitives/consensus/common" }
frame-system = { version = "2.0.0", path = "../../../frame/system" }
substrate-test-client = { version = "2.0.0", path = "../../../test-utils/client" }
pallet-timestamp = { version = "2.0.0", path = "../../../frame/timestamp" }
pallet-transaction-payment = { version = "2.0.0", path = "../../../frame/transaction-payment" }
pallet-treasury = { version = "2.0.0", path = "../../../frame/treasury" }
wabt = "0.9.2"
+sp-api = { version = "2.0.0", path = "../../../primitives/api" }
+sp-finality-tracker = { version = "2.0.0", default-features = false, path = "../../../primitives/finality-tracker" }
+sp-timestamp = { version = "2.0.0", default-features = false, path = "../../../primitives/timestamp" }
+sp-block-builder = { version = "2.0.0", path = "../../../primitives/block-builder" }
+sp-inherents = { version = "2.0.0", path = "../../../primitives/inherents" }
+sp-blockchain = { version = "2.0.0", path = "../../../primitives/blockchain" }
+log = "0.4.8"
+
+[dev-dependencies]
+criterion = "0.3.0"
+tempdir = "0.3"
+fs_extra = "1"
+hex-literal = "0.2.1"
+sc-cli = { version = "0.8.0", path = "../../../client/cli" }
+
+[[bench]]
+name = "import"
+harness = false
diff --git a/substrate/bin/node/testing/benches/import.rs b/substrate/bin/node/testing/benches/import.rs
new file mode 100644
index 0000000000..86092a7836
--- /dev/null
+++ b/substrate/bin/node/testing/benches/import.rs
@@ -0,0 +1,503 @@
+// Copyright 2020 Parity Technologies (UK) Ltd.
+// This file is part of Substrate.
+
+// Substrate is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Substrate is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Substrate. If not, see .
+
+//! Block import benchmark.
+//!
+//! This benchmark is expected to measure block import operation of
+//! some more or less full block.
+//!
+//! As we also want to protect against cold-cache attacks, this
+//! benchmark should not rely on any caching (except those that
+//! DO NOT depend on user input). Thus block generation should be
+//! based on randomized operation.
+//!
+//! This is supposed to be very simple benchmark and is not subject
+//! to much configuring - just block full of randomized transactions.
+//! It is not supposed to measure runtime modules weight correctness
+
+use std::{sync::Arc, path::Path, collections::BTreeMap};
+
+use node_primitives::Block;
+use node_testing::client::{Client, Backend};
+use node_testing::keyring::*;
+use sc_client_db::PruningMode;
+use sc_executor::{NativeExecutor, WasmExecutionMethod};
+use sp_consensus::{
+ BlockOrigin, BlockImport, BlockImportParams,
+ ForkChoiceStrategy, ImportResult, ImportedAux
+};
+use sp_runtime::{
+ generic::BlockId,
+ OpaqueExtrinsic,
+ traits::{Block as BlockT, Verify, Zero, IdentifyAccount},
+};
+use codec::{Decode, Encode};
+use node_runtime::{
+ Call,
+ CheckedExtrinsic,
+ constants::currency::DOLLARS,
+ UncheckedExtrinsic,
+ MinimumPeriod,
+ BalancesCall,
+ AccountId,
+ Signature,
+};
+use sp_core::ExecutionContext;
+use sp_api::ProvideRuntimeApi;
+use sp_block_builder::BlockBuilder;
+use sp_inherents::InherentData;
+use sc_client_api::{
+ Backend as _, ExecutionStrategy,
+ execution_extensions::{ExecutionExtensions, ExecutionStrategies},
+};
+use sp_core::{Pair, Public, sr25519};
+
+use criterion::{Criterion, criterion_group, criterion_main};
+
+criterion_group!(
+ name = benches;
+ config = Criterion::default().sample_size(10);
+ targets = bench_block_import
+);
+criterion_group!(
+ name = profile;
+ config = Criterion::default().sample_size(10);
+ targets = profile_block_import
+);
+criterion_main!(benches, profile);
+
+fn genesis(keyring: &BenchKeyring) -> node_runtime::GenesisConfig {
+ node_testing::genesis::config_endowed(
+ false,
+ Some(node_runtime::WASM_BINARY),
+ keyring.collect_account_ids(),
+ )
+}
+
+// this is deterministic keyring of ordered accounts
+// //endowed-user//00
+// //endowed-user//01
+// ...
+// //endowed-user//N
+struct BenchKeyring {
+ accounts: BTreeMap,
+}
+
+impl BenchKeyring {
+ fn new(num: usize) -> Self {
+ let mut accounts = BTreeMap::new();
+
+ for n in 0..num {
+ let seed = format!("//endowed-user/{}", n);
+ let pair = sr25519::Pair::from_string(&seed, None).expect("failed to generate pair");
+ let account_id = AccountPublic::from(pair.public()).into_account();
+ accounts.insert(account_id, pair);
+ }
+
+ Self { accounts }
+ }
+
+ fn collect_account_ids(&self) -> Vec {
+ self.accounts.keys().cloned().collect()
+ }
+
+ fn at(&self, index: usize) -> AccountId {
+ self.accounts.keys().nth(index).expect("Failed to get account").clone()
+ }
+
+ fn sign(&self, xt: CheckedExtrinsic, version: u32, genesis_hash: [u8; 32]) -> UncheckedExtrinsic {
+ match xt.signed {
+ Some((signed, extra)) => {
+ let payload = (xt.function, extra.clone(), version, genesis_hash, genesis_hash);
+ let key = self.accounts.get(&signed).expect("Account id not found in keyring");
+ let signature = payload.using_encoded(|b| {
+ if b.len() > 256 {
+ key.sign(&sp_io::hashing::blake2_256(b))
+ } else {
+ key.sign(b)
+ }
+ }).into();
+ UncheckedExtrinsic {
+ signature: Some((pallet_indices::address::Address::Id(signed), signature, extra)),
+ function: payload.0,
+ }
+ }
+ None => UncheckedExtrinsic {
+ signature: None,
+ function: xt.function,
+ },
+ }
+ }
+}
+
+#[derive(Clone, Copy, Debug)]
+enum Profile {
+ Native,
+ Wasm,
+}
+
+impl Profile {
+ fn into_execution_strategies(self) -> ExecutionStrategies {
+ match self {
+ Profile::Wasm => ExecutionStrategies {
+ syncing: ExecutionStrategy::AlwaysWasm,
+ importing: ExecutionStrategy::AlwaysWasm,
+ block_construction: ExecutionStrategy::AlwaysWasm,
+ offchain_worker: ExecutionStrategy::AlwaysWasm,
+ other: ExecutionStrategy::AlwaysWasm,
+ },
+ Profile::Native => ExecutionStrategies {
+ syncing: ExecutionStrategy::NativeElseWasm,
+ importing: ExecutionStrategy::NativeElseWasm,
+ block_construction: ExecutionStrategy::NativeElseWasm,
+ offchain_worker: ExecutionStrategy::NativeElseWasm,
+ other: ExecutionStrategy::NativeElseWasm,
+ }
+ }
+ }
+}
+
+// This should return client that is doing everything that full node
+// is doing.
+//
+// - This client should use best wasm execution method.
+// - This client should work with real database only.
+fn bench_client(dir: &std::path::Path, profile: Profile, keyring: &BenchKeyring) -> (Client, std::sync::Arc) {
+ let db_config = sc_client_db::DatabaseSettings {
+ state_cache_size: 16*1024*1024,
+ state_cache_child_ratio: Some((0, 100)),
+ pruning: PruningMode::ArchiveAll,
+ source: sc_client_db::DatabaseSettingsSrc::Path {
+ path: dir.into(),
+ cache_size: None,
+ },
+ };
+
+ let (client, backend) = sc_client_db::new_client(
+ db_config,
+ NativeExecutor::new(WasmExecutionMethod::Compiled, None),
+ &genesis(keyring),
+ None,
+ None,
+ ExecutionExtensions::new(profile.into_execution_strategies(), None),
+ ).expect("Should not fail");
+
+ (client, backend)
+}
+
+struct Guard(tempdir::TempDir);
+
+struct BenchContext {
+ client: Client,
+ backend: Arc,
+ db_guard: Guard,
+ keyring: BenchKeyring,
+}
+
+impl BenchContext {
+ fn new(profile: Profile) -> BenchContext {
+ let keyring = BenchKeyring::new(128);
+
+ let dir = tempdir::TempDir::new("sub-bench").expect("temp dir creation failed");
+ log::trace!(
+ target: "bench-logistics",
+ "Created seed db at {}",
+ dir.path().to_string_lossy(),
+ );
+ let (client, backend) = bench_client(dir.path(), profile, &keyring);
+ let db_guard = Guard(dir);
+
+
+ BenchContext { client, backend, db_guard, keyring }
+ }
+
+ fn new_from_seed(profile: Profile, seed_dir: &Path) -> BenchContext {
+ let keyring = BenchKeyring::new(128);
+
+ let dir = tempdir::TempDir::new("sub-bench").expect("temp dir creation failed");
+
+ log::trace!(
+ target: "bench-logistics",
+ "Copying seed db from {} to {}",
+ seed_dir.to_string_lossy(),
+ dir.path().to_string_lossy(),
+ );
+ let seed_db_files = std::fs::read_dir(seed_dir)
+ .expect("failed to list file in seed dir")
+ .map(|f_result|
+ f_result.expect("failed to read file in seed db")
+ .path()
+ .clone()
+ ).collect();
+ fs_extra::copy_items(
+ &seed_db_files,
+ dir.path(),
+ &fs_extra::dir::CopyOptions::new(),
+ ).expect("Copy of seed database is ok");
+
+ let (client, backend) = bench_client(dir.path(), profile, &keyring);
+ let db_guard = Guard(dir);
+
+ BenchContext { client, backend, db_guard, keyring }
+ }
+
+ fn keep_db(self) -> Guard {
+ self.db_guard
+ }
+}
+
+type AccountPublic = ::Signer;
+
+pub fn get_from_seed(seed: &str) -> ::Public {
+ TPublic::Pair::from_string(&format!("//{}", seed), None)
+ .expect("static values are valid; qed")
+ .public()
+}
+
+pub fn get_account_id_from_seed(seed: &str) -> AccountId
+where
+ AccountPublic: From<::Public>
+{
+ AccountPublic::from(get_from_seed::(seed)).into_account()
+}
+
+// Block generation.
+fn generate_block_import(client: &Client, keyring: &BenchKeyring) -> Block {
+ let version = client.runtime_version_at(&BlockId::number(0))
+ .expect("There should be runtime version at 0")
+ .spec_version;
+ let genesis_hash = client.block_hash(Zero::zero())
+ .expect("Database error?")
+ .expect("Genesis block always exists; qed")
+ .into();
+
+ let mut block = client
+ .new_block(Default::default())
+ .expect("Block creation failed");
+
+ let timestamp = 1 * MinimumPeriod::get();
+
+ let mut inherent_data = InherentData::new();
+ inherent_data.put_data(sp_timestamp::INHERENT_IDENTIFIER, ×tamp)
+ .expect("Put timestamb failed");
+ inherent_data.put_data(sp_finality_tracker::INHERENT_IDENTIFIER, &0)
+ .expect("Put finality tracker failed");
+
+ for extrinsic in client.runtime_api()
+ .inherent_extrinsics_with_context(
+ &BlockId::number(0),
+ ExecutionContext::BlockConstruction,
+ inherent_data,
+ ).expect("Get inherents failed")
+ {
+ block.push(extrinsic).expect("Push inherent failed");
+ }
+
+ let mut iteration = 0;
+ let start = std::time::Instant::now();
+ for _ in 0..100 {
+
+ let sender = keyring.at(iteration);
+ let receiver = get_account_id_from_seed::(
+ &format!("random-user//{}", iteration)
+ );
+
+ let signed = keyring.sign(
+ CheckedExtrinsic {
+ signed: Some((sender, signed_extra(0, 1*DOLLARS))),
+ function: Call::Balances(
+ BalancesCall::transfer(
+ pallet_indices::address::Address::Id(receiver),
+ 1*DOLLARS
+ )
+ ),
+ },
+ version,
+ genesis_hash,
+ );
+
+ let encoded = Encode::encode(&signed);
+
+ let opaque = OpaqueExtrinsic::decode(&mut &encoded[..])
+ .expect("Failed to decode opaque");
+
+ match block.push(opaque) {
+ Err(sp_blockchain::Error::ApplyExtrinsicFailed(
+ sp_blockchain::ApplyExtrinsicFailed::Validity(e)
+ )) if e.exhausted_resources() => {
+ break;
+ },
+ Err(err) => panic!("Error pushing transaction: {:?}", err),
+ Ok(_) => {},
+ }
+ iteration += 1;
+ }
+ let block = block.build().expect("Block build failed").block;
+
+ log::info!(
+ target: "bench-logistics",
+ "Block construction: {:#?} ({} tx)",
+ start.elapsed(), block.extrinsics.len()
+ );
+
+ block
+}
+
+// Import generated block.
+fn import_block(client: &mut Client, block: Block) {
+ let import_params = BlockImportParams {
+ origin: BlockOrigin::NetworkBroadcast,
+ header: block.header().clone(),
+ post_digests: Default::default(),
+ body: Some(block.extrinsics().to_vec()),
+ storage_changes: Default::default(),
+ finalized: false,
+ justification: Default::default(),
+ auxiliary: Default::default(),
+ intermediates: Default::default(),
+ fork_choice: Some(ForkChoiceStrategy::LongestChain),
+ allow_missing_state: false,
+ import_existing: false,
+ };
+
+ assert_eq!(client.chain_info().best_number, 0);
+
+ assert_eq!(
+ client.import_block(import_params, Default::default())
+ .expect("Failed to import block"),
+ ImportResult::Imported(
+ ImportedAux {
+ header_only: false,
+ clear_justification_requests: false,
+ needs_justification: false,
+ bad_justification: false,
+ needs_finality_proof: false,
+ is_new_best: true,
+ }
+ )
+ );
+
+ assert_eq!(client.chain_info().best_number, 1);
+}
+
+fn bench_block_import(c: &mut Criterion) {
+ sc_cli::init_logger("");
+ // for future uses, uncomment if something wrong.
+ // sc_cli::init_logger("sc_client=debug");
+
+ let (block, guard) = {
+ let context = BenchContext::new(Profile::Wasm);
+ let block = generate_block_import(&context.client, &context.keyring);
+ (block, context.keep_db())
+ };
+
+ log::trace!(
+ target: "bench-logistics",
+ "Seed database directory: {}",
+ guard.0.path().to_string_lossy(),
+ );
+
+ c.bench_function_over_inputs("import block",
+ move |bencher, profile| {
+ bencher.iter_batched(
+ || {
+ let context = BenchContext::new_from_seed(
+ *profile,
+ guard.0.path(),
+ );
+
+ // mostly to just launch compiler before benching!
+ let version = context.client.runtime_version_at(&BlockId::Number(0))
+ .expect("Failed to get runtime version")
+ .spec_version;
+
+ log::trace!(
+ target: "bench-logistics",
+ "Next iteration database directory: {}, runtime version: {}",
+ context.db_guard.0.path().to_string_lossy(), version,
+ );
+
+ context
+ },
+ |mut context| {
+ let start = std::time::Instant::now();
+ import_block(&mut context.client, block.clone());
+ let elapsed = start.elapsed();
+
+ log::info!(
+ target: "bench-logistics",
+ "imported block with {} tx, took: {:#?}",
+ block.extrinsics.len(),
+ elapsed,
+ );
+
+ log::info!(
+ target: "bench-logistics",
+ "usage info: {}",
+ context.backend.usage_info()
+ .expect("RocksDB backend always provides usage info!"),
+ );
+ },
+ criterion::BatchSize::PerIteration,
+ );
+ },
+ vec![Profile::Wasm, Profile::Native],
+ );
+}
+
+
+// This is not an actual benchmark, so don't use it to measure anything.
+// It just produces special pattern of cpu load that allows easy picking
+// the part of block import for the profiling in the tool of choice.
+fn profile_block_import(c: &mut Criterion) {
+ sc_cli::init_logger("");
+
+ let (block, guard) = {
+ let context = BenchContext::new(Profile::Wasm);
+ let block = generate_block_import(&context.client, &context.keyring);
+ (block, context.keep_db())
+ };
+
+ c.bench_function("profile block",
+ move |bencher| {
+ bencher.iter_batched(
+ || {
+ let context = BenchContext::new_from_seed(
+ Profile::Native,
+ guard.0.path(),
+ );
+ context
+ },
+ |mut context| {
+ // until better osx signpost/callgrind signal is possible to use
+ // in rust, we just pause everything completely to help choosing
+ // actual profiling interval
+ std::thread::park_timeout(std::time::Duration::from_secs(2));
+ import_block(&mut context.client, block.clone());
+ // and here as well
+ std::thread::park_timeout(std::time::Duration::from_secs(2));
+ log::info!(
+ target: "bench-logistics",
+ "imported block, usage info: {}",
+ context.backend.usage_info()
+ .expect("RocksDB backend always provides usage info!"),
+ )
+ },
+ criterion::BatchSize::PerIteration,
+ );
+ },
+ );
+}
\ No newline at end of file
diff --git a/substrate/bin/node/testing/src/client.rs b/substrate/bin/node/testing/src/client.rs
index 1dddd8ba5a..29b086c3c1 100644
--- a/substrate/bin/node/testing/src/client.rs
+++ b/substrate/bin/node/testing/src/client.rs
@@ -35,6 +35,9 @@ pub type Client = sc_client::Client<
node_runtime::RuntimeApi,
>;
+/// Transaction for node-runtime.
+pub type Transaction = sc_client_api::backend::TransactionFor;
+
/// Genesis configuration parameters for `TestClient`.
#[derive(Default)]
pub struct GenesisParameters {
diff --git a/substrate/bin/node/testing/src/genesis.rs b/substrate/bin/node/testing/src/genesis.rs
index 8c5514defa..58e646e3d7 100644
--- a/substrate/bin/node/testing/src/genesis.rs
+++ b/substrate/bin/node/testing/src/genesis.rs
@@ -21,14 +21,38 @@ use sp_keyring::{Ed25519Keyring, Sr25519Keyring};
use node_runtime::{
GenesisConfig, BalancesConfig, SessionConfig, StakingConfig, SystemConfig,
GrandpaConfig, IndicesConfig, ContractsConfig, SocietyConfig, WASM_BINARY,
+ AccountId,
};
use node_runtime::constants::currency::*;
use sp_core::ChangesTrieConfiguration;
use sp_runtime::Perbill;
-
/// Create genesis runtime configuration for tests.
pub fn config(support_changes_trie: bool, code: Option<&[u8]>) -> GenesisConfig {
+ config_endowed(support_changes_trie, code, Default::default())
+}
+
+/// Create genesis runtime configuration for tests with some extra
+/// endowed accounts.
+pub fn config_endowed(
+ support_changes_trie: bool,
+ code: Option<&[u8]>,
+ extra_endowed: Vec,
+) -> GenesisConfig {
+
+ let mut endowed = vec![
+ (alice(), 111 * DOLLARS),
+ (bob(), 100 * DOLLARS),
+ (charlie(), 100_000_000 * DOLLARS),
+ (dave(), 111 * DOLLARS),
+ (eve(), 101 * DOLLARS),
+ (ferdie(), 100 * DOLLARS),
+ ];
+
+ endowed.extend(
+ extra_endowed.into_iter().map(|endowed| (endowed, 100*DOLLARS))
+ );
+
GenesisConfig {
frame_system: Some(SystemConfig {
changes_trie_config: if support_changes_trie { Some(ChangesTrieConfiguration {
@@ -41,14 +65,7 @@ pub fn config(support_changes_trie: bool, code: Option<&[u8]>) -> GenesisConfig
ids: vec![alice(), bob(), charlie(), dave(), eve(), ferdie()],
}),
pallet_balances: Some(BalancesConfig {
- balances: vec![
- (alice(), 111 * DOLLARS),
- (bob(), 100 * DOLLARS),
- (charlie(), 100_000_000 * DOLLARS),
- (dave(), 111 * DOLLARS),
- (eve(), 101 * DOLLARS),
- (ferdie(), 100 * DOLLARS),
- ],
+ balances: endowed,
}),
pallet_session: Some(SessionConfig {
keys: vec![
diff --git a/substrate/client/api/src/client.rs b/substrate/client/api/src/client.rs
index 65952ce787..7503ce4a79 100644
--- a/substrate/client/api/src/client.rs
+++ b/substrate/client/api/src/client.rs
@@ -125,6 +125,8 @@ pub struct IoInfo {
pub state_reads: u64,
/// State reads (keys) from cache.
pub state_reads_cache: u64,
+ /// State reads (keys) from cache.
+ pub state_writes: u64,
}
/// Usage statistics for running client instance.
@@ -143,7 +145,7 @@ pub struct UsageInfo {
impl fmt::Display for UsageInfo {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f,
- "caches: ({} state, {} db overlay), i/o: ({} tx, {} write, {} read, {} avg tx, {}/{} key cache reads/total)",
+ "caches: ({} state, {} db overlay), i/o: ({} tx, {} write, {} read, {} avg tx, {}/{} key cache reads/total, {} key writes)",
self.memory.state_cache,
self.memory.database_cache,
self.io.transactions,
@@ -152,6 +154,7 @@ impl fmt::Display for UsageInfo {
self.io.average_transaction_size,
self.io.state_reads_cache,
self.io.state_reads,
+ self.io.state_writes,
)
}
}
diff --git a/substrate/client/db/src/lib.rs b/substrate/client/db/src/lib.rs
index 20998a57f9..b641234281 100644
--- a/substrate/client/db/src/lib.rs
+++ b/substrate/client/db/src/lib.rs
@@ -1472,6 +1472,7 @@ impl sc_client_api::backend::Backend for Backend {
average_transaction_size: io_stats.avg_transaction_size() as u64,
state_reads: state_stats.reads.ops,
state_reads_cache: state_stats.cache_reads.ops,
+ state_writes: state_stats.writes.ops,
},
})
}
diff --git a/substrate/client/db/src/light.rs b/substrate/client/db/src/light.rs
index 8fb220973f..bea08d6467 100644
--- a/substrate/client/db/src/light.rs
+++ b/substrate/client/db/src/light.rs
@@ -592,6 +592,7 @@ impl LightBlockchainStorage for LightStorage
// Light client does not track those
state_reads: 0,
state_reads_cache: 0,
+ state_writes: 0,
}
})
}
diff --git a/substrate/primitives/keyring/src/sr25519.rs b/substrate/primitives/keyring/src/sr25519.rs
index d3776f4073..476997f2db 100644
--- a/substrate/primitives/keyring/src/sr25519.rs
+++ b/substrate/primitives/keyring/src/sr25519.rs
@@ -86,7 +86,6 @@ impl Keyring {
pub fn public(self) -> Public {
self.pair().public()
}
-
pub fn to_seed(self) -> String {
format!("//{}", self)
}