From 15bd9e6fa7da77257beab32a2f9cc142022fbbd7 Mon Sep 17 00:00:00 2001 From: Sergej Sakac <73715684+Szegoo@users.noreply.github.com> Date: Sat, 24 Jun 2023 11:36:15 +0200 Subject: [PATCH] try-runtime-cli: `execute-block` & `create-snapshot` tests (#14343) * execute-block test * test create-snapshot * oops * Update utils/frame/try-runtime/cli/tests/create_snapshot.rs Co-authored-by: Liam Aharon * Update utils/frame/try-runtime/cli/tests/create_snapshot.rs Co-authored-by: Liam Aharon * Update utils/frame/try-runtime/cli/tests/create_snapshot.rs Co-authored-by: Liam Aharon * remove snapshot * execute block: new log * use prefix & make tempfile a dev dependencie * Update utils/frame/try-runtime/cli/tests/execute_block.rs Co-authored-by: Liam Aharon * Update utils/frame/try-runtime/cli/tests/create_snapshot.rs Co-authored-by: Liam Aharon * ".git/.scripts/commands/fmt/fmt.sh" * --at option in execute-block test * fixes & use --at option in create-snapshot test * hmm * fmt * remove nonsense * Update utils/frame/try-runtime/cli/tests/create_snapshot.rs Co-authored-by: Oliver Tale-Yazdi * Update utils/frame/try-runtime/cli/tests/execute_block.rs Co-authored-by: Oliver Tale-Yazdi * remove unnecessary test modules * try to load snapshot file * fix --------- Co-authored-by: Liam Aharon Co-authored-by: command-bot <> Co-authored-by: Oliver Tale-Yazdi --- substrate/Cargo.lock | 3 + substrate/frame/executive/src/lib.rs | 6 ++ substrate/test-utils/cli/Cargo.toml | 1 + substrate/test-utils/cli/src/lib.rs | 23 ++++- .../utils/frame/try-runtime/cli/Cargo.toml | 2 + .../try-runtime/cli/tests/create_snapshot.rs | 95 +++++++++++++++++++ .../try-runtime/cli/tests/execute_block.rs | 70 ++++++++++++++ .../try-runtime/cli/tests/follow_chain.rs | 76 ++++++++------- 8 files changed, 236 insertions(+), 40 deletions(-) create mode 100644 substrate/utils/frame/try-runtime/cli/tests/create_snapshot.rs create mode 100644 substrate/utils/frame/try-runtime/cli/tests/execute_block.rs diff --git a/substrate/Cargo.lock b/substrate/Cargo.lock index 07091e4dd0..e8d374c15d 100644 --- a/substrate/Cargo.lock +++ b/substrate/Cargo.lock @@ -11944,6 +11944,7 @@ dependencies = [ "nix 0.26.2", "node-primitives", "regex", + "sp-rpc", "substrate-rpc-client", "tempfile", "tokio", @@ -12880,6 +12881,7 @@ dependencies = [ "frame-try-runtime", "hex", "log", + "node-primitives", "parity-scale-codec", "regex", "sc-cli", @@ -12905,6 +12907,7 @@ dependencies = [ "sp-weights", "substrate-cli-test-utils", "substrate-rpc-client", + "tempfile", "tokio", "zstd 0.12.3+zstd.1.5.2", ] diff --git a/substrate/frame/executive/src/lib.rs b/substrate/frame/executive/src/lib.rs index 31cbb0ee7b..2aed177578 100644 --- a/substrate/frame/executive/src/lib.rs +++ b/substrate/frame/executive/src/lib.rs @@ -332,6 +332,12 @@ where ); } + frame_support::log::info!( + target: LOG_TARGET, + "try-runtime: Block #{:?} successfully executed", + header.number(), + ); + Ok(frame_system::Pallet::::block_weight().total()) } diff --git a/substrate/test-utils/cli/Cargo.toml b/substrate/test-utils/cli/Cargo.toml index cc05884a6e..c05ffa902e 100644 --- a/substrate/test-utils/cli/Cargo.toml +++ b/substrate/test-utils/cli/Cargo.toml @@ -14,6 +14,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] substrate-rpc-client = { path = "../../utils/frame/rpc/client" } +sp-rpc = { version = "6.0.0", path = "../../primitives/rpc" } assert_cmd = "2.0.10" nix = "0.26.2" regex = "1.7.3" diff --git a/substrate/test-utils/cli/src/lib.rs b/substrate/test-utils/cli/src/lib.rs index 526bc1f377..b33c0e9b77 100644 --- a/substrate/test-utils/cli/src/lib.rs +++ b/substrate/test-utils/cli/src/lib.rs @@ -25,6 +25,7 @@ use nix::{ }; use node_primitives::{Hash, Header}; use regex::Regex; +use sp_rpc::{list::ListOrValue, number::NumberOrHex}; use std::{ env, io::{BufRead, BufReader, Read}, @@ -177,7 +178,8 @@ pub async fn wait_n_finalized_blocks(n: usize, url: &str) { use substrate_rpc_client::{ws_client, ChainApi}; let mut built_blocks = std::collections::HashSet::new(); - let mut interval = tokio::time::interval(Duration::from_secs(2)); + let block_duration = Duration::from_secs(2); + let mut interval = tokio::time::interval(block_duration); let rpc = ws_client(url).await.unwrap(); loop { @@ -220,6 +222,25 @@ pub async fn run_node_for_a_while(base_path: &Path, args: &[&str]) { .await } +pub async fn block_hash(block_number: u64, url: &str) -> Result { + use substrate_rpc_client::{ws_client, ChainApi}; + + let rpc = ws_client(url).await.unwrap(); + + let result = ChainApi::<(), Hash, Header, ()>::block_hash( + &rpc, + Some(ListOrValue::Value(NumberOrHex::Number(block_number))), + ) + .await + .map_err(|_| "Couldn't get block hash".to_string())?; + + match result { + ListOrValue::Value(maybe_block_hash) if maybe_block_hash.is_some() => + Ok(maybe_block_hash.unwrap()), + _ => Err("Couldn't get block hash".to_string()), + } +} + pub struct KillChildOnDrop(pub Child); impl KillChildOnDrop { diff --git a/substrate/utils/frame/try-runtime/cli/Cargo.toml b/substrate/utils/frame/try-runtime/cli/Cargo.toml index b048c716d8..ddcef6313c 100644 --- a/substrate/utils/frame/try-runtime/cli/Cargo.toml +++ b/substrate/utils/frame/try-runtime/cli/Cargo.toml @@ -46,8 +46,10 @@ zstd = { version = "0.12.3", 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.27.0" [features] diff --git a/substrate/utils/frame/try-runtime/cli/tests/create_snapshot.rs b/substrate/utils/frame/try-runtime/cli/tests/create_snapshot.rs new file mode 100644 index 0000000000..59a36fd702 --- /dev/null +++ b/substrate/utils/frame/try-runtime/cli/tests/create_snapshot.rs @@ -0,0 +1,95 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#![cfg(unix)] +#![cfg(feature = "try-runtime")] + +use assert_cmd::cargo::cargo_bin; +use node_primitives::Hash; +use regex::Regex; +use remote_externalities::{Builder, Mode, OfflineConfig, SnapshotConfig}; +use sp_runtime::testing::{Block as RawBlock, ExtrinsicWrapper}; +use std::{ + path::{Path, PathBuf}, + process, + time::Duration, +}; +use substrate_cli_test_utils as common; +use tokio::process::{Child, Command}; + +type Block = RawBlock>; + +#[tokio::test] +async fn create_snapshot_works() { + // Build substrate so binaries used in the test use the latest code. + common::build_substrate(&["--features=try-runtime"]); + + let temp_dir = tempfile::Builder::new() + .prefix("try-runtime-cli-test-dir") + .tempdir() + .expect("Failed to create a tempdir"); + let snap_file_path = temp_dir.path().join("snapshot.snap"); + + common::run_with_timeout(Duration::from_secs(60), async move { + fn create_snapshot(ws_url: &str, snap_file: &PathBuf, at: Hash) -> Child { + Command::new(cargo_bin("substrate")) + .stdout(process::Stdio::piped()) + .stderr(process::Stdio::piped()) + .args(&["try-runtime", "--runtime=existing"]) + .args(&["create-snapshot", format!("--uri={}", ws_url).as_str()]) + .arg(snap_file) + .args(&["--at", format!("{:?}", at).as_str()]) + .kill_on_drop(true) + .spawn() + .unwrap() + } + + // Start a node and wait for it to begin finalizing blocks + let mut node = common::KillChildOnDrop(common::start_node()); + let ws_url = common::extract_info_from_output(node.stderr.take().unwrap()).0.ws_url; + common::wait_n_finalized_blocks(3, &ws_url).await; + + let block_number = 2; + let block_hash = common::block_hash(block_number, &ws_url).await.unwrap(); + + // Try to create a snapshot. + let mut snapshot_creation = create_snapshot(&ws_url, &snap_file_path, block_hash); + + let re = Regex::new(r#".*writing snapshot of (\d+) bytes to .*"#).unwrap(); + let matched = + common::wait_for_stream_pattern_match(snapshot_creation.stderr.take().unwrap(), re) + .await; + + // Assert that the snapshot creation succeded. + assert!(matched.is_ok(), "Failed to create snapshot"); + + let snapshot_is_on_disk = Path::new(&snap_file_path).exists(); + assert!(snapshot_is_on_disk, "Snapshot was not written to disk"); + + // Try and load the snapshot we have created by running `create-snapshot`. + let snapshot_loading_result = Builder::::new() + .mode(Mode::Offline(OfflineConfig { + state_snapshot: SnapshotConfig { path: snap_file_path }, + })) + .build() + .await; + + assert!(snapshot_loading_result.is_ok(), "Snapshot couldn't be loaded"); + }) + .await; +} diff --git a/substrate/utils/frame/try-runtime/cli/tests/execute_block.rs b/substrate/utils/frame/try-runtime/cli/tests/execute_block.rs new file mode 100644 index 0000000000..025b753be7 --- /dev/null +++ b/substrate/utils/frame/try-runtime/cli/tests/execute_block.rs @@ -0,0 +1,70 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#![cfg(unix)] +#![cfg(feature = "try-runtime")] + +use assert_cmd::cargo::cargo_bin; +use node_primitives::Hash; +use regex::Regex; +use std::{process, time::Duration}; +use substrate_cli_test_utils as common; +use tokio::process::{Child, Command}; + +#[tokio::test] +async fn block_execution_works() { + // Build substrate so binaries used in the test use the latest code. + common::build_substrate(&["--features=try-runtime"]); + + common::run_with_timeout(Duration::from_secs(60), async move { + fn execute_block(ws_url: &str, at: Hash) -> Child { + Command::new(cargo_bin("substrate")) + .stdout(process::Stdio::piped()) + .stderr(process::Stdio::piped()) + .args(&["try-runtime", "--runtime=existing"]) + .args(&["execute-block"]) + .args(&["live", format!("--uri={}", ws_url).as_str()]) + .args(&["--at", format!("{:?}", at).as_str()]) + .kill_on_drop(true) + .spawn() + .unwrap() + } + + // Start a node and wait for it to begin finalizing blocks + let mut node = common::KillChildOnDrop(common::start_node()); + let ws_url = common::extract_info_from_output(node.stderr.take().unwrap()).0.ws_url; + common::wait_n_finalized_blocks(3, &ws_url).await; + + let block_number = 1; + let block_hash = common::block_hash(block_number, &ws_url).await.unwrap(); + + // Try to execute the block. + let mut block_execution = execute_block(&ws_url, block_hash); + + // The execute-block command is actually executing the next block. + let expected_output = + format!(r#".*Block #{} successfully executed"#, block_number.saturating_add(1)); + let re = Regex::new(expected_output.as_str()).unwrap(); + let matched = + common::wait_for_stream_pattern_match(block_execution.stderr.take().unwrap(), re).await; + + // Assert that the block-execution process has executed a block. + assert!(matched.is_ok()); + }) + .await; +} diff --git a/substrate/utils/frame/try-runtime/cli/tests/follow_chain.rs b/substrate/utils/frame/try-runtime/cli/tests/follow_chain.rs index a4961aa280..63b8b4c386 100644 --- a/substrate/utils/frame/try-runtime/cli/tests/follow_chain.rs +++ b/substrate/utils/frame/try-runtime/cli/tests/follow_chain.rs @@ -17,49 +17,47 @@ // along with this program. If not, see . #![cfg(unix)] +#![cfg(feature = "try-runtime")] -#[cfg(feature = "try-runtime")] -mod tests { - use assert_cmd::cargo::cargo_bin; - use regex::Regex; - use std::{ - process::{self}, - time::Duration, - }; - use substrate_cli_test_utils as common; - use tokio::process::{Child, Command}; +use assert_cmd::cargo::cargo_bin; +use regex::Regex; +use std::{ + process::{self}, + time::Duration, +}; +use substrate_cli_test_utils as common; +use tokio::process::{Child, Command}; - #[tokio::test] - async fn follow_chain_works() { - // Build substrate so binaries used in the test use the latest code. - common::build_substrate(&["--features=try-runtime"]); +#[tokio::test] +async fn follow_chain_works() { + // Build substrate so binaries used in the test use the latest code. + common::build_substrate(&["--features=try-runtime"]); - common::run_with_timeout(Duration::from_secs(60), async move { - fn start_follow(ws_url: &str) -> Child { - Command::new(cargo_bin("substrate")) - .stdout(process::Stdio::piped()) - .stderr(process::Stdio::piped()) - .args(&["try-runtime", "--runtime=existing"]) - .args(&["follow-chain", format!("--uri={}", ws_url).as_str()]) - .kill_on_drop(true) - .spawn() - .unwrap() - } + common::run_with_timeout(Duration::from_secs(60), async move { + fn start_follow(ws_url: &str) -> Child { + Command::new(cargo_bin("substrate")) + .stdout(process::Stdio::piped()) + .stderr(process::Stdio::piped()) + .args(&["try-runtime", "--runtime=existing"]) + .args(&["follow-chain", format!("--uri={}", ws_url).as_str()]) + .kill_on_drop(true) + .spawn() + .unwrap() + } - // Start a node and wait for it to begin finalizing blocks - let mut node = common::KillChildOnDrop(common::start_node()); - let ws_url = common::extract_info_from_output(node.stderr.take().unwrap()).0.ws_url; - common::wait_n_finalized_blocks(1, &ws_url).await; + // Start a node and wait for it to begin finalizing blocks + let mut node = common::KillChildOnDrop(common::start_node()); + let ws_url = common::extract_info_from_output(node.stderr.take().unwrap()).0.ws_url; + common::wait_n_finalized_blocks(1, &ws_url).await; - // Kick off the follow-chain process and wait for it to process at least 3 blocks. - let mut follow = start_follow(&ws_url); - let re = Regex::new(r#".*executed block ([3-9]|[1-9]\d+).*"#).unwrap(); - let matched = - common::wait_for_stream_pattern_match(follow.stderr.take().unwrap(), re).await; + // Kick off the follow-chain process and wait for it to process at least 3 blocks. + let mut follow = start_follow(&ws_url); + let re = Regex::new(r#".*executed block ([3-9]|[1-9]\d+).*"#).unwrap(); + let matched = + common::wait_for_stream_pattern_match(follow.stderr.take().unwrap(), re).await; - // Assert that the follow-chain process has followed at least 3 blocks. - assert!(matches!(matched, Ok(_))); - }) - .await; - } + // Assert that the follow-chain process has followed at least 3 blocks. + assert!(matched.is_ok()); + }) + .await; }