try_runtime_core/commands/
execute_block.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
18use std::{fmt::Debug, str::FromStr};
19
20use parity_scale_codec::Encode;
21use sc_executor::sp_wasm_interface::HostFunctions;
22use sp_runtime::{
23    generic::SignedBlock,
24    traits::{Block as BlockT, Header as HeaderT, NumberFor},
25};
26use substrate_rpc_client::{ws_client, ChainApi};
27
28use crate::{
29    common::state::{
30        build_executor, state_machine_call_with_proof, LiveState, RuntimeChecks, State,
31    },
32    full_extensions, rpc_err_handler, SharedParams, LOG_TARGET,
33};
34
35/// Configurations for [`run`].
36///
37/// This will always call into `TryRuntime_execute_block`, which can optionally skip the state-root
38/// check (useful for trying a unreleased runtime), and can execute runtime sanity checks as well.
39#[derive(Debug, Clone, clap::Parser)]
40pub struct Command {
41    /// Which try-state targets to execute when running this command.
42    ///
43    /// Expected values:
44    /// - `all`
45    /// - `none`
46    /// - A comma separated list of pallets, as per pallet names in `construct_runtime!()` (e.g.
47    ///   `Staking, System`).
48    /// - `rr-[x]` where `[x]` is a number. Then, the given number of pallets are checked in a
49    ///   round-robin fashion.
50    #[arg(long, default_value = "all")]
51    pub try_state: frame_try_runtime::TryStateSelect,
52
53    /// The ws uri from which to fetch the block.
54    ///
55    /// This will always fetch the next block of whatever `state` is referring to, because this is
56    /// the only sensible combination. In other words, if you have the state of block `n`, you
57    /// should execute block `n+1` on top of it.
58    ///
59    /// If `state` is `Live`, this can be ignored and the same uri is used for both.
60    #[arg(
61		long,
62		value_parser = crate::common::parse::url
63	)]
64    pub block_ws_uri: Option<String>,
65
66    /// The state type to use.
67    #[command(subcommand)]
68    pub state: State,
69}
70
71impl Command {
72    fn block_ws_uri(&self) -> String {
73        match (&self.block_ws_uri, &self.state) {
74            (Some(block_ws_uri), State::Snap { .. }) => block_ws_uri.to_owned(),
75            (Some(block_ws_uri), State::Live { .. }) => {
76                log::error!(target: LOG_TARGET, "--block-uri is provided while state type is live, Are you sure you know what you are doing?");
77                block_ws_uri.to_owned()
78            }
79            (None, State::Live(LiveState { uri, .. })) => uri.clone(),
80            (None, State::Snap { .. }) => {
81                panic!("either `--block-uri` must be provided, or state must be `live`");
82            }
83        }
84    }
85}
86
87// Runs the `execute_block` command.
88pub async fn run<Block, HostFns>(shared: SharedParams, command: Command) -> sc_cli::Result<()>
89where
90    Block: BlockT + serde::de::DeserializeOwned,
91    <Block::Hash as FromStr>::Err: Debug,
92    Block::Hash: serde::de::DeserializeOwned,
93    Block::Header: serde::de::DeserializeOwned,
94    <NumberFor<Block> as TryInto<u64>>::Error: Debug,
95    HostFns: HostFunctions,
96{
97    let executor = build_executor::<HostFns>(&shared);
98    let block_ws_uri = command.block_ws_uri();
99    let rpc = ws_client(&block_ws_uri).await?;
100
101    let live_state = match command.state {
102        State::Live(live_state) => {
103            // If no --at is provided, get the latest block to replay
104            if live_state.at.is_some() {
105                live_state
106            } else {
107                let header =
108                    ChainApi::<(), Block::Hash, Block::Header, SignedBlock<Block>>::header(
109                        &rpc, None,
110                    )
111                    .await
112                    .map_err(rpc_err_handler)?
113                    .expect("header exists, block should also exist; qed");
114                LiveState {
115                    uri: block_ws_uri,
116                    at: Some(hex::encode(header.hash().encode())),
117                    pallet: Default::default(),
118                    hashed_prefixes: Default::default(),
119                    child_tree: Default::default(),
120                }
121            }
122        }
123        _ => {
124            unreachable!("execute block currently only supports Live state")
125        }
126    };
127
128    // The block we want to *execute* at is the block passed by the user
129    let execute_at = live_state.at::<Block>()?;
130
131    let prev_block_live_state = live_state.to_prev_block_live_state::<Block>().await?;
132
133    // Get state for the prev block
134    let runtime_checks = RuntimeChecks {
135        name_matches: !shared.disable_spec_name_check,
136        version_increases: false,
137        try_runtime_feature_enabled: true,
138    };
139    let ext = State::Live(prev_block_live_state)
140        .to_ext::<Block, HostFns>(&shared, &executor, None, runtime_checks)
141        .await?;
142
143    // Execute the desired block on top of it
144    let block =
145        ChainApi::<(), Block::Hash, Block::Header, SignedBlock<Block>>::block(&rpc, execute_at)
146            .await
147            .map_err(rpc_err_handler)?
148            .expect("header exists, block should also exist; qed")
149            .block;
150
151    // A digest item gets added when the runtime is processing the block, so we need to pop
152    // the last one to be consistent with what a gossiped block would contain.
153    let (mut header, extrinsics) = block.deconstruct();
154    header.digest_mut().pop();
155    let block = Block::new(header, extrinsics);
156
157    // for now, hardcoded for the sake of simplicity. We might customize them one day.
158    let state_root_check = false;
159    let signature_check = false;
160    let payload = (
161        block.clone(),
162        state_root_check,
163        signature_check,
164        command.try_state,
165    )
166        .encode();
167
168    let _ = state_machine_call_with_proof::<Block, HostFns>(
169        &ext,
170        &mut Default::default(),
171        &executor,
172        "TryRuntime_execute_block",
173        &payload,
174        full_extensions(executor.clone()),
175        shared.export_proof,
176    )?;
177
178    Ok(())
179}