diff --git a/cli/src/args.rs b/cli/src/args.rs index d2d9276..1f65cb5 100644 --- a/cli/src/args.rs +++ b/cli/src/args.rs @@ -45,4 +45,13 @@ pub struct Args { /// exists. #[arg(long)] pub overwrite: bool, + + /// Set to perform serial replay of blocks. + /// + /// Slower, but forces initial state of block `n+1` consistent with final + /// state of block `n`. This is not ensured with parallel replay because + /// the final state may differ with the final state on the official + /// blockchain. + #[arg(long)] + pub serial: bool, } diff --git a/cli/src/main.rs b/cli/src/main.rs index 9d50f55..2765942 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -105,6 +105,7 @@ fn run(args: Args) -> anyhow::Result<()> { let txt_out = args.txt_out; let trace_out = args.trace_out; let overwrite = args.overwrite; + let serial = args.serial; check_file(&svg_path, overwrite)?; check_file(&txt_out, overwrite)?; @@ -117,7 +118,7 @@ fn run(args: Args) -> anyhow::Result<()> { tracing::info!(%start_block, %end_block, "Re-executing blocks"); let start_time = std::time::Instant::now(); - let visited_pcs = run_replay(&replay_range, &trace_out, &storage)?; + let visited_pcs = run_replay(&replay_range, &trace_out, &storage, serial)?; let elapsed = start_time.elapsed(); tracing::info!(?elapsed, "Finished"); diff --git a/starknet-replay/src/runner/mod.rs b/starknet-replay/src/runner/mod.rs index bd8b631..e942545 100644 --- a/starknet-replay/src/runner/mod.rs +++ b/starknet-replay/src/runner/mod.rs @@ -42,6 +42,7 @@ pub fn run_replay( replay_range: &ReplayRange, trace_out: &Option, storage: &T, + serial: bool, ) -> Result where T: Storage + Sync + Send, @@ -51,7 +52,11 @@ where // Iterate through each block in `replay_work` and replay all the // transactions - replay_blocks(storage, trace_out, &replay_work) + if !serial { + replay_blocks_parallel(storage, trace_out, &replay_work) + } else { + replay_blocks_serial(storage, trace_out, &replay_work) + } } /// Generates the list of transactions to be replayed. @@ -137,23 +142,25 @@ pub fn process_transaction_traces(transaction_simulations: Vec( +pub fn replay_blocks_parallel( storage: &T, trace_out: &Option, replay_work: &[ReplayBlock], @@ -161,7 +168,7 @@ pub fn replay_blocks( where T: Storage + Sync + Send, { - info!("Starting transactions replay"); + info!("Starting parallel blocks replay"); let (sender, receiver) = channel(); replay_work .par_iter() @@ -170,7 +177,7 @@ where |(storage, trace_out, sender), block| -> anyhow::Result<()> { let block_transaction_traces = storage.execute_block(block, trace_out)?; let block_number = BlockNumber::new(block.header.block_number.0); - info!("Simulation completed block {block_number}"); + info!("Replay completed block {block_number}"); let visited_pcs = process_transaction_traces(block_transaction_traces); sender.send(visited_pcs)?; Ok(()) @@ -187,3 +194,42 @@ where Ok(cumulative_visited_pcs) } + +/// Serially re-executes the list of blocks in `replay_work` and returns the +/// statistics on libfunc usage. +/// +/// Serial replay is slower than parallel, however it ensures state consistency +/// between initial state of block `n+1` and final state of block `n`. +/// +/// # Arguments +/// +/// - `storage`: The object to query the starknet blockchain using the RPC +/// protocol. +/// - `trace_out`: The output file of the transaction traces. +/// - `replay_work`: The list of transactions to replay grouped by block. +/// +/// # Errors +/// +/// Returns [`Err`] if the function `execute_block` fails to replay any +/// transaction. +pub fn replay_blocks_serial( + storage: &T, + trace_out: &Option, + replay_work: &[ReplayBlock], +) -> Result +where + T: Storage + Sync + Send, +{ + info!("Starting serial blocks replay"); + + let mut cumulative_visited_pcs = VisitedPcs::default(); + for block in replay_work { + let block_transaction_traces = storage.execute_block(block, trace_out)?; + let block_number = BlockNumber::new(block.header.block_number.0); + info!("Replay completed block {block_number}"); + let visited_pcs = process_transaction_traces(block_transaction_traces); + cumulative_visited_pcs.extend(visited_pcs.into_iter()); + } + + Ok(cumulative_visited_pcs) +} diff --git a/starknet-replay/tests/test_replay_blocks.rs b/starknet-replay/tests/test_replay_blocks.rs index a479768..6684598 100644 --- a/starknet-replay/tests/test_replay_blocks.rs +++ b/starknet-replay/tests/test_replay_blocks.rs @@ -12,7 +12,7 @@ use starknet_replay::block_number::BlockNumber; use starknet_replay::profiler::analysis::extract_libfuncs_weight; use starknet_replay::profiler::replay_statistics::ReplayStatistics; use starknet_replay::runner::replay_block::ReplayBlock; -use starknet_replay::runner::replay_blocks; +use starknet_replay::runner::replay_blocks_parallel; use starknet_replay::storage::rpc::RpcStorage; use starknet_replay::storage::Storage; use test_log::test; @@ -47,7 +47,7 @@ fn test_replay_blocks() { replay_work.push(replay_block); let trace_out = None; - let visited_pcs = replay_blocks(&storage, &trace_out, &replay_work).unwrap(); + let visited_pcs = replay_blocks_parallel(&storage, &trace_out, &replay_work).unwrap(); let libfunc_stats = extract_libfuncs_weight(&visited_pcs, &storage).unwrap();