try_runtime/main.rs
1// This file is part of try-runtime-cli.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: Apache-2.0
5
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18//! # Try-runtime
19//!
20//! Substrate's programmatic testing framework.
21//!
22//! > As the name suggests, `try-runtime` is a detailed testing framework that gives you a lot of
23//! > control over what is being executed in which environment. It is recommended that user's first
24//! > familiarize themselves with substrate in depth, particularly the execution model. It is
25//! > critical
26//! > to deeply understand how the wasm/client/runtime interactions, and the runtime apis work in
27//! > the
28//! > substrate runtime, before commencing to working with `try-runtime`.
29//!
30//! #### Resources
31//!
32//! Some resources about the above:
33//!
34//! 1. <https://www.crowdcast.io/e/substrate-seminar/41>
35//! 2. <https://docs.substrate.io/fundamentals/runtime-development/>
36//! 3. <https://www.youtube.com/watch?v=a_u3KMG-n-I>
37//!
38//! ---
39//!
40//! ## Background Knowledge
41//!
42//! The basis of all try-runtime commands is the same: connect to a live node, scrape its *state*
43//! and put it inside a [`TestExternalities`], then call into a *specific runtime-api* using the
44//! given state and some *runtime*.
45//!
46//! Alternatively, the state could come from a snapshot file.
47//!
48//! All of the variables in the above statement are made *italic*. Let's look at each of them:
49//!
50//! 1. **State** is the key-value pairs of data that comprise the canonical information that any
51//! blockchain is keeping. A state can be full (all key-value pairs), or be partial (only pairs
52//! related to some pallets/prefixes). Moreover, some keys are special and are not related to
53//! specific pallets, known as [`well_known_keys`] in substrate. The most important of these is
54//! the `:CODE:` key, which contains the code used for execution, when wasm execution is chosen.
55//!
56//! 2. *A runtime-api* call is a call into a function defined in the runtime, *on top of a given
57//! state*. Each subcommand of `try-runtime` utilizes a specific *runtime-api*.
58//!
59//! 3. Finally, the **runtime** is the actual code that is used to execute the aforementioned
60//! runtime-api. Everything in this crate assumes wasm execution, which means the runtime that
61//! you use is the one stored onchain, namely under the `:CODE:` key.
62//!
63//! To recap, a typical try-runtime command does the following:
64//!
65//! 1. Download the state of a live chain, and write to an `externalities`.
66//! 2. Overwrite the `:CODE:` with a given wasm blob
67//! 3. Test some functionality via calling a runtime-api.
68//!
69//! ## Installation
70
71//!```bash
72//! # Install latest version (recommended for local development)
73//! cargo install --git https://github.com/paritytech/try-runtime-cli --locked
74//! # Install a specific version (recommended for tools like CI)
75//! cargo install --git https://github.com/paritytech/try-runtime-cli --tag vX.Y.Z --locked
76//! try-runtime --help
77//! try-runtime on-runtime-upgrade --help
78//! ```
79//!
80//! ## Usage
81//!
82//! To use any of the provided commands, [`SharedParams`] must be provided. The most important of
83//! which being [`SharedParams::runtime`], which specifies which runtime to use. Furthermore,
84//! [`SharedParams::overwrite_state_version`] can be used to alter the state-version (see
85//! <https://forum.polkadot.network/t/state-trie-migration/852> for more info).
86//!
87//! Then, the specific command has to be specified. See [`Action`] for more information about each
88//! command's specific customization flags, and assumptions regarding the runtime being used.
89//!
90//! Briefly, this CLI is capable of executing:
91//!
92//! * [`Action::OnRuntimeUpgrade`]: execute all the [`OnRuntimeUpgrade`] hooks.
93//! * [`Action::ExecuteBlock`]: re-execute the given block.
94//! * [`Action::FastForward`]: execute [`OnRuntimeUpgrade`] hooks, then fast-forward the chain a
95//! given number of blocks while checking try-state invarients.
96//! * [`Action::OffchainWorker`]: re-execute the given block's offchain worker code path.
97//! * [`Action::FollowChain`]: continuously execute the blocks of a remote chain on top of a given
98//! runtime.
99//! * [`Action::CreateSnapshot`]: Create a snapshot file from a remote node.
100//!
101//! Finally, to make sure there are no errors regarding this, always run any `try-runtime` command
102//! with `executor=trace` logging targets, which will specify which runtime is being used per api
103//! call. Moreover, `remote-ext`, `try-runtime` and `runtime` logs targets will also be useful.
104//!
105//! ## Spec name check
106//!
107//! A common pitfall is that you might be running some test on top of the state of chain `x`, with
108//! the runtime of chain `y`. To avoid this all commands do a spec-name check before executing
109//! anything by default. This will check the, if any alterations are being made to the `:CODE:`,
110//! then the spec names match. The spec versions are warned, but are not mandated to match.
111//!
112//! > If anything, in most cases, we expect spec-versions to NOT match, because try-runtime is all
113//! > about testing unreleased runtimes.
114//!
115//! ## Note on signature and state-root checks
116//!
117//! All of the commands calling into `TryRuntime_execute_block` ([`Action::ExecuteBlock`] and
118//! [`Action::FollowChain`]) disable both state root and signature checks. This is because in 99%
119//! of the cases, the runtime that is being tested is different from the one that is stored in the
120//! canonical chain state. This implies:
121//!
122//! 1. the state root will NEVER match, because `:CODE:` is different between the two.
123//! 2. replaying all transactions will fail, because the spec-version is part of the transaction
124//! signature.
125//!
126//! ## Best Practices
127//!
128//! Try-runtime is all about battle-testing unreleased runtimes. The following list of suggestions
129//! help developers maximize their testing coverage and make the best use of `try-runtime` features.
130//!
131//! ### Testing Runtime Upgrades
132//!
133//! One of the most powerful abilities of `try-runtime` is using the
134//! [`OnRuntimeUpgrade::pre_upgrade`] and [`OnRuntimeUpgrade::post_upgrade`] hooks to test runtime
135//! upgrades implemented with [`OnRuntimeUpgrade`]. [`OnRuntimeUpgrade`] can be implemented inside
136//! the pallet, or standalone in a runtime to define a migration to execute next runtime upgrade. In
137//! both cases, these methods can be added:
138//!
139//! ```ignore
140//! #[cfg(feature = "try-runtime")]
141//! fn pre_upgrade() -> Result<Vec<u8>, TryRuntimeError> {}
142//!
143//! #[cfg(feature = "try-runtime")]
144//! fn post_upgrade(state: Vec<u8>) -> Result<(), TryRuntimeError> {}
145//! ```
146//!
147//! (The pallet macro syntax will support this simply as a part of `#[pallet::hooks]`).
148//!
149//! These hooks will be called when you execute the [`Action::OnRuntimeUpgrade`] command, before and
150//! after the migration. [`OnRuntimeUpgrade::pre_upgrade`] returns a [`Vec<u8>`] that can contain
151//! arbitrary encoded data (usually some pre-upgrade state) which will be passed to
152//! [`OnRuntimeUpgrade::pre_upgrade`] after upgrading and used for post checking.
153//!
154//! ### [`VersionedMigration`]
155//!
156//! It is strongly suggested to use [`VersionedMigration`] when writing custom migrations for
157//! pallets.
158//!
159//! ### State Consistency
160//!
161//! Similarly, each pallet can expose a function in `#[pallet::hooks]` section as follows:
162//!
163//! ```ignore
164//! #[cfg(feature = "try-runtime")]
165//! fn try_state(_: BlockNumber) -> Result<(), TryRuntimeError> {}
166//! ```
167//!
168//! which is called on numerous code paths in the try-runtime tool. These checks should ensure that
169//! the state of the pallet is consistent and correct. See [`TryState`] for more info.
170//!
171//! ### Logging
172//!
173//! It is super helpful to make sure your migration code uses logging (always with a `runtime` log
174//! target prefix, e.g. `runtime::balance`) and state exactly at which stage it is, and what it is
175//! doing.
176//!
177//! ## Examples
178//!
179//! For the following examples, we assume the existence of the following:
180//!
181//! 1. a substrate node compiled with `--features try-runtime`, called `substrate`. This will be the
182//! running node that you connect to, and provide a wasm blob that has try-runtime functionality
183//! enabled.
184//! 2. the `try-runtime` CLI binary on your path.
185//!
186//! ```bash
187//! # this is like your running deployed node.
188//! cargo build --features try-runtime --release && cp target/release/substrate .
189//! ```
190//!
191//! > The above example is with `substrate`'s `kitchensink-runtime`, but is applicable to any
192//! > substrate-based chain.
193//!
194//! * Run the migrations of a given runtime on top of a live state.
195//!
196//! ```bash
197//! # assuming there's `./substrate --dev --tmp --ws-port 9999` or similar running.
198//! try-runtime \
199//! --runtime /path-to-substrate/target/release/wbuild/my-runtime.wasm \
200//! on-runtime-upgrade \
201//! live --uri ws://localhost:9999
202//! ```
203//!
204//! * Same as the previous example, but run it at specific block number's state and using the live
205//! polkadot network. This means that this block hash's state should not yet have been pruned by
206//! the node running at `rpc.polkadot.io`.
207//!
208//! ```bash
209//! try-runtime \
210//! --runtime /path-to-polkadot-runtimes/target/release/wbuild/polkadot-runtime/polkadot-runtime.wasm \
211//! on-runtime-upgrade \
212//! live --uri wss://rpc.polkadot.io:443 \
213//! # replace with your desired block hash!
214//! --at 0xa1b16c1efd889a9f17375ec4dd5c1b4351a2be17fa069564fced10d23b9b3836
215//! ```
216//!
217//! * Now, let's use a snapshot file. First, we create the snapshot:
218//!
219//! ```bash
220//! try-runtime --runtime existing create-snapshot --uri ws://localhost:9999 my-snapshot.snap
221//! 2022-12-13 10:28:17.516 INFO main remote-ext: since no at is provided, setting it to latest finalized head, 0xe7d0b614dfe89af65b33577aae46a6f958c974bf52f8a5e865a0f4faeb578d22
222//! 2022-12-13 10:28:17.516 INFO main remote-ext: since no prefix is filtered, the data for all pallets will be downloaded
223//! 2022-12-13 10:28:17.550 INFO main remote-ext: writing snapshot of 1611464 bytes to "node-268@latest.snap"
224//! 2022-12-13 10:28:17.551 INFO main remote-ext: initialized state externalities with storage root 0x925e4e95de4c08474fb7f976c4472fa9b8a1091619cd7820a793bf796ee6d932 and state_version V1
225//! ```
226//!
227//! > Note that the snapshot contains the `existing` runtime, which does not have the correct
228//! > `try-runtime` feature. In the following commands, we still need to overwrite the runtime.
229//!
230//! Then, we can use it to have the same command as before, `on-runtime-upgrade`
231//!
232//! ```bash
233//! try-runtime \
234//! --runtime /path-to-substrate/target/release/wbuild/my-runtime.wasm \
235//! on-runtime-upgrade \
236//! snap -p my-snapshot.snap
237//! ```
238//!
239//! * Execute the latest finalized block with the given runtime.
240//!
241//! ```bash
242//! try-runtime \
243//! --runtime /path-to-substrate/target/release/wbuild/my-runtime.wasm \
244//! execute-block live \
245//! --uri ws://localhost:9999
246//! ```
247//!
248//! This can still be customized at a given block with `--at`. If you want to use a snapshot, you
249//! can still use `--block-ws-uri` to provide a node form which the block data can be fetched.
250//!
251//! Moreover, this runs the [`TryState`] hooks as well. The hooks to run can be customized with the
252//! `--try-state`. For example:
253//!
254//! ```bash
255//! try-runtime \
256//! --runtime /path-to-substrate/target/release/wbuild/my-runtime.wasm \
257//! execute-block \
258//! --try-state System,Staking \
259//! live \
260//! --uri ws://localhost:9999 \
261//! --pallet System Staking
262//! ```
263//!
264//! Will only run the `try-state` of the two given pallets. When running `try-state` against
265//! some real chain data it can take a long time for the command to execute since it has to
266//! query all the key-value pairs. In scenarios like above where we only want to run the
267//! `try-state` for some specific pallets, we can use the `--pallet` option to specify from
268//! which pallets we want to query the state. This will greatly decrease the execution time.
269//!
270//! See [`TryStateSelect`] for more information.
271//!
272//! * Follow our live chain's blocks using `follow-chain`, whilst running the try-state of 3 pallets
273//! in a round robin fashion
274//!
275//! ```bash
276//! try-runtime \
277//! --runtime /path-to-substrate/target/release/wbuild/my-runtime.wasm \
278//! follow-chain \
279//! --uri ws://localhost:9999 \
280//! --try-state rr-3
281//! ```
282//!
283//! [`VersionedMigration`]: frame_support::migrations::VersionedMigration
284//! [`OnRuntimeUpgrade`]: frame_support::traits::OnRuntimeUpgrade
285//! [`OnRuntimeUpgrade::pre_upgrade`]: frame_support::traits::OnRuntimeUpgrade::pre_upgrade
286//! [`OnRuntimeUpgrade::post_upgrade`]: frame_support::traits::OnRuntimeUpgrade::post_upgrade
287//! [`TryStateSelect`]: frame_support::traits::TryStateSelect
288//! [`TryState`]: frame_support::traits::TryState
289//! [`TestExternalities`]: sp_state_machine::TestExternalities
290//! [`well_known_keys`]: sp_storage::well_known_keys
291//! [`Action`]: try_runtime_core::commands::Action
292//! [`Action::FollowChain`]: try_runtime_core::commands::Action::FollowChain
293//! [`Action::OnRuntimeUpgrade`]: try_runtime_core::commands::Action::OnRuntimeUpgrade
294//! [`Action::ExecuteBlock`]: try_runtime_core::commands::Action::ExecuteBlock
295//! [`Action::OffchainWorker`]: try_runtime_core::commands::Action::OffchainWorker
296//! [`Action::CreateSnapshot`]: try_runtime_core::commands::Action::CreateSnapshot
297//! [`Action::FastForward`]: try_runtime_core::commands::Action::FastForward
298//! [`SharedParams`]: try_runtime_core::shared_parameters::SharedParams
299//! [`SharedParams::runtime`]: try_runtime_core::common::shared_parameters::SharedParams::runtime
300//! [`SharedParams::overwrite_state_version`]: try_runtime_core::common::shared_parameters::SharedParams::overwrite_state_version
301
302use std::env;
303
304use clap::Parser;
305use sp_runtime::{
306 generic::{Block, Header},
307 traits::BlakeTwo256,
308 OpaqueExtrinsic,
309};
310use try_runtime_core::commands::TryRuntime;
311
312fn init_env() {
313 if env::var(env_logger::DEFAULT_FILTER_ENV).is_err() {
314 env::set_var(env_logger::DEFAULT_FILTER_ENV, "info");
315 }
316 env_logger::init();
317}
318
319#[tokio::main]
320async fn main() {
321 init_env();
322
323 let cmd = TryRuntime::parse();
324 cmd.run::<Block<Header<u32, BlakeTwo256>, OpaqueExtrinsic>, sp_io::SubstrateHostFunctions>()
325 .await
326 .unwrap();
327}