Remove deprecated TryRuntime subcommand (#4017)

Completes the removal of `try-runtime-cli` logic from `polkadot-sdk`.
This commit is contained in:
Liam Aharon
2024-04-12 06:01:16 +10:00
committed by GitHub
parent 25f038aa8e
commit 39b1f50f1c
27 changed files with 17 additions and 2001 deletions
Generated
-44
View File
@@ -12756,7 +12756,6 @@ dependencies = [
"sp-runtime",
"substrate-build-script-utils",
"thiserror",
"try-runtime-cli",
]
[[package]]
@@ -18980,7 +18979,6 @@ dependencies = [
"sp-timestamp",
"substrate-build-script-utils",
"substrate-frame-rpc-system",
"try-runtime-cli",
]
[[package]]
@@ -20247,7 +20245,6 @@ dependencies = [
"tempfile",
"tokio",
"tokio-util",
"try-runtime-cli",
"wait-timeout",
"wat",
]
@@ -21933,47 +21930,6 @@ version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed"
[[package]]
name = "try-runtime-cli"
version = "0.38.0"
dependencies = [
"assert_cmd",
"async-trait",
"clap 4.5.3",
"frame-remote-externalities",
"frame-try-runtime",
"hex",
"log",
"node-primitives",
"parity-scale-codec",
"regex",
"sc-cli",
"sc-executor",
"serde",
"serde_json",
"sp-api",
"sp-consensus-aura",
"sp-consensus-babe",
"sp-core",
"sp-debug-derive 14.0.0",
"sp-externalities 0.25.0",
"sp-inherents",
"sp-io",
"sp-keystore",
"sp-rpc",
"sp-runtime",
"sp-state-machine",
"sp-timestamp",
"sp-transaction-storage-proof",
"sp-version",
"sp-weights",
"substrate-cli-test-utils",
"substrate-rpc-client",
"tempfile",
"tokio",
"zstd 0.12.4",
]
[[package]]
name = "trybuild"
version = "1.0.89"
-1
View File
@@ -507,7 +507,6 @@ members = [
"substrate/utils/frame/rpc/state-trie-migration-rpc",
"substrate/utils/frame/rpc/support",
"substrate/utils/frame/rpc/system",
"substrate/utils/frame/try-runtime/cli",
"substrate/utils/prometheus",
"substrate/utils/substrate-bip39",
"substrate/utils/wasm-builder",
-5
View File
@@ -55,11 +55,6 @@ pub enum Subcommand {
/// The pallet benchmarking moved to the `pallet` sub-command.
#[command(subcommand)]
Benchmark(frame_benchmarking_cli::BenchmarkCmd),
/// Try-runtime has migrated to a standalone
/// [CLI](<https://github.com/paritytech/try-runtime-cli>). The subcommand exists as a stub and
/// deprecation notice. It will be removed entirely some time after January 2024.
TryRuntime,
}
const AFTER_HELP_EXAMPLE: &str = color_print::cstr!(
+2 -4
View File
@@ -549,9 +549,8 @@ pub fn run() -> Result<()> {
},
Some(Subcommand::ExportGenesisHead(cmd)) => {
let runner = cli.create_runner(cmd)?;
runner.sync_run(|config| {
construct_partials!(config, |partials| cmd.run(partials.client))
})
runner
.sync_run(|config| construct_partials!(config, |partials| cmd.run(partials.client)))
},
Some(Subcommand::ExportGenesisWasm(cmd)) => {
let runner = cli.create_runner(cmd)?;
@@ -601,7 +600,6 @@ pub fn run() -> Result<()> {
_ => Err("Benchmarking sub-command unsupported".into()),
}
},
Some(Subcommand::TryRuntime) => Err("The `try-runtime` subcommand has been migrated to a standalone CLI (https://github.com/paritytech/try-runtime-cli). It is no longer being maintained here and will be removed entirely some time after January 2024. Please remove this subcommand from your runtime and use the standalone CLI.".into()),
Some(Subcommand::Key(cmd)) => Ok(cmd.run(&cli)?),
None => {
let runner = cli.create_runner(&cli.run.normalize())?;
-3
View File
@@ -33,7 +33,6 @@ sp-io = { path = "../../substrate/primitives/io" }
sp-keyring = { path = "../../substrate/primitives/keyring" }
sp-maybe-compressed-blob = { path = "../../substrate/primitives/maybe-compressed-blob" }
frame-benchmarking-cli = { path = "../../substrate/utils/frame/benchmarking-cli", optional = true }
try-runtime-cli = { path = "../../substrate/utils/frame/try-runtime/cli", optional = true }
sc-cli = { path = "../../substrate/client/cli", optional = true }
sc-service = { path = "../../substrate/client/service", optional = true }
polkadot-node-metrics = { path = "../node/metrics" }
@@ -57,7 +56,6 @@ cli = [
"sc-service",
"sc-tracing",
"service",
"try-runtime-cli",
]
runtime-benchmarks = [
"frame-benchmarking-cli?/runtime-benchmarks",
@@ -70,7 +68,6 @@ full-node = ["service/full-node"]
try-runtime = [
"service/try-runtime",
"sp-runtime/try-runtime",
"try-runtime-cli/try-runtime",
]
fast-runtime = ["service/fast-runtime"]
pyroscope = ["pyro", "pyroscope_pprofrs"]
-5
View File
@@ -50,11 +50,6 @@ pub enum Subcommand {
#[command(subcommand)]
Benchmark(frame_benchmarking_cli::BenchmarkCmd),
/// Try-runtime has migrated to a standalone CLI
/// (<https://github.com/paritytech/try-runtime-cli>). The subcommand exists as a stub and
/// deprecation notice. It will be removed entirely some time after January 2024.
TryRuntime,
/// Key management CLI utilities
#[command(subcommand)]
Key(sc_cli::KeySubcommand),
-7
View File
@@ -476,13 +476,6 @@ pub fn run() -> Result<()> {
}
},
Some(Subcommand::Key(cmd)) => Ok(cmd.run(&cli)?),
#[cfg(feature = "try-runtime")]
Some(Subcommand::TryRuntime) => Err(try_runtime_cli::DEPRECATION_NOTICE.to_owned().into()),
#[cfg(not(feature = "try-runtime"))]
Some(Subcommand::TryRuntime) => Err("TryRuntime wasn't enabled when building the node. \
You can enable it with `--features try-runtime`."
.to_owned()
.into()),
Some(Subcommand::ChainInfo(cmd)) => {
let runner = cli.create_runner(cmd)?;
Ok(runner.sync_run(|config| cmd.run::<service::Block>(&config))?)
+15
View File
@@ -0,0 +1,15 @@
title: Complete removal of `TryRuntime` subcommand
doc:
- audience: Node Operator
description: |
The deprecated `try-runtime` subcommand and `try-runtime-cli` crate has been completely removed.
If you are not already, you should be using the standalone CLI at https://github.com/paritytech/try-runtime-cli.
crates:
- name: polkadot-parachain-bin
bump: patch
- name: polkadot-cli
bump: patch
- name: staging-node-cli
bump: patch
-4
View File
@@ -116,7 +116,6 @@ node-primitives = { path = "../primitives" }
sc-cli = { path = "../../../client/cli", optional = true }
frame-benchmarking-cli = { path = "../../../utils/frame/benchmarking-cli", optional = true }
node-inspect = { package = "staging-node-inspect", path = "../inspect", optional = true }
try-runtime-cli = { path = "../../../utils/frame/try-runtime/cli", optional = true }
serde_json = { workspace = true, default-features = true }
[dev-dependencies]
@@ -172,7 +171,6 @@ node-inspect = { package = "staging-node-inspect", path = "../inspect", optional
frame-benchmarking-cli = { path = "../../../utils/frame/benchmarking-cli", optional = true }
substrate-build-script-utils = { path = "../../../utils/build-script-utils", optional = true }
substrate-frame-cli = { path = "../../../utils/frame/frame-utilities-cli", optional = true }
try-runtime-cli = { path = "../../../utils/frame/try-runtime/cli", optional = true }
sc-cli = { path = "../../../client/cli", optional = true }
pallet-balances = { path = "../../../frame/balances" }
sc-storage-monitor = { path = "../../../client/storage-monitor" }
@@ -188,7 +186,6 @@ cli = [
"sc-service/rocksdb",
"substrate-build-script-utils",
"substrate-frame-cli",
"try-runtime-cli",
]
runtime-benchmarks = [
"frame-benchmarking-cli/runtime-benchmarks",
@@ -232,7 +229,6 @@ try-runtime = [
"pallet-treasury/try-runtime",
"sp-runtime/try-runtime",
"substrate-cli-test-utils/try-runtime",
"try-runtime-cli/try-runtime",
]
[[bench]]
-5
View File
@@ -61,11 +61,6 @@ pub enum Subcommand {
#[command(subcommand)]
Benchmark(frame_benchmarking_cli::BenchmarkCmd),
/// Try-runtime has migrated to a standalone CLI
/// (<https://github.com/paritytech/try-runtime-cli>). The subcommand exists as a stub and
/// deprecation notice. It will be removed entirely some time after January 2024.
TryRuntime,
/// Key management cli utilities
#[command(subcommand)]
Key(sc_cli::KeySubcommand),
-6
View File
@@ -221,12 +221,6 @@ pub fn run() -> Result<()> {
Ok((cmd.run(client, backend, Some(aux_revert)), task_manager))
})
},
#[cfg(feature = "try-runtime")]
Some(Subcommand::TryRuntime) => Err(try_runtime_cli::DEPRECATION_NOTICE.into()),
#[cfg(not(feature = "try-runtime"))]
Some(Subcommand::TryRuntime) => Err("TryRuntime wasn't enabled when building the node. \
You can enable it with `--features try-runtime`."
.into()),
Some(Subcommand::ChainInfo(cmd)) => {
let runner = cli.create_runner(cmd)?;
runner.sync_run(|config| cmd.run::<Block>(&config))
@@ -1,63 +0,0 @@
[package]
name = "try-runtime-cli"
version = "0.38.0"
authors.workspace = true
edition.workspace = true
license = "Apache-2.0"
homepage = "https://substrate.io"
repository.workspace = true
description = "Cli command runtime testing and dry-running"
[lints]
workspace = true
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]
[dependencies]
remote-externalities = { package = "frame-remote-externalities", path = "../../remote-externalities" }
sc-cli = { path = "../../../../client/cli" }
sc-executor = { path = "../../../../client/executor" }
sp-consensus-aura = { path = "../../../../primitives/consensus/aura" }
sp-consensus-babe = { path = "../../../../primitives/consensus/babe" }
sp-core = { path = "../../../../primitives/core" }
sp-externalities = { path = "../../../../primitives/externalities" }
sp-inherents = { path = "../../../../primitives/inherents" }
sp-io = { path = "../../../../primitives/io" }
sp-keystore = { path = "../../../../primitives/keystore" }
sp-runtime = { path = "../../../../primitives/runtime" }
sp-rpc = { path = "../../../../primitives/rpc" }
sp-state-machine = { path = "../../../../primitives/state-machine" }
sp-timestamp = { path = "../../../../primitives/timestamp" }
sp-transaction-storage-proof = { path = "../../../../primitives/transaction-storage-proof" }
sp-version = { path = "../../../../primitives/version" }
sp-debug-derive = { path = "../../../../primitives/debug-derive" }
sp-api = { path = "../../../../primitives/api" }
sp-weights = { path = "../../../../primitives/weights" }
frame-try-runtime = { path = "../../../../frame/try-runtime", optional = true }
substrate-rpc-client = { path = "../../rpc/client" }
async-trait = "0.1.79"
clap = { version = "4.5.3", features = ["derive"] }
hex = { version = "0.4.3", default-features = false }
log = { workspace = true, default-features = true }
parity-scale-codec = "3.6.1"
serde = { workspace = true, default-features = true }
serde_json = { workspace = true, default-features = true }
zstd = { version = "0.12.4", default-features = false }
[dev-dependencies]
assert_cmd = "2.0.10"
node-primitives = { path = "../../../../bin/node/primitives" }
regex = "1.7.3"
substrate-cli-test-utils = { path = "../../../../test-utils/cli" }
tempfile = "3.1.0"
tokio = "1.37"
[features]
try-runtime = [
"frame-try-runtime/try-runtime",
"sp-debug-derive/force-debug",
"sp-runtime/try-runtime",
"substrate-cli-test-utils/try-runtime",
]
@@ -1,152 +0,0 @@
// 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 crate::BlockT;
use parity_scale_codec::Encode;
use sc_cli::Result;
use sp_consensus_aura::{Slot, SlotDuration, AURA_ENGINE_ID};
use sp_consensus_babe::{
digests::{PreDigest, SecondaryPlainPreDigest},
BABE_ENGINE_ID,
};
use sp_inherents::{InherentData, InherentDataProvider};
use sp_runtime::{Digest, DigestItem};
use sp_timestamp::TimestampInherentData;
/// Something that can create inherent data providers and pre-runtime digest.
///
/// It is possible for the caller to provide custom arguments to the callee by setting the
/// `ExtraArgs` generic parameter.
///
/// This module already provides some convenience implementation of this trait for closures. So, it
/// should not be required to implement it directly.
#[async_trait::async_trait]
pub trait BlockBuildingInfoProvider<Block: BlockT, ExtraArgs = ()> {
type InherentDataProviders: InherentDataProvider;
async fn get_inherent_providers_and_pre_digest(
&self,
parent_hash: Block::Hash,
extra_args: ExtraArgs,
) -> Result<(Self::InherentDataProviders, Vec<DigestItem>)>;
}
#[async_trait::async_trait]
impl<F, Block, IDP, ExtraArgs, Fut> BlockBuildingInfoProvider<Block, ExtraArgs> for F
where
Block: BlockT,
F: Fn(Block::Hash, ExtraArgs) -> Fut + Sync + Send,
Fut: std::future::Future<Output = Result<(IDP, Vec<DigestItem>)>> + Send + 'static,
IDP: InherentDataProvider + 'static,
ExtraArgs: Send + 'static,
{
type InherentDataProviders = IDP;
async fn get_inherent_providers_and_pre_digest(
&self,
parent: Block::Hash,
extra_args: ExtraArgs,
) -> Result<(Self::InherentDataProviders, Vec<DigestItem>)> {
(*self)(parent, extra_args).await
}
}
/// Provides [`BlockBuildingInfoProvider`] implementation for chains that include timestamp inherent
/// and use Aura for a block production.
///
/// It depends only on the expected block production frequency, i.e. `blocktime_millis`.
pub fn timestamp_with_aura_info<Block: BlockT>(
blocktime_millis: u64,
) -> impl BlockBuildingInfoProvider<Block, Option<(InherentData, Digest)>> {
move |_, maybe_prev_info: Option<(InherentData, Digest)>| async move {
let timestamp_idp = match maybe_prev_info {
Some((inherent_data, _)) => sp_timestamp::InherentDataProvider::new(
inherent_data.timestamp_inherent_data().unwrap().unwrap() + blocktime_millis,
),
None => sp_timestamp::InherentDataProvider::from_system_time(),
};
let slot =
Slot::from_timestamp(*timestamp_idp, SlotDuration::from_millis(blocktime_millis));
let digest = vec![DigestItem::PreRuntime(AURA_ENGINE_ID, slot.encode())];
Ok((timestamp_idp, digest))
}
}
/// Provides [`BlockBuildingInfoProvider`] implementation for chains that include timestamp inherent
/// and use Babe for a block production.
///
/// It depends only on the expected block production frequency, i.e. `blocktime_millis`.
pub fn timestamp_with_babe_info<Block: BlockT>(
blocktime_millis: u64,
) -> impl BlockBuildingInfoProvider<Block, Option<(InherentData, Digest)>> {
move |_, maybe_prev_info: Option<(InherentData, Digest)>| async move {
let timestamp_idp = match maybe_prev_info {
Some((inherent_data, _)) => sp_timestamp::InherentDataProvider::new(
inherent_data.timestamp_inherent_data().unwrap().unwrap() + blocktime_millis,
),
None => sp_timestamp::InherentDataProvider::from_system_time(),
};
let slot =
Slot::from_timestamp(*timestamp_idp, SlotDuration::from_millis(blocktime_millis));
let slot_idp = sp_consensus_babe::inherents::InherentDataProvider::new(slot);
let digest = vec![DigestItem::PreRuntime(
BABE_ENGINE_ID,
PreDigest::SecondaryPlain(SecondaryPlainPreDigest { slot, authority_index: 0 })
.encode(),
)];
Ok(((slot_idp, timestamp_idp), digest))
}
}
/// Provides [`BlockBuildingInfoProvider`] implementation for chains that use:
/// - timestamp inherent,
/// - Babe for a block production (inherent + digest),
/// - uncles inherent,
/// - storage proof inherent
///
/// It depends only on the expected block production frequency, i.e. `blocktime_millis`.
pub fn substrate_info<Block: BlockT>(
blocktime_millis: u64,
) -> impl BlockBuildingInfoProvider<Block, Option<(InherentData, Digest)>> {
move |_, maybe_prev_info: Option<(InherentData, Digest)>| async move {
let timestamp_idp = match maybe_prev_info {
Some((inherent_data, _)) => sp_timestamp::InherentDataProvider::new(
inherent_data.timestamp_inherent_data().unwrap().unwrap() + blocktime_millis,
),
None => sp_timestamp::InherentDataProvider::from_system_time(),
};
let slot =
Slot::from_timestamp(*timestamp_idp, SlotDuration::from_millis(blocktime_millis));
let slot_idp = sp_consensus_babe::inherents::InherentDataProvider::new(slot);
let storage_proof_idp = sp_transaction_storage_proof::InherentDataProvider::new(None);
let digest = vec![DigestItem::PreRuntime(
BABE_ENGINE_ID,
PreDigest::SecondaryPlain(SecondaryPlainPreDigest { slot, authority_index: 0 })
.encode(),
)];
Ok(((slot_idp, timestamp_idp, storage_proof_idp), digest))
}
}
@@ -1,78 +0,0 @@
// 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 crate::{build_executor, LiveState, SharedParams, State, LOG_TARGET};
use sc_executor::sp_wasm_interface::HostFunctions;
use sp_runtime::traits::{Block as BlockT, NumberFor};
use std::{fmt::Debug, str::FromStr};
use substrate_rpc_client::{ws_client, StateApi};
/// Configurations of the [`crate::Command::CreateSnapshot`].
#[derive(Debug, Clone, clap::Parser)]
pub struct CreateSnapshotCmd {
/// The source of the snapshot. Must be a remote node.
#[clap(flatten)]
pub from: LiveState,
/// The snapshot path to write to.
///
/// If not provided `<spec-name>-<spec-version>@<block-hash>.snap` will be used.
pub snapshot_path: Option<String>,
}
/// inner command for `Command::CreateSnapshot`.
pub(crate) async fn create_snapshot<Block, HostFns>(
shared: SharedParams,
command: CreateSnapshotCmd,
) -> sc_cli::Result<()>
where
Block: BlockT + serde::de::DeserializeOwned,
Block::Hash: serde::de::DeserializeOwned,
Block::Header: serde::de::DeserializeOwned,
<Block::Hash as FromStr>::Err: Debug,
NumberFor<Block>: FromStr,
<NumberFor<Block> as FromStr>::Err: Debug,
HostFns: HostFunctions,
{
let snapshot_path = command.snapshot_path;
if !matches!(shared.runtime, crate::Runtime::Existing) {
return Err("creating a snapshot is only possible with --runtime existing.".into())
}
let path = match snapshot_path {
Some(path) => path,
None => {
let rpc = ws_client(&command.from.uri).await.unwrap();
let remote_spec = StateApi::<Block::Hash>::runtime_version(&rpc, None).await.unwrap();
let path_str = format!(
"{}-{}@{}.snap",
remote_spec.spec_name.to_lowercase(),
remote_spec.spec_version,
command.from.at.clone().unwrap_or("latest".to_owned())
);
log::info!(target: LOG_TARGET, "snapshot path not provided (-s), using '{}'", path_str);
path_str.into()
},
};
let executor = build_executor::<HostFns>(&shared);
let _ = State::Live(command.from)
.into_ext::<Block, HostFns>(&shared, &executor, Some(path.into()), false)
.await?;
Ok(())
}
@@ -1,170 +0,0 @@
// 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 crate::{
build_executor, full_extensions, rpc_err_handler, state_machine_call_with_proof, LiveState,
SharedParams, State, LOG_TARGET,
};
use parity_scale_codec::Encode;
use sc_executor::sp_wasm_interface::HostFunctions;
use sp_rpc::{list::ListOrValue, number::NumberOrHex};
use sp_runtime::{
generic::SignedBlock,
traits::{Block as BlockT, Header as HeaderT, NumberFor},
};
use std::{fmt::Debug, str::FromStr};
use substrate_rpc_client::{ws_client, ChainApi};
/// Configurations of the [`crate::Command::ExecuteBlock`].
///
/// This will always call into `TryRuntime_execute_block`, which can optionally skip the state-root
/// check (useful for trying a unreleased runtime), and can execute runtime sanity checks as well.
#[derive(Debug, Clone, clap::Parser)]
pub struct ExecuteBlockCmd {
/// Which try-state targets to execute when running this command.
///
/// Expected values:
/// - `all`
/// - `none`
/// - A comma separated list of pallets, as per pallet names in `construct_runtime!()` (e.g.
/// `Staking, System`).
/// - `rr-[x]` where `[x]` is a number. Then, the given number of pallets are checked in a
/// round-robin fashion.
#[arg(long, default_value = "all")]
pub try_state: frame_try_runtime::TryStateSelect,
/// The ws uri from which to fetch the block.
///
/// This will always fetch the next block of whatever `state` is referring to, because this is
/// the only sensible combination. In other words, if you have the state of block `n`, you
/// should execute block `n+1` on top of it.
///
/// If `state` is `Live`, this can be ignored and the same uri is used for both.
#[arg(
long,
value_parser = crate::parse::url
)]
pub block_ws_uri: Option<String>,
/// The state type to use.
#[command(subcommand)]
pub state: State,
}
impl ExecuteBlockCmd {
fn block_ws_uri<Block: BlockT>(&self) -> String
where
<Block::Hash as FromStr>::Err: Debug,
{
match (&self.block_ws_uri, &self.state) {
(Some(block_ws_uri), State::Snap { .. }) => block_ws_uri.to_owned(),
(Some(block_ws_uri), State::Live { .. }) => {
log::error!(target: LOG_TARGET, "--block-uri is provided while state type is live, Are you sure you know what you are doing?");
block_ws_uri.to_owned()
},
(None, State::Live(LiveState { uri, .. })) => uri.clone(),
(None, State::Snap { .. }) => {
panic!("either `--block-uri` must be provided, or state must be `live`");
},
}
}
}
pub(crate) async fn execute_block<Block, HostFns>(
shared: SharedParams,
command: ExecuteBlockCmd,
) -> sc_cli::Result<()>
where
Block: BlockT + serde::de::DeserializeOwned,
<Block::Hash as FromStr>::Err: Debug,
Block::Hash: serde::de::DeserializeOwned,
Block::Header: serde::de::DeserializeOwned,
<NumberFor<Block> as TryInto<u64>>::Error: Debug,
HostFns: HostFunctions,
{
let executor = build_executor::<HostFns>(&shared);
let ext = command.state.into_ext::<Block, HostFns>(&shared, &executor, None, true).await?;
// get the block number associated with this block.
let block_ws_uri = command.block_ws_uri::<Block>();
let rpc = ws_client(&block_ws_uri).await?;
let next_hash = next_hash_of::<Block>(&rpc, ext.block_hash).await?;
log::info!(target: LOG_TARGET, "fetching next block: {:?} ", next_hash);
let block = ChainApi::<(), Block::Hash, Block::Header, SignedBlock<Block>>::block(
&rpc,
Some(next_hash),
)
.await
.map_err(rpc_err_handler)?
.expect("header exists, block should also exist; qed")
.block;
// A digest item gets added when the runtime is processing the block, so we need to pop
// the last one to be consistent with what a gossiped block would contain.
let (mut header, extrinsics) = block.deconstruct();
header.digest_mut().pop();
let block = Block::new(header, extrinsics);
// for now, hardcoded for the sake of simplicity. We might customize them one day.
let state_root_check = false;
let signature_check = false;
let payload = (block.clone(), state_root_check, signature_check, command.try_state).encode();
let _ = state_machine_call_with_proof::<Block, HostFns>(
&ext,
&executor,
"TryRuntime_execute_block",
&payload,
full_extensions(executor.clone()),
shared.export_proof,
)?;
Ok(())
}
pub(crate) async fn next_hash_of<Block: BlockT>(
rpc: &substrate_rpc_client::WsClient,
hash: Block::Hash,
) -> sc_cli::Result<Block::Hash>
where
Block: BlockT + serde::de::DeserializeOwned,
Block::Header: serde::de::DeserializeOwned,
{
let number = ChainApi::<(), Block::Hash, Block::Header, ()>::header(rpc, Some(hash))
.await
.map_err(rpc_err_handler)
.and_then(|maybe_header| maybe_header.ok_or("header_not_found").map(|h| *h.number()))?;
let next = number + sp_runtime::traits::One::one();
let next_hash = match ChainApi::<(), Block::Hash, Block::Header, ()>::block_hash(
rpc,
Some(ListOrValue::Value(NumberOrHex::Number(
next.try_into().map_err(|_| "failed to convert number to block number")?,
))),
)
.await
.map_err(rpc_err_handler)?
{
ListOrValue::Value(t) => t.expect("value passed in; value comes out; qed"),
_ => unreachable!(),
};
Ok(next_hash)
}
@@ -1,262 +0,0 @@
// 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 crate::{
block_building_info::BlockBuildingInfoProvider, build_executor, full_extensions,
rpc_err_handler, state_machine_call, BlockT, LiveState, SharedParams, State,
};
use parity_scale_codec::{Decode, Encode};
use sc_cli::Result;
use sc_executor::{sp_wasm_interface::HostFunctions, WasmExecutor};
use serde::de::DeserializeOwned;
use sp_core::H256;
use sp_inherents::{InherentData, InherentDataProvider};
use sp_runtime::{
traits::{HashingFor, Header, NumberFor, One},
Digest,
};
use sp_state_machine::TestExternalities;
use std::{fmt::Debug, str::FromStr};
use substrate_rpc_client::{ws_client, ChainApi};
/// Configurations of the [`crate::Command::FastForward`].
#[derive(Debug, Clone, clap::Parser)]
pub struct FastForwardCmd {
/// How many blocks should be processed. If `None`, then blocks will be produced and processed
/// in a loop.
#[arg(long)]
n_blocks: Option<u64>,
/// The state type to use.
#[command(subcommand)]
state: State,
/// The ws uri from which to fetch the block.
///
/// If `state` is `Live`, this is ignored. Otherwise, it must not be empty.
#[arg(long, value_parser = crate::parse::url)]
block_ws_uri: Option<String>,
/// Which try-state targets to execute when running this command.
///
/// Expected values:
/// - `all`
/// - `none`
/// - A comma separated list of pallets, as per pallet names in `construct_runtime!()` (e.g.
/// `Staking, System`).
/// - `rr-[x]` where `[x]` is a number. Then, the given number of pallets are checked in a
/// round-robin fashion.
#[arg(long, default_value = "all")]
try_state: frame_try_runtime::TryStateSelect,
}
impl FastForwardCmd {
fn block_ws_uri(&self) -> &str {
match self.state {
State::Live(LiveState { ref uri, .. }) => &uri,
_ => self
.block_ws_uri
.as_ref()
.expect("Either `--block-uri` must be provided, or state must be `live`"),
}
}
}
/// Read the block number corresponding to `hash` with an RPC call to `ws_uri`.
async fn get_block_number<Block: BlockT>(
hash: Block::Hash,
ws_uri: &str,
) -> Result<NumberFor<Block>>
where
Block::Header: DeserializeOwned,
{
let rpc = ws_client(ws_uri).await?;
Ok(ChainApi::<(), Block::Hash, Block::Header, ()>::header(&rpc, Some(hash))
.await
.map_err(rpc_err_handler)
.and_then(|maybe_header| maybe_header.ok_or("header_not_found").map(|h| *h.number()))?)
}
/// Call `method` with `data` and return the result. `externalities` will not change.
fn dry_run<T: Decode, Block: BlockT, HostFns: HostFunctions>(
externalities: &TestExternalities<HashingFor<Block>>,
executor: &WasmExecutor<HostFns>,
method: &'static str,
data: &[u8],
) -> Result<T> {
let (_, result) = state_machine_call::<Block, HostFns>(
externalities,
executor,
method,
data,
full_extensions(executor.clone()),
)?;
Ok(<T>::decode(&mut &*result)?)
}
/// Call `method` with `data` and actually save storage changes to `externalities`.
async fn run<Block: BlockT, HostFns: HostFunctions>(
externalities: &mut TestExternalities<HashingFor<Block>>,
executor: &WasmExecutor<HostFns>,
method: &'static str,
data: &[u8],
) -> Result<()> {
let (mut changes, _) = state_machine_call::<Block, HostFns>(
externalities,
executor,
method,
data,
full_extensions(executor.clone()),
)?;
let storage_changes =
changes.drain_storage_changes(&externalities.backend, externalities.state_version)?;
externalities
.backend
.apply_transaction(storage_changes.transaction_storage_root, storage_changes.transaction);
Ok(())
}
/// Produce next empty block.
async fn next_empty_block<
Block: BlockT,
HostFns: HostFunctions,
BBIP: BlockBuildingInfoProvider<Block, Option<(InherentData, Digest)>>,
>(
externalities: &mut TestExternalities<HashingFor<Block>>,
executor: &WasmExecutor<HostFns>,
parent_height: NumberFor<Block>,
parent_hash: Block::Hash,
block_building_info_provider: &Option<BBIP>,
previous_block_building_info: Option<(InherentData, Digest)>,
) -> Result<(Block, Option<(InherentData, Digest)>)> {
let (maybe_inherent_data, pre_digest) = match &block_building_info_provider {
None => (None, Default::default()),
Some(bbip) => {
let (inherent_data_provider, pre_digest) = bbip
.get_inherent_providers_and_pre_digest(parent_hash, previous_block_building_info)
.await?;
let inherent_data = inherent_data_provider
.create_inherent_data()
.await
.map_err(|e| sc_cli::Error::Input(format!("I don't know how to convert {e:?}")))?;
(Some(inherent_data), Digest { logs: pre_digest })
},
};
let header = Block::Header::new(
parent_height + One::one(),
Default::default(),
Default::default(),
parent_hash,
pre_digest.clone(),
);
let mut extrinsics = <Vec<Block::Extrinsic>>::new();
run::<Block, _>(externalities, executor, "Core_initialize_block", &header.encode()).await?;
if let Some(ref inherent_data) = maybe_inherent_data {
extrinsics = dry_run::<Vec<Block::Extrinsic>, Block, _>(
externalities,
executor,
"BlockBuilder_inherent_extrinsics",
&inherent_data.encode(),
)?;
}
for xt in &extrinsics {
run::<Block, _>(externalities, executor, "BlockBuilder_apply_extrinsic", &xt.encode())
.await?;
}
let header = dry_run::<Block::Header, Block, _>(
externalities,
executor,
"BlockBuilder_finalize_block",
&[0u8; 0],
)?;
run::<Block, _>(externalities, executor, "BlockBuilder_finalize_block", &[0u8; 0]).await?;
Ok((Block::new(header, extrinsics), (maybe_inherent_data.map(|id| (id, pre_digest)))))
}
pub(crate) async fn fast_forward<Block, HostFns, BBIP>(
shared: SharedParams,
command: FastForwardCmd,
block_building_info_provider: Option<BBIP>,
) -> Result<()>
where
Block: BlockT<Hash = H256> + DeserializeOwned,
Block::Header: DeserializeOwned,
<Block::Hash as FromStr>::Err: Debug,
NumberFor<Block>: FromStr,
<NumberFor<Block> as FromStr>::Err: Debug,
HostFns: HostFunctions,
BBIP: BlockBuildingInfoProvider<Block, Option<(InherentData, Digest)>>,
{
let executor = build_executor::<HostFns>(&shared);
let ext = command.state.into_ext::<Block, HostFns>(&shared, &executor, None, true).await?;
let mut last_block_hash = ext.block_hash;
let mut last_block_number =
get_block_number::<Block>(last_block_hash, command.block_ws_uri()).await?;
let mut prev_block_building_info = None;
let mut ext = ext.inner_ext;
for _ in 1..=command.n_blocks.unwrap_or(u64::MAX) {
// We are saving state before we overwrite it while producing new block.
let backend = ext.as_backend();
log::info!("Producing new empty block at height {:?}", last_block_number + One::one());
let (next_block, new_block_building_info) = next_empty_block::<Block, HostFns, BBIP>(
&mut ext,
&executor,
last_block_number,
last_block_hash,
&block_building_info_provider,
prev_block_building_info,
)
.await?;
log::info!("Produced a new block: {:?}", next_block.header());
// And now we restore previous state.
ext.backend = backend;
let state_root_check = true;
let signature_check = true;
let payload =
(next_block.clone(), state_root_check, signature_check, command.try_state.clone())
.encode();
run::<Block, _>(&mut ext, &executor, "TryRuntime_execute_block", &payload).await?;
log::info!("Executed the new block");
prev_block_building_info = new_block_building_info;
last_block_hash = next_block.hash();
last_block_number += One::one();
}
Ok(())
}
@@ -1,203 +0,0 @@
// 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 crate::{
build_executor, full_extensions, parse, rpc_err_handler, state_machine_call_with_proof,
LiveState, SharedParams, State, LOG_TARGET,
};
use parity_scale_codec::{Decode, Encode};
use sc_executor::sp_wasm_interface::HostFunctions;
use serde::{de::DeserializeOwned, Serialize};
use sp_core::H256;
use sp_runtime::{
generic::SignedBlock,
traits::{Block as BlockT, Header as HeaderT, NumberFor},
};
use std::{fmt::Debug, str::FromStr};
use substrate_rpc_client::{ws_client, ChainApi, FinalizedHeaders, Subscription, WsClient};
const SUB: &str = "chain_subscribeFinalizedHeads";
const UN_SUB: &str = "chain_unsubscribeFinalizedHeads";
/// Configurations of the [`crate::Command::FollowChain`].
#[derive(Debug, Clone, clap::Parser)]
pub struct FollowChainCmd {
/// The url to connect to.
#[arg(short, long, value_parser = parse::url)]
pub uri: String,
/// If set, then the state root check is enabled.
#[arg(long)]
pub state_root_check: bool,
/// Which try-state targets to execute when running this command.
///
/// Expected values:
/// - `all`
/// - `none`
/// - A comma separated list of pallets, as per pallet names in `construct_runtime!()` (e.g.
/// `Staking, System`).
/// - `rr-[x]` where `[x]` is a number. Then, the given number of pallets are checked in a
/// round-robin fashion.
#[arg(long, default_value = "all")]
pub try_state: frame_try_runtime::TryStateSelect,
/// If present, a single connection to a node will be kept and reused for fetching blocks.
#[arg(long)]
pub keep_connection: bool,
}
/// Start listening for with `SUB` at `url`.
///
/// Returns a pair `(client, subscription)` - `subscription` alone will be useless, because it
/// relies on the related alive `client`.
async fn start_subscribing<Header: DeserializeOwned + Serialize + Send + Sync + 'static>(
url: &str,
) -> sc_cli::Result<(WsClient, Subscription<Header>)> {
let client = ws_client(url).await.map_err(|e| sc_cli::Error::Application(e.into()))?;
log::info!(target: LOG_TARGET, "subscribing to {:?} / {:?}", SUB, UN_SUB);
let sub = ChainApi::<(), (), Header, ()>::subscribe_finalized_heads(&client)
.await
.map_err(|e| sc_cli::Error::Application(e.into()))?;
Ok((client, sub))
}
pub(crate) async fn follow_chain<Block, HostFns>(
shared: SharedParams,
command: FollowChainCmd,
) -> sc_cli::Result<()>
where
Block: BlockT<Hash = H256> + DeserializeOwned,
Block::Header: DeserializeOwned,
<Block::Hash as FromStr>::Err: Debug,
NumberFor<Block>: FromStr,
<NumberFor<Block> as FromStr>::Err: Debug,
HostFns: HostFunctions,
{
let (rpc, subscription) = start_subscribing::<Block::Header>(&command.uri).await?;
let mut finalized_headers: FinalizedHeaders<Block, _, _> =
FinalizedHeaders::new(&rpc, subscription);
let mut maybe_state_ext = None;
let executor = build_executor::<HostFns>(&shared);
while let Some(header) = finalized_headers.next().await {
let hash = header.hash();
let number = header.number();
let block =
ChainApi::<(), Block::Hash, Block::Header, SignedBlock<Block>>::block(&rpc, Some(hash))
.await
.or_else(|e| {
if matches!(e, substrate_rpc_client::Error::ParseError(_)) {
log::error!(
target: LOG_TARGET,
"failed to parse the block format of remote against the local \
codebase. The block format has changed, and follow-chain cannot run in \
this case. Try running this command in a branch of your codebase that
has the same block format as the remote chain. For now, we replace the \
block with an empty one."
);
}
Err(rpc_err_handler(e))
})?
.expect("if header exists, block should also exist.")
.block;
log::debug!(
target: LOG_TARGET,
"new block event: {:?} => {:?}, extrinsics: {}",
hash,
number,
block.extrinsics().len()
);
// create an ext at the state of this block, whatever is the first subscription event.
if maybe_state_ext.is_none() {
let state = State::Live(LiveState {
uri: command.uri.clone(),
// a bit dodgy, we have to un-parse the has to a string again and re-parse it
// inside.
at: Some(hex::encode(header.parent_hash().encode())),
pallet: vec![],
child_tree: true,
});
let ext = state.into_ext::<Block, HostFns>(&shared, &executor, None, true).await?;
maybe_state_ext = Some(ext);
}
let state_ext =
maybe_state_ext.as_mut().expect("state_ext either existed or was just created");
let result = state_machine_call_with_proof::<Block, HostFns>(
state_ext,
&executor,
"TryRuntime_execute_block",
(block, command.state_root_check, true, command.try_state.clone())
.encode()
.as_ref(),
full_extensions(executor.clone()),
shared
.export_proof
.as_ref()
.map(|path| path.as_path().join(&format!("{}.json", number))),
);
if let Err(why) = result {
log::error!(
target: LOG_TARGET,
"failed to execute block {:?} due to {:?}",
number,
why
);
continue
}
let (mut changes, encoded_result) = result.expect("checked to be Ok; qed");
let consumed_weight = <sp_weights::Weight as Decode>::decode(&mut &*encoded_result)
.map_err(|e| format!("failed to decode weight: {:?}", e))?;
let storage_changes = changes
.drain_storage_changes(
&state_ext.backend,
// Note that in case a block contains a runtime upgrade, state version could
// potentially be incorrect here, this is very niche and would only result in
// unaligned roots, so this use case is ignored for now.
state_ext.state_version,
)
.unwrap();
state_ext.backend.apply_transaction(
storage_changes.transaction_storage_root,
storage_changes.transaction,
);
log::info!(
target: LOG_TARGET,
"executed block {}, consumed weight {}, new storage root {:?}",
number,
consumed_weight,
state_ext.as_backend().root(),
);
}
log::error!(target: LOG_TARGET, "ws subscription must have terminated.");
Ok(())
}
@@ -1,23 +0,0 @@
// 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.
pub mod create_snapshot;
pub mod execute_block;
pub mod fast_forward;
pub mod follow_chain;
pub mod offchain_worker;
pub mod on_runtime_upgrade;
@@ -1,102 +0,0 @@
// 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 crate::{
build_executor, commands::execute_block::next_hash_of, full_extensions, parse, rpc_err_handler,
state_machine_call, LiveState, SharedParams, State, LOG_TARGET,
};
use parity_scale_codec::Encode;
use sc_executor::sp_wasm_interface::HostFunctions;
use sp_runtime::traits::{Block as BlockT, NumberFor};
use std::{fmt::Debug, str::FromStr};
use substrate_rpc_client::{ws_client, ChainApi};
/// Configurations of the [`crate::Command::OffchainWorker`].
#[derive(Debug, Clone, clap::Parser)]
pub struct OffchainWorkerCmd {
/// The ws uri from which to fetch the header.
///
/// If the `live` state type is being used, then this can be omitted, and is equal to whatever
/// the `state::uri` is. Only use this (with care) when combined with a snapshot.
#[arg(
long,
value_parser = parse::url
)]
pub header_ws_uri: Option<String>,
/// The state type to use.
#[command(subcommand)]
pub state: State,
}
impl OffchainWorkerCmd {
fn header_ws_uri<Block: BlockT>(&self) -> String
where
<Block::Hash as FromStr>::Err: Debug,
{
match (&self.header_ws_uri, &self.state) {
(Some(header_ws_uri), State::Snap { .. }) => header_ws_uri.to_owned(),
(Some(header_ws_uri), State::Live { .. }) => {
log::error!(target: LOG_TARGET, "--header-uri is provided while state type is live, this will most likely lead to a nonsensical result.");
header_ws_uri.to_owned()
},
(None, State::Live(LiveState { uri, .. })) => uri.clone(),
(None, State::Snap { .. }) => {
panic!("either `--header-uri` must be provided, or state must be `live`");
},
}
}
}
pub(crate) async fn offchain_worker<Block, HostFns>(
shared: SharedParams,
command: OffchainWorkerCmd,
) -> sc_cli::Result<()>
where
Block: BlockT + serde::de::DeserializeOwned,
Block::Header: serde::de::DeserializeOwned,
<Block::Hash as FromStr>::Err: Debug,
NumberFor<Block>: FromStr,
<NumberFor<Block> as FromStr>::Err: Debug,
HostFns: HostFunctions,
{
let executor = build_executor(&shared);
// we first build the externalities with the remote code.
let ext = command.state.into_ext::<Block, HostFns>(&shared, &executor, None, true).await?;
let header_ws_uri = command.header_ws_uri::<Block>();
let rpc = ws_client(&header_ws_uri).await?;
let next_hash = next_hash_of::<Block>(&rpc, ext.block_hash).await?;
log::info!(target: LOG_TARGET, "fetching next header: {:?} ", next_hash);
let header = ChainApi::<(), Block::Hash, Block::Header, ()>::header(&rpc, Some(next_hash))
.await
.map_err(rpc_err_handler)
.map(|maybe_header| maybe_header.ok_or("Header does not exist"))??;
let payload = header.encode();
let _ = state_machine_call::<Block, HostFns>(
&ext,
&executor,
"OffchainWorkerApi_offchain_worker",
&payload,
full_extensions(executor.clone()),
)?;
Ok(())
}
@@ -1,89 +0,0 @@
// 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 crate::{build_executor, state_machine_call_with_proof, SharedParams, State, LOG_TARGET};
use frame_try_runtime::UpgradeCheckSelect;
use parity_scale_codec::{Decode, Encode};
use sc_executor::sp_wasm_interface::HostFunctions;
use sp_runtime::traits::{Block as BlockT, NumberFor};
use sp_weights::Weight;
use std::{fmt::Debug, str::FromStr};
/// Configurations of the [`crate::Command::OnRuntimeUpgrade`].
#[derive(Debug, Clone, clap::Parser)]
pub struct OnRuntimeUpgradeCmd {
/// The state type to use.
#[command(subcommand)]
pub state: State,
/// Select which optional checks to perform. Selects all when no value is given.
///
/// - `none`: Perform no checks.
/// - `all`: Perform all checks (default when --checks is present with no value).
/// - `pre-and-post`: Perform pre- and post-upgrade checks (default when the arg is not
/// present).
/// - `try-state`: Perform the try-state checks.
///
/// Performing any checks will potentially invalidate the measured PoV/Weight.
// NOTE: The clap attributes make it backwards compatible with the previous `--checks` flag.
#[clap(long,
default_value = "pre-and-post",
default_missing_value = "all",
num_args = 0..=1,
require_equals = true,
verbatim_doc_comment)]
pub checks: UpgradeCheckSelect,
}
pub(crate) async fn on_runtime_upgrade<Block, HostFns>(
shared: SharedParams,
command: OnRuntimeUpgradeCmd,
) -> sc_cli::Result<()>
where
Block: BlockT + serde::de::DeserializeOwned,
<Block::Hash as FromStr>::Err: Debug,
Block::Header: serde::de::DeserializeOwned,
NumberFor<Block>: FromStr,
<NumberFor<Block> as FromStr>::Err: Debug,
HostFns: HostFunctions,
{
let executor = build_executor(&shared);
let ext = command.state.into_ext::<Block, HostFns>(&shared, &executor, None, true).await?;
let (_, encoded_result) = state_machine_call_with_proof::<Block, HostFns>(
&ext,
&executor,
"TryRuntime_on_runtime_upgrade",
command.checks.encode().as_ref(),
Default::default(), // we don't really need any extensions here.
shared.export_proof,
)?;
let (weight, total_weight) = <(Weight, Weight) as Decode>::decode(&mut &*encoded_result)
.map_err(|e| format!("failed to decode weight: {:?}", e))?;
log::info!(
target: LOG_TARGET,
"TryRuntime_on_runtime_upgrade executed without errors. Consumed weight = ({} ps, {} byte), total weight = ({} ps, {} byte) ({:.2} %, {:.2} %).",
weight.ref_time(), weight.proof_size(),
total_weight.ref_time(), total_weight.proof_size(),
(weight.ref_time() as f64 / total_weight.ref_time().max(1) as f64) * 100.0,
(weight.proof_size() as f64 / total_weight.proof_size().max(1) as f64) * 100.0,
);
Ok(())
}
@@ -1,701 +0,0 @@
// 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.
//! # Try-runtime
//!
//! Substrate's `try-runtime` subcommand has been migrated to a [standalone
//! CLI](https://github.com/paritytech/try-runtime-cli).
//!
//! It is no longer maintained here and will be removed in the future.
#![cfg(feature = "try-runtime")]
use crate::block_building_info::BlockBuildingInfoProvider;
use parity_scale_codec::Decode;
use remote_externalities::{
Builder, Mode, OfflineConfig, OnlineConfig, RemoteExternalities, SnapshotConfig,
};
use sc_cli::{
execution_method_from_cli, CliConfiguration, RuntimeVersion, WasmExecutionMethod,
WasmtimeInstantiationStrategy, DEFAULT_WASMTIME_INSTANTIATION_STRATEGY,
DEFAULT_WASM_EXECUTION_METHOD,
};
use sc_executor::{
sp_wasm_interface::HostFunctions, HeapAllocStrategy, WasmExecutor, DEFAULT_HEAP_ALLOC_STRATEGY,
};
use sp_core::{
hexdisplay::HexDisplay,
offchain::{
testing::{TestOffchainExt, TestTransactionPoolExt},
OffchainDbExt, OffchainWorkerExt, TransactionPoolExt,
},
storage::well_known_keys,
traits::{CallContext, ReadRuntimeVersion, ReadRuntimeVersionExt},
twox_128, H256,
};
use sp_externalities::Extensions;
use sp_inherents::InherentData;
use sp_keystore::{testing::MemoryKeystore, KeystoreExt};
use sp_runtime::{
traits::{BlakeTwo256, Block as BlockT, Hash as HashT, HashingFor, NumberFor},
DeserializeOwned, Digest,
};
use sp_state_machine::{
CompactProof, OverlayedChanges, StateMachine, TestExternalities, TrieBackendBuilder,
};
use sp_version::StateVersion;
use std::{fmt::Debug, path::PathBuf, str::FromStr};
pub mod block_building_info;
pub mod commands;
pub(crate) mod parse;
pub(crate) const LOG_TARGET: &str = "try-runtime::cli";
/// Possible commands of `try-runtime`.
#[derive(Debug, Clone, clap::Subcommand)]
pub enum Command {
/// Execute the migrations of the given runtime
///
/// This uses a custom runtime api call, namely "TryRuntime_on_runtime_upgrade". The code path
/// only triggers all of the `on_runtime_upgrade` hooks in the runtime, and optionally
/// `try_state`.
///
/// See [`frame_try_runtime::TryRuntime`] and
/// [`commands::on_runtime_upgrade::OnRuntimeUpgradeCmd`] for more information.
OnRuntimeUpgrade(commands::on_runtime_upgrade::OnRuntimeUpgradeCmd),
/// Executes the given block against some state.
///
/// This uses a custom runtime api call, namely "TryRuntime_execute_block". Some checks, such
/// as state-root and signature checks are always disabled, and additional checks like
/// `try-state` can be enabled.
///
/// See [`frame_try_runtime::TryRuntime`] and [`commands::execute_block::ExecuteBlockCmd`] for
/// more information.
ExecuteBlock(commands::execute_block::ExecuteBlockCmd),
/// Executes *the offchain worker hooks* of a given block against some state.
///
/// This executes the same runtime api as normal block import, namely
/// `OffchainWorkerApi_offchain_worker`.
///
/// See [`frame_try_runtime::TryRuntime`] and [`commands::offchain_worker::OffchainWorkerCmd`]
/// for more information.
OffchainWorker(commands::offchain_worker::OffchainWorkerCmd),
/// Follow the given chain's finalized blocks and apply all of its extrinsics.
///
/// This is essentially repeated calls to [`Command::ExecuteBlock`].
///
/// This allows the behavior of a new runtime to be inspected over a long period of time, with
/// realistic transactions coming as input.
///
/// NOTE: this does NOT execute the offchain worker hooks of mirrored blocks. This might be
/// added in the future.
///
/// This does not support snapshot states, and can only work with a remote chain. Upon first
/// connections, starts listening for finalized block events. Upon first block notification, it
/// initializes the state from the remote node, and starts applying that block, plus all the
/// blocks that follow, to the same growing state.
///
/// This can only work if the block format between the remote chain and the new runtime being
/// tested has remained the same, otherwise block decoding might fail.
FollowChain(commands::follow_chain::FollowChainCmd),
/// Produce a series of empty, consecutive blocks and execute them one-by-one.
///
/// To compare it with [`Command::FollowChain`]:
/// - we don't have the delay of the original blocktime (for Polkadot 6s), but instead, we
/// execute every block immediately
/// - the only data that will be put into blocks are pre-runtime digest items and inherent
/// extrinsics; both things should be defined in your node CLI handling level
FastForward(commands::fast_forward::FastForwardCmd),
/// Create a new snapshot file.
CreateSnapshot(commands::create_snapshot::CreateSnapshotCmd),
}
#[derive(Debug, Clone)]
pub enum Runtime {
/// Use the given path to the wasm binary file.
///
/// It must have been compiled with `try-runtime`.
Path(PathBuf),
/// Use the code of the remote node, or the snapshot.
///
/// In almost all cases, this is not what you want, because the code in the remote node does
/// not have any of the try-runtime custom runtime APIs.
Existing,
}
impl FromStr for Runtime {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(match s.to_lowercase().as_ref() {
"existing" => Runtime::Existing,
x @ _ => Runtime::Path(x.into()),
})
}
}
/// Shared parameters of the `try-runtime` commands
#[derive(Debug, Clone, clap::Parser)]
#[group(skip)]
pub struct SharedParams {
/// Shared parameters of substrate cli.
///
/// TODO: this is only needed because try-runtime is embedded in the substrate CLI. It should
/// go away.
#[allow(missing_docs)]
#[clap(flatten)]
pub shared_params: sc_cli::SharedParams,
/// The runtime to use.
///
/// Must be a path to a wasm blob, compiled with `try-runtime` feature flag.
///
/// Or, `existing`, indicating that you don't want to overwrite the runtime. This will use
/// whatever comes from the remote node, or the snapshot file. This will most likely not work
/// against a remote node, as no (sane) blockchain should compile its onchain wasm with
/// `try-runtime` feature.
#[arg(long)]
pub runtime: Runtime,
/// Type of wasm execution used.
#[arg(
long = "wasm-execution",
value_name = "METHOD",
value_enum,
ignore_case = true,
default_value_t = DEFAULT_WASM_EXECUTION_METHOD,
)]
pub wasm_method: WasmExecutionMethod,
/// The WASM instantiation method to use.
///
/// Only has an effect when `wasm-execution` is set to `compiled`.
#[arg(
long = "wasm-instantiation-strategy",
value_name = "STRATEGY",
default_value_t = DEFAULT_WASMTIME_INSTANTIATION_STRATEGY,
value_enum,
)]
pub wasmtime_instantiation_strategy: WasmtimeInstantiationStrategy,
/// The number of 64KB pages to allocate for Wasm execution. Defaults to
/// [`sc_service::Configuration.default_heap_pages`].
#[arg(long)]
pub heap_pages: Option<u64>,
/// Path to a file to export the storage proof into (as a JSON).
/// If several blocks are executed, the path is interpreted as a folder
/// where one file per block will be written (named `{block_number}-{block_hash}`).
#[clap(long)]
pub export_proof: Option<PathBuf>,
/// Overwrite the `state_version`.
///
/// Otherwise `remote-externalities` will automatically set the correct state version.
#[arg(long, value_parser = parse::state_version)]
pub overwrite_state_version: Option<StateVersion>,
}
/// Our `try-runtime` command.
///
/// See [`Command`] for more info.
#[derive(Debug, Clone, clap::Parser)]
pub struct TryRuntimeCmd {
#[clap(flatten)]
pub shared: SharedParams,
#[command(subcommand)]
pub command: Command,
}
/// A `Live` variant [`State`]
#[derive(Debug, Clone, clap::Args)]
pub struct LiveState {
/// The url to connect to.
#[arg(
short,
long,
value_parser = parse::url,
)]
uri: String,
/// The block hash at which to fetch the state.
///
/// If non provided, then the latest finalized head is used.
#[arg(
short,
long,
value_parser = parse::hash,
)]
at: Option<String>,
/// A pallet to scrape. Can be provided multiple times. If empty, entire chain state will
/// be scraped.
#[arg(short, long, num_args = 1..)]
pallet: Vec<String>,
/// Fetch the child-keys as well.
///
/// Default is `false`, if specific `--pallets` are specified, `true` otherwise. In other
/// words, if you scrape the whole state the child tree data is included out of the box.
/// Otherwise, it must be enabled explicitly using this flag.
#[arg(long)]
child_tree: bool,
}
/// The source of runtime *state* to use.
#[derive(Debug, Clone, clap::Subcommand)]
pub enum State {
/// Use a state snapshot as the source of runtime state.
Snap {
#[arg(short, long)]
snapshot_path: PathBuf,
},
/// Use a live chain as the source of runtime state.
Live(LiveState),
}
impl State {
/// Create the [`remote_externalities::RemoteExternalities`] using [`remote-externalities`] from
/// self.
///
/// This will override the code as it sees fit based on [`SharedParams::Runtime`]. It will also
/// check the spec-version and name.
pub(crate) async fn into_ext<Block: BlockT + DeserializeOwned, HostFns: HostFunctions>(
&self,
shared: &SharedParams,
executor: &WasmExecutor<HostFns>,
state_snapshot: Option<SnapshotConfig>,
try_runtime_check: bool,
) -> sc_cli::Result<RemoteExternalities<HashingFor<Block>>>
where
Block::Header: DeserializeOwned,
<Block::Hash as FromStr>::Err: Debug,
{
let builder = match self {
State::Snap { snapshot_path } =>
Builder::<Block>::new().mode(Mode::Offline(OfflineConfig {
state_snapshot: SnapshotConfig::new(snapshot_path),
})),
State::Live(LiveState { pallet, uri, at, child_tree }) => {
let at = match at {
Some(at_str) => Some(hash_of::<Block>(at_str)?),
None => None,
};
Builder::<Block>::new().mode(Mode::Online(OnlineConfig {
at,
transport: uri.to_owned().into(),
state_snapshot,
pallets: pallet.clone(),
child_trie: *child_tree,
hashed_keys: vec![
// we always download the code, but we almost always won't use it, based on
// `Runtime`.
well_known_keys::CODE.to_vec(),
// we will always download this key, since it helps detect if we should do
// runtime migration or not.
[twox_128(b"System"), twox_128(b"LastRuntimeUpgrade")].concat(),
[twox_128(b"System"), twox_128(b"Number")].concat(),
],
hashed_prefixes: vec![],
}))
},
};
// possibly overwrite the state version, should hardly be needed.
let builder = if let Some(state_version) = shared.overwrite_state_version {
log::warn!(
target: LOG_TARGET,
"overwriting state version to {:?}, you better know what you are doing.",
state_version
);
builder.overwrite_state_version(state_version)
} else {
builder
};
// then, we prepare to replace the code based on what the CLI wishes.
let maybe_code_to_overwrite = match shared.runtime {
Runtime::Path(ref path) => Some(std::fs::read(path).map_err(|e| {
format!("error while reading runtime file from {:?}: {:?}", path, e)
})?),
Runtime::Existing => None,
};
// build the main ext.
let mut ext = builder.build().await?;
// actually replace the code if needed.
if let Some(new_code) = maybe_code_to_overwrite {
let original_code = ext
.execute_with(|| sp_io::storage::get(well_known_keys::CODE))
.expect("':CODE:' is always downloaded in try-runtime-cli; qed");
// NOTE: see the impl notes of `read_runtime_version`, the ext is almost not used here,
// only as a backup.
ext.insert(well_known_keys::CODE.to_vec(), new_code.clone());
let old_version = <RuntimeVersion as Decode>::decode(
&mut &*executor.read_runtime_version(&original_code, &mut ext.ext()).unwrap(),
)
.unwrap();
log::info!(
target: LOG_TARGET,
"original spec: {:?}-{:?}, code hash: {:?}",
old_version.spec_name,
old_version.spec_version,
HexDisplay::from(BlakeTwo256::hash(&original_code).as_fixed_bytes()),
);
let new_version = <RuntimeVersion as Decode>::decode(
&mut &*executor.read_runtime_version(&new_code, &mut ext.ext()).unwrap(),
)
.unwrap();
log::info!(
target: LOG_TARGET,
"new spec: {:?}-{:?}, code hash: {:?}",
new_version.spec_name,
new_version.spec_version,
HexDisplay::from(BlakeTwo256::hash(&new_code).as_fixed_bytes())
);
if new_version.spec_name != old_version.spec_name {
return Err("Spec names must match.".into())
}
}
// whatever runtime we have in store now must have been compiled with try-runtime feature.
if try_runtime_check {
if !ensure_try_runtime::<Block, HostFns>(&executor, &mut ext) {
return Err("given runtime is NOT compiled with try-runtime feature!".into())
}
}
Ok(ext)
}
}
pub const DEPRECATION_NOTICE: &str = "Substrate's `try-runtime` subcommand has been migrated to a standalone CLI (https://github.com/paritytech/try-runtime-cli). It is no longer being maintained here and will be removed entirely some time after January 2024. Please remove this subcommand from your runtime and use the standalone CLI.";
impl TryRuntimeCmd {
// Can't reuse DEPRECATION_NOTICE in the deprecated macro
#[deprecated(
note = "Substrate's `try-runtime` subcommand has been migrated to a standalone CLI (https://github.com/paritytech/try-runtime-cli). It is no longer being maintained here and will be removed entirely some time after January 2024. Please remove this subcommand from your runtime and use the standalone CLI."
)]
pub async fn run<Block, HostFns, BBIP>(
&self,
block_building_info_provider: Option<BBIP>,
) -> sc_cli::Result<()>
where
Block: BlockT<Hash = H256> + DeserializeOwned,
Block::Header: DeserializeOwned,
<Block::Hash as FromStr>::Err: Debug,
<NumberFor<Block> as FromStr>::Err: Debug,
<NumberFor<Block> as TryInto<u64>>::Error: Debug,
NumberFor<Block>: FromStr,
HostFns: HostFunctions,
BBIP: BlockBuildingInfoProvider<Block, Option<(InherentData, Digest)>>,
{
match &self.command {
Command::OnRuntimeUpgrade(ref cmd) =>
commands::on_runtime_upgrade::on_runtime_upgrade::<Block, HostFns>(
self.shared.clone(),
cmd.clone(),
)
.await,
Command::OffchainWorker(cmd) =>
commands::offchain_worker::offchain_worker::<Block, HostFns>(
self.shared.clone(),
cmd.clone(),
)
.await,
Command::ExecuteBlock(cmd) =>
commands::execute_block::execute_block::<Block, HostFns>(
self.shared.clone(),
cmd.clone(),
)
.await,
Command::FollowChain(cmd) =>
commands::follow_chain::follow_chain::<Block, HostFns>(
self.shared.clone(),
cmd.clone(),
)
.await,
Command::FastForward(cmd) =>
commands::fast_forward::fast_forward::<Block, HostFns, BBIP>(
self.shared.clone(),
cmd.clone(),
block_building_info_provider,
)
.await,
Command::CreateSnapshot(cmd) =>
commands::create_snapshot::create_snapshot::<Block, HostFns>(
self.shared.clone(),
cmd.clone(),
)
.await,
}
}
}
impl CliConfiguration for TryRuntimeCmd {
fn shared_params(&self) -> &sc_cli::SharedParams {
&self.shared.shared_params
}
fn chain_id(&self, _is_dev: bool) -> sc_cli::Result<String> {
Ok(match self.shared.shared_params.chain {
Some(ref chain) => chain.clone(),
None => "dev".into(),
})
}
}
/// Get the hash type of the generic `Block` from a `hash_str`.
pub(crate) fn hash_of<Block: BlockT>(hash_str: &str) -> sc_cli::Result<Block::Hash>
where
<Block::Hash as FromStr>::Err: Debug,
{
hash_str
.parse::<<Block as BlockT>::Hash>()
.map_err(|e| format!("Could not parse block hash: {:?}", e).into())
}
/// Build all extensions that we typically use.
pub(crate) fn full_extensions<H: HostFunctions>(wasm_executor: WasmExecutor<H>) -> Extensions {
let mut extensions = Extensions::default();
let (offchain, _offchain_state) = TestOffchainExt::new();
let (pool, _pool_state) = TestTransactionPoolExt::new();
let keystore = MemoryKeystore::new();
extensions.register(OffchainDbExt::new(offchain.clone()));
extensions.register(OffchainWorkerExt::new(offchain));
extensions.register(KeystoreExt::new(keystore));
extensions.register(TransactionPoolExt::new(pool));
extensions.register(ReadRuntimeVersionExt::new(wasm_executor));
extensions
}
/// Build wasm executor by default config.
pub(crate) fn build_executor<H: HostFunctions>(shared: &SharedParams) -> WasmExecutor<H> {
let heap_pages = shared
.heap_pages
.map_or(DEFAULT_HEAP_ALLOC_STRATEGY, |p| HeapAllocStrategy::Static { extra_pages: p as _ });
WasmExecutor::builder()
.with_execution_method(execution_method_from_cli(
shared.wasm_method,
shared.wasmtime_instantiation_strategy,
))
.with_onchain_heap_alloc_strategy(heap_pages)
.with_offchain_heap_alloc_strategy(heap_pages)
.build()
}
/// Ensure that the given `ext` is compiled with `try-runtime`
fn ensure_try_runtime<Block: BlockT, HostFns: HostFunctions>(
executor: &WasmExecutor<HostFns>,
ext: &mut TestExternalities<HashingFor<Block>>,
) -> bool {
use sp_api::RuntimeApiInfo;
let final_code = ext
.execute_with(|| sp_io::storage::get(well_known_keys::CODE))
.expect("':CODE:' is always downloaded in try-runtime-cli; qed");
let final_version = <RuntimeVersion as Decode>::decode(
&mut &*executor.read_runtime_version(&final_code, &mut ext.ext()).unwrap(),
)
.unwrap();
final_version
.api_version(&<dyn frame_try_runtime::TryRuntime<Block>>::ID)
.is_some()
}
/// Execute the given `method` and `data` on top of `ext`, returning the results (encoded) and the
/// state `changes`.
pub(crate) fn state_machine_call<Block: BlockT, HostFns: HostFunctions>(
ext: &TestExternalities<HashingFor<Block>>,
executor: &WasmExecutor<HostFns>,
method: &'static str,
data: &[u8],
mut extensions: Extensions,
) -> sc_cli::Result<(OverlayedChanges<HashingFor<Block>>, Vec<u8>)> {
let mut changes = Default::default();
let encoded_results = StateMachine::new(
&ext.backend,
&mut changes,
executor,
method,
data,
&mut extensions,
&sp_state_machine::backend::BackendRuntimeCode::new(&ext.backend).runtime_code()?,
CallContext::Offchain,
)
.execute()
.map_err(|e| format!("failed to execute '{}': {}", method, e))
.map_err::<sc_cli::Error, _>(Into::into)?;
Ok((changes, encoded_results))
}
/// Same as [`state_machine_call`], but it also computes and prints the storage proof in different
/// size and formats.
///
/// Make sure [`LOG_TARGET`] is enabled in logging.
pub(crate) fn state_machine_call_with_proof<Block: BlockT, HostFns: HostFunctions>(
ext: &TestExternalities<HashingFor<Block>>,
executor: &WasmExecutor<HostFns>,
method: &'static str,
data: &[u8],
mut extensions: Extensions,
maybe_export_proof: Option<PathBuf>,
) -> sc_cli::Result<(OverlayedChanges<HashingFor<Block>>, Vec<u8>)> {
use parity_scale_codec::Encode;
let mut changes = Default::default();
let backend = ext.backend.clone();
let runtime_code_backend = sp_state_machine::backend::BackendRuntimeCode::new(&backend);
let proving_backend =
TrieBackendBuilder::wrap(&backend).with_recorder(Default::default()).build();
let runtime_code = runtime_code_backend.runtime_code()?;
let pre_root = *backend.root();
let encoded_results = StateMachine::new(
&proving_backend,
&mut changes,
executor,
method,
data,
&mut extensions,
&runtime_code,
CallContext::Offchain,
)
.execute()
.map_err(|e| format!("failed to execute {}: {}", method, e))
.map_err::<sc_cli::Error, _>(Into::into)?;
let proof = proving_backend
.extract_proof()
.expect("A recorder was set and thus, a storage proof can be extracted; qed");
if let Some(path) = maybe_export_proof {
let mut file = std::fs::File::create(&path).map_err(|e| {
log::error!(
target: LOG_TARGET,
"Failed to create file {}: {:?}",
path.to_string_lossy(),
e
);
e
})?;
log::info!(target: LOG_TARGET, "Writing storage proof to {}", path.to_string_lossy());
use std::io::Write as _;
file.write_all(storage_proof_to_raw_json(&proof).as_bytes()).map_err(|e| {
log::error!(
target: LOG_TARGET,
"Failed to write storage proof to {}: {:?}",
path.to_string_lossy(),
e
);
e
})?;
}
let proof_size = proof.encoded_size();
let compact_proof = proof
.clone()
.into_compact_proof::<HashingFor<Block>>(pre_root)
.map_err(|e| {
log::error!(target: LOG_TARGET, "failed to generate compact proof {}: {:?}", method, e);
e
})
.unwrap_or(CompactProof { encoded_nodes: Default::default() });
let compact_proof_size = compact_proof.encoded_size();
let compressed_proof = zstd::stream::encode_all(&compact_proof.encode()[..], 0)
.map_err(|e| {
log::error!(
target: LOG_TARGET,
"failed to generate compressed proof {}: {:?}",
method,
e
);
e
})
.unwrap_or_default();
let proof_nodes = proof.into_nodes();
let humanize = |s| {
if s < 1024 * 1024 {
format!("{:.2} KB ({} bytes)", s as f64 / 1024f64, s)
} else {
format!(
"{:.2} MB ({} KB) ({} bytes)",
s as f64 / (1024f64 * 1024f64),
s as f64 / 1024f64,
s
)
}
};
log::debug!(
target: LOG_TARGET,
"proof: 0x{}... / {} nodes",
HexDisplay::from(&proof_nodes.iter().flatten().cloned().take(10).collect::<Vec<_>>()),
proof_nodes.len()
);
log::debug!(target: LOG_TARGET, "proof size: {}", humanize(proof_size));
log::debug!(target: LOG_TARGET, "compact proof size: {}", humanize(compact_proof_size),);
log::debug!(
target: LOG_TARGET,
"zstd-compressed compact proof {}",
humanize(compressed_proof.len()),
);
log::debug!(target: LOG_TARGET, "{} executed without errors.", method);
Ok((changes, encoded_results))
}
pub(crate) fn rpc_err_handler(error: impl Debug) -> &'static str {
log::error!(target: LOG_TARGET, "rpc error: {:?}", error);
"rpc error."
}
/// Converts a [`sp_state_machine::StorageProof`] into a JSON string.
fn storage_proof_to_raw_json(storage_proof: &sp_state_machine::StorageProof) -> String {
serde_json::Value::Object(
storage_proof
.to_memory_db::<sp_runtime::traits::BlakeTwo256>()
.drain()
.iter()
.map(|(key, (value, _n))| {
(
format!("0x{}", hex::encode(key.as_bytes())),
serde_json::Value::String(format!("0x{}", hex::encode(value))),
)
})
.collect(),
)
.to_string()
}
@@ -1,53 +0,0 @@
// 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.
//! Utils for parsing user input
use sp_version::StateVersion;
pub(crate) fn hash(block_hash: &str) -> Result<String, String> {
let (block_hash, offset) = if let Some(block_hash) = block_hash.strip_prefix("0x") {
(block_hash, 2)
} else {
(block_hash, 0)
};
if let Some(pos) = block_hash.chars().position(|c| !c.is_ascii_hexdigit()) {
Err(format!(
"Expected block hash, found illegal hex character at position: {}",
offset + pos,
))
} else {
Ok(block_hash.into())
}
}
pub(crate) fn url(s: &str) -> Result<String, &'static str> {
if s.starts_with("ws://") || s.starts_with("wss://") {
// could use Url crate as well, but lets keep it simple for now.
Ok(s.to_string())
} else {
Err("not a valid WS(S) url: must start with 'ws://' or 'wss://'")
}
}
pub(crate) fn state_version(s: &str) -> Result<StateVersion, &'static str> {
s.parse::<u8>()
.map_err(|_| ())
.and_then(StateVersion::try_from)
.map_err(|_| "Invalid state version.")
}
-5
View File
@@ -37,11 +37,6 @@ pub enum Subcommand {
/// The pallet benchmarking moved to the `pallet` sub-command.
#[command(subcommand)]
Benchmark(frame_benchmarking_cli::BenchmarkCmd),
/// Try-runtime has migrated to a standalone
/// [CLI](<https://github.com/paritytech/try-runtime-cli>). The subcommand exists as a stub and
/// deprecation notice. It will be removed entirely some time after January 2024.
TryRuntime,
}
const AFTER_HELP_EXAMPLE: &str = color_print::cstr!(
-1
View File
@@ -216,7 +216,6 @@ pub fn run() -> Result<()> {
_ => Err("Benchmarking sub-command unsupported".into()),
}
},
Some(Subcommand::TryRuntime) => Err("The `try-runtime` subcommand has been migrated to a standalone CLI (https://github.com/paritytech/try-runtime-cli). It is no longer being maintained here and will be removed entirely some time after January 2024. Please remove this subcommand from your runtime and use the standalone CLI.".into()),
None => {
let runner = cli.create_runner(&cli.run.normalize())?;
let collator_options = cli.run.collator_options();
-4
View File
@@ -64,9 +64,6 @@ frame-benchmarking-cli = { path = "../../../substrate/utils/frame/benchmarking-c
# Local Dependencies
solochain-template-runtime = { path = "../runtime" }
# CLI-specific dependencies
try-runtime-cli = { path = "../../../substrate/utils/frame/try-runtime/cli", optional = true }
[build-dependencies]
substrate-build-script-utils = { path = "../../../substrate/utils/build-script-utils" }
@@ -87,5 +84,4 @@ try-runtime = [
"pallet-transaction-payment/try-runtime",
"solochain-template-runtime/try-runtime",
"sp-runtime/try-runtime",
"try-runtime-cli/try-runtime",
]
-5
View File
@@ -41,11 +41,6 @@ pub enum Subcommand {
#[command(subcommand)]
Benchmark(frame_benchmarking_cli::BenchmarkCmd),
/// Try-runtime has migrated to a standalone CLI
/// (<https://github.com/paritytech/try-runtime-cli>). The subcommand exists as a stub and
/// deprecation notice. It will be removed entirely some time after January 2024.
TryRuntime,
/// Db meta columns information.
ChainInfo(sc_cli::ChainInfoCmd),
}
-6
View File
@@ -170,12 +170,6 @@ pub fn run() -> sc_cli::Result<()> {
}
})
},
#[cfg(feature = "try-runtime")]
Some(Subcommand::TryRuntime) => Err(try_runtime_cli::DEPRECATION_NOTICE.into()),
#[cfg(not(feature = "try-runtime"))]
Some(Subcommand::TryRuntime) => Err("TryRuntime wasn't enabled when building the node. \
You can enable it with `--features try-runtime`."
.into()),
Some(Subcommand::ChainInfo(cmd)) => {
let runner = cli.create_runner(cmd)?;
runner.sync_run(|config| cmd.run::<Block>(&config))