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,104 @@ 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+ let _ =
154+ execute_block :: < Block , HostFns > ( & shared, & command, & executor, & rpc, live_state)
155+ . await ;
121156 }
122157 }
123- _ => {
124- unreachable ! ( "execute block currently only supports Live state" )
125- }
126- } ;
127158
159+ Ok ( ( ) )
160+ } else {
161+ let live_state = match & command. state {
162+ State :: Live ( live_state) => {
163+ // If no --at is provided, get the latest block to replay
164+ if live_state. at . is_some ( ) {
165+ live_state. clone ( )
166+ } else {
167+ let header =
168+ ChainApi :: < ( ) , Block :: Hash , Block :: Header , SignedBlock < Block > > :: header (
169+ & rpc, None ,
170+ )
171+ . await
172+ . map_err ( rpc_err_handler) ?
173+ . expect ( "header exists, block should also exist; qed" ) ;
174+ LiveState {
175+ uri : vec ! [ block_ws_uri] ,
176+ at : Some ( hex:: encode ( header. hash ( ) . encode ( ) ) ) ,
177+ pallet : Default :: default ( ) ,
178+ hashed_prefixes : Default :: default ( ) ,
179+ child_tree : Default :: default ( ) ,
180+ }
181+ }
182+ }
183+ _ => {
184+ unreachable ! ( "execute block currently only supports Live state" )
185+ }
186+ } ;
187+
188+ execute_block :: < Block , HostFns > ( & shared, & command, & executor, & rpc, live_state) . await
189+ }
190+ }
191+
192+ // Perform block execution on live state
193+ pub async fn execute_block < Block , HostFns > (
194+ shared : & SharedParams ,
195+ command : & Command ,
196+ executor : & WasmExecutor < HostFns > ,
197+ rpc : & WsClient ,
198+ live_state : LiveState ,
199+ ) -> sc_cli:: Result < ( ) >
200+ where
201+ Block : BlockT + serde:: de:: DeserializeOwned ,
202+ <Block :: Hash as FromStr >:: Err : Debug ,
203+ Block :: Hash : serde:: de:: DeserializeOwned ,
204+ Block :: Header : serde:: de:: DeserializeOwned ,
205+ <NumberFor < Block > as TryInto < u64 > >:: Error : Debug ,
206+ HostFns : HostFunctions ,
207+ {
128208 // The block we want to *execute* at is the block passed by the user
129209 let execute_at = live_state. at :: < Block > ( ) ?;
130210
@@ -137,12 +217,12 @@ where
137217 try_runtime_feature_enabled : true ,
138218 } ;
139219 let ext = State :: Live ( prev_block_live_state)
140- . to_ext :: < Block , HostFns > ( & shared, & executor, None , runtime_checks)
220+ . to_ext :: < Block , HostFns > ( shared, executor, None , runtime_checks)
141221 . await ?;
142222
143223 // Execute the desired block on top of it
144224 let block =
145- ChainApi :: < ( ) , Block :: Hash , Block :: Header , SignedBlock < Block > > :: block ( & rpc, execute_at)
225+ ChainApi :: < ( ) , Block :: Hash , Block :: Header , SignedBlock < Block > > :: block ( rpc, execute_at)
146226 . await
147227 . map_err ( rpc_err_handler) ?
148228 . expect ( "header exists, block should also exist; qed" )
@@ -161,18 +241,18 @@ where
161241 block. clone ( ) ,
162242 state_root_check,
163243 signature_check,
164- command. try_state ,
244+ command. try_state . clone ( ) ,
165245 )
166246 . encode ( ) ;
167247
168248 let _ = state_machine_call_with_proof :: < Block , HostFns > (
169249 & ext,
170250 & mut Default :: default ( ) ,
171- & executor,
251+ executor,
172252 "TryRuntime_execute_block" ,
173253 & payload,
174254 full_extensions ( executor. clone ( ) ) ,
175- shared. export_proof ,
255+ shared. export_proof . clone ( ) ,
176256 ) ?;
177257
178258 Ok ( ( ) )
0 commit comments