From 03716513150dd7418ba3e14d8d3fd767c817ec03 Mon Sep 17 00:00:00 2001 From: Nnamdi Aninye Date: Mon, 18 May 2026 23:26:07 +0100 Subject: [PATCH 01/30] add from and to arguments --- core/src/commands/execute_block.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/core/src/commands/execute_block.rs b/core/src/commands/execute_block.rs index 68125160410..ec5a890b95d 100644 --- a/core/src/commands/execute_block.rs +++ b/core/src/commands/execute_block.rs @@ -50,6 +50,14 @@ pub struct Command { #[arg(long, default_value = "all")] pub try_state: frame_try_runtime::TryStateSelect, + /// Block number to start execution from + #[arg(long, requires = "to")] + pub from: Option + + /// Block number to stop execution at + #[arg(long, requires = "from")] + pub to: Option + /// 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 From b1ecf9859fe9aef9c79c46fc172a018c5e922ee9 Mon Sep 17 00:00:00 2001 From: Nnamdi Aninye Date: Mon, 18 May 2026 23:45:47 +0100 Subject: [PATCH 02/30] extract execute block logic to helper function --- core/src/commands/execute_block.rs | 84 +++++++++++++++++++++++++++++- 1 file changed, 83 insertions(+), 1 deletion(-) diff --git a/core/src/commands/execute_block.rs b/core/src/commands/execute_block.rs index ec5a890b95d..a3f5fc35335 100644 --- a/core/src/commands/execute_block.rs +++ b/core/src/commands/execute_block.rs @@ -23,7 +23,7 @@ use sp_runtime::{ generic::SignedBlock, traits::{Block as BlockT, Header as HeaderT, NumberFor}, }; -use substrate_rpc_client::{ws_client, ChainApi}; +use substrate_rpc_client::{ws_client, WsClient, ChainApi}; use crate::{ common::state::{ @@ -185,3 +185,85 @@ where Ok(()) } + +// Perform block execution +pub async fn execute_block(executor: WasmExecutor, url: Option, rpc: WsClient, command: Command) -> sc_cli::Result<()> { + let live_state = match command.state { + State::Live(live_state) => { + // If no --at is provided, get the latest block to replay + if live_state.at.is_some() { + live_state + } else { + let header = + ChainApi::<(), Block::Hash, Block::Header, SignedBlock>::header( + &rpc, None, + ) + .await + .map_err(rpc_err_handler)? + .expect("header exists, block should also exist; qed"); + LiveState { + uri: vec![block_ws_uri], + at: Some(hex::encode(header.hash().encode())), + pallet: Default::default(), + hashed_prefixes: Default::default(), + child_tree: Default::default(), + } + } + } + _ => { + unreachable!("execute block currently only supports Live state") + } + }; + + // The block we want to *execute* at is the block passed by the user + let execute_at = live_state.at::()?; + + let prev_block_live_state = live_state.to_prev_block_live_state::().await?; + + // Get state for the prev block + let runtime_checks = RuntimeChecks { + name_matches: !shared.disable_spec_name_check, + version_increases: false, + try_runtime_feature_enabled: true, + }; + let ext = State::Live(prev_block_live_state) + .to_ext::(&shared, &executor, None, runtime_checks) + .await?; + + // Execute the desired block on top of it + let block = + ChainApi::<(), Block::Hash, Block::Header, SignedBlock>::block(&rpc, execute_at) + .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::( + &ext, + &mut Default::default(), + &executor, + "TryRuntime_execute_block", + &payload, + full_extensions(executor.clone()), + shared.export_proof, + )?; + + Ok(()) +} From 079652fbacf857100d4e089d721f33f4f7ceb338 Mon Sep 17 00:00:00 2001 From: Nnamdi Aninye Date: Tue, 19 May 2026 06:02:54 +0100 Subject: [PATCH 03/30] add trait bounds --- core/src/commands/execute_block.rs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/core/src/commands/execute_block.rs b/core/src/commands/execute_block.rs index a3f5fc35335..d9e5e66873b 100644 --- a/core/src/commands/execute_block.rs +++ b/core/src/commands/execute_block.rs @@ -18,7 +18,7 @@ use std::{fmt::Debug, str::FromStr}; use parity_scale_codec::Encode; -use sc_executor::sp_wasm_interface::HostFunctions; +use sc_executor::{sp_wasm_interface::HostFunctions, WasmExecutor}; use sp_runtime::{ generic::SignedBlock, traits::{Block as BlockT, Header as HeaderT, NumberFor}, @@ -50,13 +50,13 @@ pub struct Command { #[arg(long, default_value = "all")] pub try_state: frame_try_runtime::TryStateSelect, - /// Block number to start execution from + /// Block number to start execution from. #[arg(long, requires = "to")] - pub from: Option + pub from: Option, - /// Block number to stop execution at + /// Block number to stop execution at. #[arg(long, requires = "from")] - pub to: Option + pub to: Option, /// The ws uri from which to fetch the block. /// @@ -187,7 +187,11 @@ where } // Perform block execution -pub async fn execute_block(executor: WasmExecutor, url: Option, rpc: WsClient, command: Command) -> sc_cli::Result<()> { +pub async fn execute_block(shared: SharedParams, command: Command, executor: WasmExecutor, url: Option, rpc: WsClient) -> sc_cli::Result<()> +where + Block: BlockT + serde::de::DeserializeOwned, + HostFns: HostFunctions, +{ let live_state = match command.state { State::Live(live_state) => { // If no --at is provided, get the latest block to replay From e8267fa45a246cff8a7b52e13cdaafea52ad94e9 Mon Sep 17 00:00:00 2001 From: Nnamdi Aninye Date: Tue, 19 May 2026 06:04:21 +0100 Subject: [PATCH 04/30] add remaining trait bounds --- core/src/commands/execute_block.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/core/src/commands/execute_block.rs b/core/src/commands/execute_block.rs index d9e5e66873b..092a40eadfa 100644 --- a/core/src/commands/execute_block.rs +++ b/core/src/commands/execute_block.rs @@ -190,6 +190,10 @@ where pub async fn execute_block(shared: SharedParams, command: Command, executor: WasmExecutor, url: Option, rpc: WsClient) -> sc_cli::Result<()> where Block: BlockT + serde::de::DeserializeOwned, + ::Err: Debug, + Block::Hash: serde::de::DeserializeOwned, + Block::Header: serde::de::DeserializeOwned, + as TryInto>::Error: Debug, HostFns: HostFunctions, { let live_state = match command.state { From 1036d6466629b365a2692a395a904d9891f14419 Mon Sep 17 00:00:00 2001 From: Nnamdi Aninye Date: Tue, 19 May 2026 06:05:40 +0100 Subject: [PATCH 05/30] add url --- core/src/commands/execute_block.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/commands/execute_block.rs b/core/src/commands/execute_block.rs index 092a40eadfa..535f382329f 100644 --- a/core/src/commands/execute_block.rs +++ b/core/src/commands/execute_block.rs @@ -210,7 +210,7 @@ where .map_err(rpc_err_handler)? .expect("header exists, block should also exist; qed"); LiveState { - uri: vec![block_ws_uri], + uri: vec![url], at: Some(hex::encode(header.hash().encode())), pallet: Default::default(), hashed_prefixes: Default::default(), From 5b39cdab6876dbcc7ba3b1114f5a408254e38e26 Mon Sep 17 00:00:00 2001 From: Nnamdi Aninye Date: Tue, 19 May 2026 06:08:09 +0100 Subject: [PATCH 06/30] update --- core/src/commands/execute_block.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/commands/execute_block.rs b/core/src/commands/execute_block.rs index 535f382329f..8ca0b7f5a5b 100644 --- a/core/src/commands/execute_block.rs +++ b/core/src/commands/execute_block.rs @@ -187,7 +187,7 @@ where } // Perform block execution -pub async fn execute_block(shared: SharedParams, command: Command, executor: WasmExecutor, url: Option, rpc: WsClient) -> sc_cli::Result<()> +pub async fn execute_block(shared: SharedParams, command: Command, executor: WasmExecutor, url: String, rpc: WsClient) -> sc_cli::Result<()> where Block: BlockT + serde::de::DeserializeOwned, ::Err: Debug, From d3f416b79a2502a7b267b57265a670cbd6185f2a Mon Sep 17 00:00:00 2001 From: Nnamdi Aninye Date: Tue, 19 May 2026 06:10:58 +0100 Subject: [PATCH 07/30] replace logic with helper --- core/src/commands/execute_block.rs | 79 +----------------------------- 1 file changed, 1 insertion(+), 78 deletions(-) diff --git a/core/src/commands/execute_block.rs b/core/src/commands/execute_block.rs index 8ca0b7f5a5b..d9bb84c9899 100644 --- a/core/src/commands/execute_block.rs +++ b/core/src/commands/execute_block.rs @@ -106,84 +106,7 @@ where let block_ws_uri = command.block_ws_uri(); let rpc = ws_client(&block_ws_uri).await?; - let live_state = match command.state { - State::Live(live_state) => { - // If no --at is provided, get the latest block to replay - if live_state.at.is_some() { - live_state - } else { - let header = - ChainApi::<(), Block::Hash, Block::Header, SignedBlock>::header( - &rpc, None, - ) - .await - .map_err(rpc_err_handler)? - .expect("header exists, block should also exist; qed"); - LiveState { - uri: vec![block_ws_uri], - at: Some(hex::encode(header.hash().encode())), - pallet: Default::default(), - hashed_prefixes: Default::default(), - child_tree: Default::default(), - } - } - } - _ => { - unreachable!("execute block currently only supports Live state") - } - }; - - // The block we want to *execute* at is the block passed by the user - let execute_at = live_state.at::()?; - - let prev_block_live_state = live_state.to_prev_block_live_state::().await?; - - // Get state for the prev block - let runtime_checks = RuntimeChecks { - name_matches: !shared.disable_spec_name_check, - version_increases: false, - try_runtime_feature_enabled: true, - }; - let ext = State::Live(prev_block_live_state) - .to_ext::(&shared, &executor, None, runtime_checks) - .await?; - - // Execute the desired block on top of it - let block = - ChainApi::<(), Block::Hash, Block::Header, SignedBlock>::block(&rpc, execute_at) - .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::( - &ext, - &mut Default::default(), - &executor, - "TryRuntime_execute_block", - &payload, - full_extensions(executor.clone()), - shared.export_proof, - )?; - - Ok(()) + execute_block(shared, command, executor, block_ws_uri, rpc) } // Perform block execution From 3314348ec3fbba77430d03e0c03d1d29fc17d605 Mon Sep 17 00:00:00 2001 From: Nnamdi Aninye Date: Tue, 19 May 2026 06:12:32 +0100 Subject: [PATCH 08/30] await execute block --- core/src/commands/execute_block.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/commands/execute_block.rs b/core/src/commands/execute_block.rs index d9bb84c9899..12523c464ba 100644 --- a/core/src/commands/execute_block.rs +++ b/core/src/commands/execute_block.rs @@ -106,7 +106,7 @@ where let block_ws_uri = command.block_ws_uri(); let rpc = ws_client(&block_ws_uri).await?; - execute_block(shared, command, executor, block_ws_uri, rpc) + execute_block(shared, command, executor, block_ws_uri, rpc).await } // Perform block execution From 4032ea7528635253d29646e547655b6e89c34f10 Mon Sep 17 00:00:00 2001 From: Nnamdi Aninye Date: Tue, 19 May 2026 06:14:07 +0100 Subject: [PATCH 09/30] add types --- core/src/commands/execute_block.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/commands/execute_block.rs b/core/src/commands/execute_block.rs index 12523c464ba..44917868368 100644 --- a/core/src/commands/execute_block.rs +++ b/core/src/commands/execute_block.rs @@ -106,7 +106,7 @@ where let block_ws_uri = command.block_ws_uri(); let rpc = ws_client(&block_ws_uri).await?; - execute_block(shared, command, executor, block_ws_uri, rpc).await + execute_block::(shared, command, executor, block_ws_uri, rpc).await } // Perform block execution From e7e8832ccc3a5e0c2d8a284985ef8c9b792ac62b Mon Sep 17 00:00:00 2001 From: Nnamdi Aninye Date: Tue, 19 May 2026 06:15:53 +0100 Subject: [PATCH 10/30] fmt --- core/src/commands/execute_block.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/core/src/commands/execute_block.rs b/core/src/commands/execute_block.rs index 44917868368..d6dee543865 100644 --- a/core/src/commands/execute_block.rs +++ b/core/src/commands/execute_block.rs @@ -23,7 +23,7 @@ use sp_runtime::{ generic::SignedBlock, traits::{Block as BlockT, Header as HeaderT, NumberFor}, }; -use substrate_rpc_client::{ws_client, WsClient, ChainApi}; +use substrate_rpc_client::{ws_client, ChainApi, WsClient}; use crate::{ common::state::{ @@ -110,7 +110,13 @@ where } // Perform block execution -pub async fn execute_block(shared: SharedParams, command: Command, executor: WasmExecutor, url: String, rpc: WsClient) -> sc_cli::Result<()> +pub async fn execute_block( + shared: SharedParams, + command: Command, + executor: WasmExecutor, + url: String, + rpc: WsClient, +) -> sc_cli::Result<()> where Block: BlockT + serde::de::DeserializeOwned, ::Err: Debug, From 6aa5946e473b5e2abaa91af1d867543af84e9c57 Mon Sep 17 00:00:00 2001 From: Nnamdi Aninye Date: Tue, 19 May 2026 07:21:08 +0100 Subject: [PATCH 11/30] move live_state resolution into run function --- core/src/commands/execute_block.rs | 76 ++++++++++++++++++------------ 1 file changed, 45 insertions(+), 31 deletions(-) diff --git a/core/src/commands/execute_block.rs b/core/src/commands/execute_block.rs index d6dee543865..2e7d816cbc0 100644 --- a/core/src/commands/execute_block.rs +++ b/core/src/commands/execute_block.rs @@ -106,7 +106,49 @@ where let block_ws_uri = command.block_ws_uri(); let rpc = ws_client(&block_ws_uri).await?; - execute_block::(shared, command, executor, block_ws_uri, rpc).await + // If --from and --to is passed, they take precedence over LiveState --at. + if let(Some(from), Some(to)) = (command.from, command.to) { + if from > to { + return Err(sc_cli::Error::Application( + format!("--from ({from}) must be less than or equal to --to ({to})").into(), + )); + } + + for block_number in from..=to { + + } + } else { + let live_state = match command.state { + State::Live(live_state) => { + // If no --at is provided, get the latest block to replay + if live_state.at.is_some() { + live_state + } else { + let header = + ChainApi::<(), Block::Hash, Block::Header, SignedBlock>::header( + &rpc, None, + ) + .await + .map_err(rpc_err_handler)? + .expect("header exists, block should also exist; qed"); + LiveState { + uri: vec![url], + at: Some(hex::encode(header.hash().encode())), + pallet: Default::default(), + hashed_prefixes: Default::default(), + child_tree: Default::default(), + } + } + } + _ => { + unreachable!("execute block currently only supports Live state") + } + }; + + execute_block::(shared, command, executor, block_ws_uri, rpc, live_state).await; + } + + Ok(()) } // Perform block execution @@ -116,7 +158,8 @@ pub async fn execute_block( executor: WasmExecutor, url: String, rpc: WsClient, -) -> sc_cli::Result<()> + live_state: State; +) where Block: BlockT + serde::de::DeserializeOwned, ::Err: Debug, @@ -125,33 +168,6 @@ where as TryInto>::Error: Debug, HostFns: HostFunctions, { - let live_state = match command.state { - State::Live(live_state) => { - // If no --at is provided, get the latest block to replay - if live_state.at.is_some() { - live_state - } else { - let header = - ChainApi::<(), Block::Hash, Block::Header, SignedBlock>::header( - &rpc, None, - ) - .await - .map_err(rpc_err_handler)? - .expect("header exists, block should also exist; qed"); - LiveState { - uri: vec![url], - at: Some(hex::encode(header.hash().encode())), - pallet: Default::default(), - hashed_prefixes: Default::default(), - child_tree: Default::default(), - } - } - } - _ => { - unreachable!("execute block currently only supports Live state") - } - }; - // The block we want to *execute* at is the block passed by the user let execute_at = live_state.at::()?; @@ -201,6 +217,4 @@ where full_extensions(executor.clone()), shared.export_proof, )?; - - Ok(()) } From 0b6a87c0b588fc9a8db1386971a99fa64a7eaf98 Mon Sep 17 00:00:00 2001 From: Nnamdi Aninye Date: Tue, 19 May 2026 07:24:31 +0100 Subject: [PATCH 12/30] refactor --- core/src/commands/execute_block.rs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/core/src/commands/execute_block.rs b/core/src/commands/execute_block.rs index 2e7d816cbc0..ce6e64cf472 100644 --- a/core/src/commands/execute_block.rs +++ b/core/src/commands/execute_block.rs @@ -117,6 +117,8 @@ where for block_number in from..=to { } + + Ok(()) } else { let live_state = match command.state { State::Live(live_state) => { @@ -132,7 +134,7 @@ where .map_err(rpc_err_handler)? .expect("header exists, block should also exist; qed"); LiveState { - uri: vec![url], + uri: vec![block_ws_uri], at: Some(hex::encode(header.hash().encode())), pallet: Default::default(), hashed_prefixes: Default::default(), @@ -145,10 +147,8 @@ where } }; - execute_block::(shared, command, executor, block_ws_uri, rpc, live_state).await; + execute_block::(shared, command, executor, rpc, live_state).await } - - Ok(()) } // Perform block execution @@ -156,10 +156,9 @@ pub async fn execute_block( shared: SharedParams, command: Command, executor: WasmExecutor, - url: String, rpc: WsClient, - live_state: State; -) + live_state: State, +) -> sc_cli::Result<()> where Block: BlockT + serde::de::DeserializeOwned, ::Err: Debug, @@ -217,4 +216,6 @@ where full_extensions(executor.clone()), shared.export_proof, )?; + + Ok(()) } From 171905a458a2370f3e32587d49267d0d41d639d9 Mon Sep 17 00:00:00 2001 From: Nnamdi Aninye Date: Tue, 19 May 2026 07:26:31 +0100 Subject: [PATCH 13/30] update state type --- core/src/commands/execute_block.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/commands/execute_block.rs b/core/src/commands/execute_block.rs index ce6e64cf472..2da66aa54e8 100644 --- a/core/src/commands/execute_block.rs +++ b/core/src/commands/execute_block.rs @@ -117,7 +117,7 @@ where for block_number in from..=to { } - + Ok(()) } else { let live_state = match command.state { @@ -157,7 +157,7 @@ pub async fn execute_block( command: Command, executor: WasmExecutor, rpc: WsClient, - live_state: State, + live_state: LiveState, ) -> sc_cli::Result<()> where Block: BlockT + serde::de::DeserializeOwned, From e276b2420794b314c4d405553bb8dc6c8be792f6 Mon Sep 17 00:00:00 2001 From: Nnamdi Aninye Date: Tue, 19 May 2026 07:31:20 +0100 Subject: [PATCH 14/30] add ref --- core/src/commands/execute_block.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/commands/execute_block.rs b/core/src/commands/execute_block.rs index 2da66aa54e8..2ed13e9a637 100644 --- a/core/src/commands/execute_block.rs +++ b/core/src/commands/execute_block.rs @@ -121,7 +121,7 @@ where Ok(()) } else { let live_state = match command.state { - State::Live(live_state) => { + State::Live(ref live_state) => { // If no --at is provided, get the latest block to replay if live_state.at.is_some() { live_state From 8779c3866ae2dff46acf809cd4c635cd34775ef6 Mon Sep 17 00:00:00 2001 From: Nnamdi Aninye Date: Tue, 19 May 2026 07:38:32 +0100 Subject: [PATCH 15/30] resolve ref issues --- core/src/commands/execute_block.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/src/commands/execute_block.rs b/core/src/commands/execute_block.rs index 2ed13e9a637..b4cf1557674 100644 --- a/core/src/commands/execute_block.rs +++ b/core/src/commands/execute_block.rs @@ -120,11 +120,11 @@ where Ok(()) } else { - let live_state = match command.state { - State::Live(ref live_state) => { + let live_state = match &command.state { + State::Live(live_state) => { // If no --at is provided, get the latest block to replay if live_state.at.is_some() { - live_state + live_state.clone() } else { let header = ChainApi::<(), Block::Hash, Block::Header, SignedBlock>::header( From ff15397e30c66c7023e24c7b222ede6da840668b Mon Sep 17 00:00:00 2001 From: Nnamdi Aninye Date: Tue, 19 May 2026 08:19:29 +0100 Subject: [PATCH 16/30] complete initial logic to execute block from to =to --- core/src/commands/execute_block.rs | 33 +++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/core/src/commands/execute_block.rs b/core/src/commands/execute_block.rs index b4cf1557674..13deb12c14f 100644 --- a/core/src/commands/execute_block.rs +++ b/core/src/commands/execute_block.rs @@ -19,6 +19,7 @@ use std::{fmt::Debug, str::FromStr}; use parity_scale_codec::Encode; use sc_executor::{sp_wasm_interface::HostFunctions, WasmExecutor}; +use sp_rpc::{list::ListOrValue, number::NumberOrHex}; use sp_runtime::{ generic::SignedBlock, traits::{Block as BlockT, Header as HeaderT, NumberFor}, @@ -115,7 +116,37 @@ where } for block_number in from..=to { + let hash = ChainApi::<(), Block::Hash, Block::Header, SignedBlock>::block_hash( + &rpc, + Some(ListOrValue::Value(NumberOrHex::Number(block_number))), + ) + .await + .map_err(rpc_err_handler)? + .expect("block hash should exist;"); + + let header = ChainApi::<(), Block::Hash, Block::Header, SignedBlock>::header( + &rpc, + Some(hash), + ) + .await + .map_err(rpc_err_handler)? + .expect("hash exists, header should exist;"); + + let live_state = LiveState { + uri: vec![block_ws_uri], + at: Some(hex::encode(header.hash().encode())), + pallet: Default::default(), + hashed_prefixes: Default::default(), + child_tree: Default::default(), + } + let _ = execute_block::( + shared, + command, + executor, + rpc, + live_state, + ); } Ok(()) @@ -151,7 +182,7 @@ where } } -// Perform block execution +// Perform block execution on live state pub async fn execute_block( shared: SharedParams, command: Command, From e1037d7fa9d897272b8883bf87cb32d5d392691f Mon Sep 17 00:00:00 2001 From: Nnamdi Aninye Date: Thu, 21 May 2026 09:32:05 +0100 Subject: [PATCH 17/30] log block hash --- core/src/commands/execute_block.rs | 56 +++++++++++++++++------------- 1 file changed, 31 insertions(+), 25 deletions(-) diff --git a/core/src/commands/execute_block.rs b/core/src/commands/execute_block.rs index 13deb12c14f..845336df954 100644 --- a/core/src/commands/execute_block.rs +++ b/core/src/commands/execute_block.rs @@ -116,37 +116,43 @@ where } for block_number in from..=to { - let hash = ChainApi::<(), Block::Hash, Block::Header, SignedBlock>::block_hash( + let hash_result = ChainApi::<(), Block::Hash, Block::Header, SignedBlock>::block_hash( &rpc, Some(ListOrValue::Value(NumberOrHex::Number(block_number))), ) .await - .map_err(rpc_err_handler)? - .expect("block hash should exist;"); + .map_err(rpc_err_handler)?; - let header = ChainApi::<(), Block::Hash, Block::Header, SignedBlock>::header( - &rpc, - Some(hash), - ) - .await - .map_err(rpc_err_handler)? - .expect("hash exists, header should exist;"); - - let live_state = LiveState { - uri: vec![block_ws_uri], - at: Some(hex::encode(header.hash().encode())), - pallet: Default::default(), - hashed_prefixes: Default::default(), - child_tree: Default::default(), - } + let hash = match hash_result { + ListOrValue::Value(h) => h.expect("block hash should exist"), + _ => unreachable!("requested a single block hash"), + }; + + log::debug!(target: LOG_TARGET, "obtained block hash {:?} for block number {}", hash, block_number); + + // let header = ChainApi::<(), Block::Hash, Block::Header, SignedBlock>::header( + // &rpc, + // Some(hash), + // ) + // .await + // .map_err(rpc_err_handler)? + // .expect("hash exists, header should exist;"); + + // let live_state = LiveState { + // uri: vec![block_ws_uri], + // at: Some(hex::encode(header.hash().encode())), + // pallet: Default::default(), + // hashed_prefixes: Default::default(), + // child_tree: Default::default(), + // }; - let _ = execute_block::( - shared, - command, - executor, - rpc, - live_state, - ); + // let _ = execute_block::( + // shared, + // command, + // executor, + // rpc, + // live_state, + // ); } Ok(()) From 26fe5501bb5e00c0999716c721978fb5347fea0f Mon Sep 17 00:00:00 2001 From: Nnamdi Aninye Date: Thu, 21 May 2026 10:30:59 +0100 Subject: [PATCH 18/30] fetch hash list and log --- core/src/commands/execute_block.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/core/src/commands/execute_block.rs b/core/src/commands/execute_block.rs index 845336df954..2d62b0ca517 100644 --- a/core/src/commands/execute_block.rs +++ b/core/src/commands/execute_block.rs @@ -115,6 +115,19 @@ where )); } + let block_numbers = (from..=to) + .map(|n| NumberOrHex::Number(n)) + .collect::>(); + + let hash_list = ChainApi::<(), Block::Hash, Block::Header, SignedBlock>::block_hash( + &rpc, + Some(ListOrValue::List(block_numbers)), + ) + .await + .map_err(rpc_err_handler)?; + + log::debug!(target: LOG_TARGET, "block_hash response: {:?}", hash_list); + for block_number in from..=to { let hash_result = ChainApi::<(), Block::Hash, Block::Header, SignedBlock>::block_hash( &rpc, From 18d52caa8571eedbe91b30624324cf29966f001a Mon Sep 17 00:00:00 2001 From: Nnamdi Aninye Date: Thu, 21 May 2026 10:51:16 +0100 Subject: [PATCH 19/30] loop through hash list --- core/src/commands/execute_block.rs | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/core/src/commands/execute_block.rs b/core/src/commands/execute_block.rs index 2d62b0ca517..b5b1cfd9dd4 100644 --- a/core/src/commands/execute_block.rs +++ b/core/src/commands/execute_block.rs @@ -128,20 +128,14 @@ where log::debug!(target: LOG_TARGET, "block_hash response: {:?}", hash_list); - for block_number in from..=to { - let hash_result = ChainApi::<(), Block::Hash, Block::Header, SignedBlock>::block_hash( - &rpc, - Some(ListOrValue::Value(NumberOrHex::Number(block_number))), - ) - .await - .map_err(rpc_err_handler)?; + if let Some(ListOrValue::List(hashed)) = hash_list { + for (block_number, hash) in (from..=to).zip(hashes) { + let Some(hash) = hash else { + log::warn!(target: LOG_TARGET, "skipping block {block_number}, hash was None"); + continue; + } - let hash = match hash_result { - ListOrValue::Value(h) => h.expect("block hash should exist"), - _ => unreachable!("requested a single block hash"), - }; - - log::debug!(target: LOG_TARGET, "obtained block hash {:?} for block number {}", hash, block_number); + log::debug!(target: LOG_TARGET, "hash found, block number: {block_number}, hash: {hash}"); // let header = ChainApi::<(), Block::Hash, Block::Header, SignedBlock>::header( // &rpc, @@ -166,6 +160,7 @@ where // rpc, // live_state, // ); + } } Ok(()) From ea5f3e2d294d296453e1e2c552035d990a441b5f Mon Sep 17 00:00:00 2001 From: Nnamdi Aninye Date: Thu, 21 May 2026 10:54:26 +0100 Subject: [PATCH 20/30] fix --- core/src/commands/execute_block.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/commands/execute_block.rs b/core/src/commands/execute_block.rs index b5b1cfd9dd4..c090517a211 100644 --- a/core/src/commands/execute_block.rs +++ b/core/src/commands/execute_block.rs @@ -128,12 +128,12 @@ where log::debug!(target: LOG_TARGET, "block_hash response: {:?}", hash_list); - if let Some(ListOrValue::List(hashed)) = hash_list { + if let Some(ListOrValue::List(hashes)) = hash_list { for (block_number, hash) in (from..=to).zip(hashes) { let Some(hash) = hash else { log::warn!(target: LOG_TARGET, "skipping block {block_number}, hash was None"); continue; - } + }; log::debug!(target: LOG_TARGET, "hash found, block number: {block_number}, hash: {hash}"); From af080f64fddc8bfe14bb007bce55e7a4e31f6f48 Mon Sep 17 00:00:00 2001 From: Nnamdi Aninye Date: Thu, 21 May 2026 10:59:31 +0100 Subject: [PATCH 21/30] update --- core/src/commands/execute_block.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/commands/execute_block.rs b/core/src/commands/execute_block.rs index c090517a211..bb96e7bb52b 100644 --- a/core/src/commands/execute_block.rs +++ b/core/src/commands/execute_block.rs @@ -128,7 +128,7 @@ where log::debug!(target: LOG_TARGET, "block_hash response: {:?}", hash_list); - if let Some(ListOrValue::List(hashes)) = hash_list { + if let ListOrValue::List(hashes) = hash_list { for (block_number, hash) in (from..=to).zip(hashes) { let Some(hash) = hash else { log::warn!(target: LOG_TARGET, "skipping block {block_number}, hash was None"); From 8764f6402d404ae320e7ba7b269b8be6ed5277d1 Mon Sep 17 00:00:00 2001 From: Nnamdi Aninye Date: Thu, 21 May 2026 11:05:11 +0100 Subject: [PATCH 22/30] uncomment actual execute block logic --- core/src/commands/execute_block.rs | 42 +++++++++++++++--------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/core/src/commands/execute_block.rs b/core/src/commands/execute_block.rs index bb96e7bb52b..9c4d4aa3154 100644 --- a/core/src/commands/execute_block.rs +++ b/core/src/commands/execute_block.rs @@ -137,29 +137,29 @@ where log::debug!(target: LOG_TARGET, "hash found, block number: {block_number}, hash: {hash}"); - // let header = ChainApi::<(), Block::Hash, Block::Header, SignedBlock>::header( - // &rpc, - // Some(hash), - // ) - // .await - // .map_err(rpc_err_handler)? - // .expect("hash exists, header should exist;"); + let header = ChainApi::<(), Block::Hash, Block::Header, SignedBlock>::header( + &rpc, + Some(hash), + ) + .await + .map_err(rpc_err_handler)? + .expect("hash exists, header should exist;"); - // let live_state = LiveState { - // uri: vec![block_ws_uri], - // at: Some(hex::encode(header.hash().encode())), - // pallet: Default::default(), - // hashed_prefixes: Default::default(), - // child_tree: Default::default(), - // }; + let live_state = LiveState { + uri: vec![block_ws_uri], + at: Some(hex::encode(header.hash().encode())), + pallet: Default::default(), + hashed_prefixes: Default::default(), + child_tree: Default::default(), + }; - // let _ = execute_block::( - // shared, - // command, - // executor, - // rpc, - // live_state, - // ); + let _ = execute_block::( + shared, + command, + executor, + rpc, + live_state, + ); } } From 53cf1c6e089ebe7cc9d0b803dc18585f4afd4a0b Mon Sep 17 00:00:00 2001 From: Nnamdi Aninye Date: Thu, 21 May 2026 11:11:57 +0100 Subject: [PATCH 23/30] borrow rpc instead of owning --- core/src/commands/execute_block.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/src/commands/execute_block.rs b/core/src/commands/execute_block.rs index 9c4d4aa3154..cd14e3beaee 100644 --- a/core/src/commands/execute_block.rs +++ b/core/src/commands/execute_block.rs @@ -157,7 +157,7 @@ where shared, command, executor, - rpc, + &rpc, live_state, ); } @@ -192,7 +192,7 @@ where } }; - execute_block::(shared, command, executor, rpc, live_state).await + execute_block::(shared, command, executor, &rpc, live_state).await } } @@ -201,7 +201,7 @@ pub async fn execute_block( shared: SharedParams, command: Command, executor: WasmExecutor, - rpc: WsClient, + rpc: &WsClient, live_state: LiveState, ) -> sc_cli::Result<()> where From e0c6f01dd7aaa725dc1e3536d25c71bba401ff8e Mon Sep 17 00:00:00 2001 From: Nnamdi Aninye Date: Thu, 21 May 2026 11:16:00 +0100 Subject: [PATCH 24/30] update execute_block to take references, only move live state --- core/src/commands/execute_block.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/core/src/commands/execute_block.rs b/core/src/commands/execute_block.rs index cd14e3beaee..d40dff2d7b7 100644 --- a/core/src/commands/execute_block.rs +++ b/core/src/commands/execute_block.rs @@ -146,7 +146,7 @@ where .expect("hash exists, header should exist;"); let live_state = LiveState { - uri: vec![block_ws_uri], + uri: vec![block_ws_uri.clone()], at: Some(hex::encode(header.hash().encode())), pallet: Default::default(), hashed_prefixes: Default::default(), @@ -154,9 +154,9 @@ where }; let _ = execute_block::( - shared, - command, - executor, + &shared, + &command, + &executor, &rpc, live_state, ); @@ -192,15 +192,15 @@ where } }; - execute_block::(shared, command, executor, &rpc, live_state).await + execute_block::(&shared, &command, &executor, &rpc, live_state).await } } // Perform block execution on live state pub async fn execute_block( - shared: SharedParams, - command: Command, - executor: WasmExecutor, + shared: &SharedParams, + command: &Command, + executor: &WasmExecutor, rpc: &WsClient, live_state: LiveState, ) -> sc_cli::Result<()> From 9907167fcd9d231747e787d65b65aa64c57875dc Mon Sep 17 00:00:00 2001 From: Nnamdi Aninye Date: Thu, 21 May 2026 11:18:29 +0100 Subject: [PATCH 25/30] update --- core/src/commands/execute_block.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/commands/execute_block.rs b/core/src/commands/execute_block.rs index d40dff2d7b7..9a8bb238a6e 100644 --- a/core/src/commands/execute_block.rs +++ b/core/src/commands/execute_block.rs @@ -229,7 +229,7 @@ where // Execute the desired block on top of it let block = - ChainApi::<(), Block::Hash, Block::Header, SignedBlock>::block(&rpc, execute_at) + ChainApi::<(), Block::Hash, Block::Header, SignedBlock>::block(rpc, execute_at) .await .map_err(rpc_err_handler)? .expect("header exists, block should also exist; qed") From 4a9dd1c576e828a36b0e58c46384765650865abc Mon Sep 17 00:00:00 2001 From: Nnamdi Aninye Date: Thu, 21 May 2026 11:20:02 +0100 Subject: [PATCH 26/30] update --- core/src/commands/execute_block.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/commands/execute_block.rs b/core/src/commands/execute_block.rs index 9a8bb238a6e..ce66beb037e 100644 --- a/core/src/commands/execute_block.rs +++ b/core/src/commands/execute_block.rs @@ -248,7 +248,7 @@ where block.clone(), state_root_check, signature_check, - command.try_state, + command.try_state.clone(), ) .encode(); @@ -259,7 +259,7 @@ where "TryRuntime_execute_block", &payload, full_extensions(executor.clone()), - shared.export_proof, + shared.export_proof.clone(), )?; Ok(()) From 6071c8a2769cc3ba5702ec5d4df5ac446544fa61 Mon Sep 17 00:00:00 2001 From: Nnamdi Aninye Date: Thu, 21 May 2026 11:22:42 +0100 Subject: [PATCH 27/30] await execute block inside loop --- core/src/commands/execute_block.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/commands/execute_block.rs b/core/src/commands/execute_block.rs index ce66beb037e..7bc297a7175 100644 --- a/core/src/commands/execute_block.rs +++ b/core/src/commands/execute_block.rs @@ -159,7 +159,7 @@ where &executor, &rpc, live_state, - ); + ).await; } } From f809e8b2fa9d82fa6ae88a6985c616944f9fc004 Mon Sep 17 00:00:00 2001 From: Nnamdi Aninye Date: Thu, 21 May 2026 11:42:31 +0100 Subject: [PATCH 28/30] update log --- core/src/commands/execute_block.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/core/src/commands/execute_block.rs b/core/src/commands/execute_block.rs index 7bc297a7175..ab0946ae477 100644 --- a/core/src/commands/execute_block.rs +++ b/core/src/commands/execute_block.rs @@ -126,8 +126,6 @@ where .await .map_err(rpc_err_handler)?; - log::debug!(target: LOG_TARGET, "block_hash response: {:?}", hash_list); - if let ListOrValue::List(hashes) = hash_list { for (block_number, hash) in (from..=to).zip(hashes) { let Some(hash) = hash else { @@ -135,7 +133,7 @@ where continue; }; - log::debug!(target: LOG_TARGET, "hash found, block number: {block_number}, hash: {hash}"); + log::info!(target: LOG_TARGET, "hash found, block number: {block_number}, hash: {hash}"); let header = ChainApi::<(), Block::Hash, Block::Header, SignedBlock>::header( &rpc, From bf205e5baef44fd6b1da6e23a1bee046ba2db83c Mon Sep 17 00:00:00 2001 From: Nnamdi Aninye Date: Thu, 21 May 2026 11:55:26 +0100 Subject: [PATCH 29/30] cargo fmt --- core/src/commands/execute_block.rs | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/core/src/commands/execute_block.rs b/core/src/commands/execute_block.rs index ab0946ae477..f1fc789af1e 100644 --- a/core/src/commands/execute_block.rs +++ b/core/src/commands/execute_block.rs @@ -108,7 +108,7 @@ where let rpc = ws_client(&block_ws_uri).await?; // If --from and --to is passed, they take precedence over LiveState --at. - if let(Some(from), Some(to)) = (command.from, command.to) { + if let (Some(from), Some(to)) = (command.from, command.to) { if from > to { return Err(sc_cli::Error::Application( format!("--from ({from}) must be less than or equal to --to ({to})").into(), @@ -135,13 +135,14 @@ where log::info!(target: LOG_TARGET, "hash found, block number: {block_number}, hash: {hash}"); - let header = ChainApi::<(), Block::Hash, Block::Header, SignedBlock>::header( - &rpc, - Some(hash), - ) - .await - .map_err(rpc_err_handler)? - .expect("hash exists, header should exist;"); + let header = + ChainApi::<(), Block::Hash, Block::Header, SignedBlock>::header( + &rpc, + Some(hash), + ) + .await + .map_err(rpc_err_handler)? + .expect("hash exists, header should exist;"); let live_state = LiveState { uri: vec![block_ws_uri.clone()], @@ -151,13 +152,9 @@ where child_tree: Default::default(), }; - let _ = execute_block::( - &shared, - &command, - &executor, - &rpc, - live_state, - ).await; + let _ = + execute_block::(&shared, &command, &executor, &rpc, live_state) + .await; } } From 0e4fb1dd3dfddda8b085d31474adb7424776c865 Mon Sep 17 00:00:00 2001 From: Nnamdi Aninye Date: Thu, 21 May 2026 13:43:52 +0100 Subject: [PATCH 30/30] add new test case --- cli/tests/execute_block.rs | 40 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/cli/tests/execute_block.rs b/cli/tests/execute_block.rs index 18e8180769d..d27d2b9b29a 100644 --- a/cli/tests/execute_block.rs +++ b/cli/tests/execute_block.rs @@ -121,5 +121,45 @@ async fn execute_block_works() { .status .success()); }) + .await; + + // Test passing --from and --to to execute a range of blocks. + common::run_with_timeout(Duration::from_secs(120), async move { + let ws_url = format!("ws://localhost:{}", port); + let from = 3u64; + let to = 5u64; + + fn execute_block_range(ws_url: &str, from: u64, to: u64) -> tokio::process::Child { + Command::new(cargo_bin("try-runtime")) + .stdout(std::process::Stdio::piped()) + .stderr(std::process::Stdio::piped()) + .arg("--runtime=existing") + .args(["execute-block"]) + .args([format!("--from={}", from), format!("--to={}", to)]) + .args(["live", format!("--uri={}", ws_url).as_str()]) + .kill_on_drop(true) + .spawn() + .unwrap() + } + + let mut block_execution = execute_block_range(&ws_url, from, to); + + // Expect the last block in the range to be successfully executed. + let expected_output = format!(r#".*Block #{} successfully executed"#, to); + 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 all blocks in the range were executed. + assert!(matched.is_ok()); + + // Assert that the block-execution exited successfully. + assert!(block_execution + .wait_with_output() + .await + .unwrap() + .status + .success()); + }) .await }