mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-12 08:51:09 +00:00
Remove deprecated TryRuntime subcommand (#4017)
Completes the removal of `try-runtime-cli` logic from `polkadot-sdk`.
This commit is contained in:
Generated
-44
@@ -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"
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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!(
|
||||
|
||||
@@ -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())?;
|
||||
|
||||
@@ -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"]
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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))?)
|
||||
|
||||
@@ -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
|
||||
@@ -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]]
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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.")
|
||||
}
|
||||
@@ -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!(
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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",
|
||||
]
|
||||
|
||||
@@ -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),
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
|
||||
Reference in New Issue
Block a user