Check runtime
The try-runtime
command-line tool enables you to query a snapshot of runtime storage using an in-memory-externalities data structure to store state.
By using the in-memory storage, you can write tests for a specified runtime state so that you can test against real chain state before going to production.
In its simplest form, you can use try-runtime
to test the runtime state by doing the following:
- Connect to a remote node.
- Call a specific runtime API.
- Retrieve state from the node at a specific block.
- Write tests for the data retrieved.
Motivation
The initial motivation for try-runtime
came from the need to test runtime changes against state from a real chain.
Prior TestExternalities
and BasicExternalities
existed
for writing unit and integrated tests with mock data, but lacked the ability to test against a chain's actual state.
The try-runtime
tool extends TestExternalities
and BasicExternalities
by retrieving state using the following RPC endpoints for the node:
rpc_get_storage
rpc_get_keys_paged
(see remote externalities lib
for more details;)
After using the key-value database to retrieve state, try-runtime inserts the data into TestExternalities
.
How it works
The try-runtime
tool has its own implementation of externalities called remote_externalities
which is just a wrapper around TestExternalities
that uses a generic key-value store where data is type encoded.
The diagram below illustrates the way externalities sits outside a compiled runtime as a means to capture the storage of that runtime.
With remote_externalities
, you can capture some chain state and run tests on it. Essentially, RemoteExternalities
will populate a TestExternalities
with a real chain's data.
To query state, try-runtime
uses the RPC methods provided by StateApiClient
.
In particular:
storage
This method returns the storage value for the key that represents the block you specify.storage_key_paged
This method returns the keys that match a prefix you specify with pagination support.
Usage
The most common use case for try-runtime
is to help you prepare for storage migration and runtime upgrades.
Because the RPC calls that query storage are computationally expensive, there are a number of command-line options you should set for a running node before you use the try-runtime
command. To prepare a node for try-runtime
testing, set the following options:
- Set
--rpc-max-payload 1000
to ensure large RPC queries can work. - Set
--rpc-cors all
to ensure WebSocket connections can come through.
You can combine try-runtime
with fork-off-substrate
to test your chain before production.
Use try-runtime
to test your chain's migration and its pre and post states.
Then, use fork-off-substrate
if you want to check that block production continues after the migration.
Runtime upgrade hooks
By default, runtime upgrade hooks—which can be defined inside of the runtime or inside pallets—specify what should happen when there's been a runtime upgrade.
That is, the default on_runtime_upgrade
method only describes runtime state after the upgrade.
However, it is possible to use methods provided by try-runtime
to inspect and compare the runtime state before and after a runtime upgrade for testing purposes.
If you enable the try-runtime
feature for the runtime, you can define pre-upgrade
and post-upgrade
hooks for the runtime as follows:
#[cfg(feature = "try-runtime")]
fn pre_upgrade() -> Result<Vec<u8>, &'static str> {
Ok(Vec::new())
}
#[cfg(feature = "try-runtime")]
fn post_upgrade(_state: Vec<u8>) -> Result<(), &'static str> {
Ok(())
}
With these function, you can use the pre_upgrade
hook to retrieve the runtime state and return it as a Vecpost_upgrade
hook.
Command-line examples
To use try-runtime
from the command line, run your node with the --features=try-runtime
flag.
For example:
cargo run --release --features=try-runtime try-runtime
You can use the following subcommands with try-runtime
:
on-runtime-upgrade
: ExecutestryRuntime_on_runtime_upgrade
against the given runtime state.offchain-worker
: ExecutesoffchainWorkerApi_offchain_worker
against the given runtime state.execute-block
: Executescore_execute_block
using the given block and the runtime state of the parent block.follow-chain
: Follows a given chain's finalized blocks and applies to all its extrinsics. This allows the behavior of a new runtime to be inspected over a long period of time, with real transactions coming as input.
To view usage information for a specific try-runtime
subcommand, specify the subcommand and the --help
flag.
For example, to see usage information for try-runtime on-runtime-upgrade
, you can run the following command:
cargo run --release --features=try-runtime try-runtime on-runtime-upgrade --help
For example, you can run try-runtime
with the on-runtime-upgrade
subcommand for a chain running locally with a command like this:
cargo run --release --features=try-runtime try-runtime on-runtime-upgrade live ws://localhost:9944
You can use try-runtime
to re-execute code from the ElectionProviderMultiPhase
offchain worker on localhost:9944
with a command like this:
cargo run -- --release \
--features=try-runtime \
try-runtime \
--execution Wasm \
--wasm-execution Compiled \
offchain-worker \
--header-at 0x491d09f313c707b5096650d76600f063b09835fd820e2916d3f8b0f5b45bec30 \
live \
-b 0x491d09f313c707b5096650d76600f063b09835fd820e2916d3f8b0f5b45bec30 \
-m ElectionProviderMultiPhase \
--uri wss://localhost:9944
You can run the migrations of the local runtime on the state of SomeChain with a command like this:
RUST_LOG=runtime=trace,try-runtime::cli=trace,executor=trace \
cargo run try-runtime \
--execution Native \
--chain somechain-dev \
on-runtime-upgrade \
live \
--uri wss://rpc.polkadot.io
You can run try-runtime against the state for a specific block number with a command like this:
RUST_LOG=runtime=trace,try-runtime::cli=trace,executor=trace \
cargo run try-runtime \
--execution Native \
--chain dev \
--no-spec-name-check \
on-runtime-upgrade \
live \
--uri wss://rpc.polkadot.io \
--at <block-hash>
Notice that this command requires the --no-spec-name-check
command-line option.