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) }