1818use std:: { fmt:: Debug , str:: FromStr } ;
1919
2020use parity_scale_codec:: Encode ;
21- use sc_executor:: sp_wasm_interface:: HostFunctions ;
21+ use sc_executor:: { sp_wasm_interface:: HostFunctions , WasmExecutor } ;
22+ use sp_rpc:: { list:: ListOrValue , number:: NumberOrHex } ;
2223use sp_runtime:: {
2324 generic:: SignedBlock ,
2425 traits:: { Block as BlockT , Header as HeaderT , NumberFor } ,
2526} ;
26- use substrate_rpc_client:: { ws_client, ChainApi } ;
27+ use substrate_rpc_client:: { ws_client, ChainApi , WsClient } ;
2728
2829use crate :: {
2930 common:: state:: {
@@ -50,6 +51,14 @@ pub struct Command {
5051 #[ arg( long, default_value = "all" ) ]
5152 pub try_state : frame_try_runtime:: TryStateSelect ,
5253
54+ /// Block number to start execution from.
55+ #[ arg( long, requires = "to" ) ]
56+ pub from : Option < u64 > ,
57+
58+ /// Block number to stop execution at.
59+ #[ arg( long, requires = "from" ) ]
60+ pub to : Option < u64 > ,
61+
5362 /// The ws uri from which to fetch the block.
5463 ///
5564 /// This will always fetch the next block of whatever `state` is referring to, because this is
@@ -98,33 +107,103 @@ where
98107 let block_ws_uri = command. block_ws_uri ( ) ;
99108 let rpc = ws_client ( & block_ws_uri) . await ?;
100109
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 {
110+ // If --from and --to is passed, they take precedence over LiveState --at.
111+ if let ( Some ( from) , Some ( to) ) = ( command. from , command. to ) {
112+ if from > to {
113+ return Err ( sc_cli:: Error :: Application (
114+ format ! ( "--from ({from}) must be less than or equal to --to ({to})" ) . into ( ) ,
115+ ) ) ;
116+ }
117+
118+ let block_numbers = ( from..=to) . map ( NumberOrHex :: Number ) . collect :: < Vec < _ > > ( ) ;
119+
120+ let hash_list = ChainApi :: < ( ) , Block :: Hash , Block :: Header , SignedBlock < Block > > :: block_hash (
121+ & rpc,
122+ Some ( ListOrValue :: List ( block_numbers) ) ,
123+ )
124+ . await
125+ . map_err ( rpc_err_handler) ?;
126+
127+ if let ListOrValue :: List ( hashes) = hash_list {
128+ for ( block_number, hash) in ( from..=to) . zip ( hashes) {
129+ let Some ( hash) = hash else {
130+ log:: warn!( target: LOG_TARGET , "skipping block {block_number}, hash was None" ) ;
131+ continue ;
132+ } ;
133+
134+ log:: info!( target: LOG_TARGET , "hash found, block number: {block_number}, hash: {hash}" ) ;
135+
107136 let header =
108137 ChainApi :: < ( ) , Block :: Hash , Block :: Header , SignedBlock < Block > > :: header (
109- & rpc, None ,
138+ & rpc,
139+ Some ( hash) ,
110140 )
111141 . await
112142 . map_err ( rpc_err_handler) ?
113- . expect ( "header exists, block should also exist; qed" ) ;
114- LiveState {
115- uri : vec ! [ block_ws_uri] ,
143+ . expect ( "hash exists, header should exist;" ) ;
144+
145+ let live_state = LiveState {
146+ uri : vec ! [ block_ws_uri. clone( ) ] ,
116147 at : Some ( hex:: encode ( header. hash ( ) . encode ( ) ) ) ,
117148 pallet : Default :: default ( ) ,
118149 hashed_prefixes : Default :: default ( ) ,
119150 child_tree : Default :: default ( ) ,
120- }
151+ } ;
152+
153+ execute_block :: < Block , HostFns > ( & shared, & command, & executor, & rpc, live_state)
154+ . await ?;
121155 }
122156 }
123- _ => {
124- unreachable ! ( "execute block currently only supports Live state" )
125- }
126- } ;
127157
158+ Ok ( ( ) )
159+ } else {
160+ let live_state = match & command. state {
161+ State :: Live ( live_state) => {
162+ // If no --at is provided, get the latest block to replay
163+ if live_state. at . is_some ( ) {
164+ live_state. clone ( )
165+ } else {
166+ let header =
167+ ChainApi :: < ( ) , Block :: Hash , Block :: Header , SignedBlock < Block > > :: header (
168+ & rpc, None ,
169+ )
170+ . await
171+ . map_err ( rpc_err_handler) ?
172+ . expect ( "header exists, block should also exist; qed" ) ;
173+ LiveState {
174+ uri : vec ! [ block_ws_uri] ,
175+ at : Some ( hex:: encode ( header. hash ( ) . encode ( ) ) ) ,
176+ pallet : Default :: default ( ) ,
177+ hashed_prefixes : Default :: default ( ) ,
178+ child_tree : Default :: default ( ) ,
179+ }
180+ }
181+ }
182+ _ => {
183+ unreachable ! ( "execute block currently only supports Live state" )
184+ }
185+ } ;
186+
187+ execute_block :: < Block , HostFns > ( & shared, & command, & executor, & rpc, live_state) . await
188+ }
189+ }
190+
191+ // Perform block execution on live state
192+ pub async fn execute_block < Block , HostFns > (
193+ shared : & SharedParams ,
194+ command : & Command ,
195+ executor : & WasmExecutor < HostFns > ,
196+ rpc : & WsClient ,
197+ live_state : LiveState ,
198+ ) -> sc_cli:: Result < ( ) >
199+ where
200+ Block : BlockT + serde:: de:: DeserializeOwned ,
201+ <Block :: Hash as FromStr >:: Err : Debug ,
202+ Block :: Hash : serde:: de:: DeserializeOwned ,
203+ Block :: Header : serde:: de:: DeserializeOwned ,
204+ <NumberFor < Block > as TryInto < u64 > >:: Error : Debug ,
205+ HostFns : HostFunctions ,
206+ {
128207 // The block we want to *execute* at is the block passed by the user
129208 let execute_at = live_state. at :: < Block > ( ) ?;
130209
@@ -137,12 +216,12 @@ where
137216 try_runtime_feature_enabled : true ,
138217 } ;
139218 let ext = State :: Live ( prev_block_live_state)
140- . to_ext :: < Block , HostFns > ( & shared, & executor, None , runtime_checks)
219+ . to_ext :: < Block , HostFns > ( shared, executor, None , runtime_checks)
141220 . await ?;
142221
143222 // Execute the desired block on top of it
144223 let block =
145- ChainApi :: < ( ) , Block :: Hash , Block :: Header , SignedBlock < Block > > :: block ( & rpc, execute_at)
224+ ChainApi :: < ( ) , Block :: Hash , Block :: Header , SignedBlock < Block > > :: block ( rpc, execute_at)
146225 . await
147226 . map_err ( rpc_err_handler) ?
148227 . expect ( "header exists, block should also exist; qed" )
@@ -161,18 +240,18 @@ where
161240 block. clone ( ) ,
162241 state_root_check,
163242 signature_check,
164- command. try_state ,
243+ command. try_state . clone ( ) ,
165244 )
166245 . encode ( ) ;
167246
168247 let _ = state_machine_call_with_proof :: < Block , HostFns > (
169248 & ext,
170249 & mut Default :: default ( ) ,
171- & executor,
250+ executor,
172251 "TryRuntime_execute_block" ,
173252 & payload,
174253 full_extensions ( executor. clone ( ) ) ,
175- shared. export_proof ,
254+ shared. export_proof . clone ( ) ,
176255 ) ?;
177256
178257 Ok ( ( ) )
0 commit comments