From 45bf5c8897882e39bb5e23f9559bd72f6400c90e Mon Sep 17 00:00:00 2001 From: Pedro Fontana Date: Mon, 29 May 2023 12:40:35 -0300 Subject: [PATCH] CairoRunner.run_until_pc_with_steps_limit (#1181) * Add CairoRunner.run_until_pc_with_steps_limit * Add unit test * Add integration test * Minor API change * Update CHANGELOG.md * Add doc * change error msg --- CHANGELOG.md | 2 + src/vm/errors/vm_errors.rs | 2 + src/vm/runners/cairo_runner.rs | 147 +++++++++++++++++++++++++++++++++ 3 files changed, 151 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3eba913919..9a61dd69dc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ #### Upcoming Changes +* Add `CairoRunner::run_until_pc_with_steps_limit method` [#1181](https://github.com/lambdaclass/cairo-rs/pull/1181) + * fix: felt_from_number not properly returning parse errors [#1012](https://github.com/lambdaclass/cairo-rs/pull/1012) * fix: Fix felt sqrt and Signed impl [#1150](https://github.com/lambdaclass/cairo-rs/pull/1150) diff --git a/src/vm/errors/vm_errors.rs b/src/vm/errors/vm_errors.rs index 432d1a3d1c..1cd5e35769 100644 --- a/src/vm/errors/vm_errors.rs +++ b/src/vm/errors/vm_errors.rs @@ -93,6 +93,8 @@ pub enum VirtualMachineError { NoImm, #[error("Execution reached the end of the program. Requested remaining steps: {0}.")] EndOfProgram(usize), + #[error("Could not reach the end of the program. Executed steps: {0}.")] + StepsLimit(u64), #[error(transparent)] TracerError(#[from] TraceError), #[error(transparent)] diff --git a/src/vm/runners/cairo_runner.rs b/src/vm/runners/cairo_runner.rs index 93e01a0b01..a919d59b5a 100644 --- a/src/vm/runners/cairo_runner.rs +++ b/src/vm/runners/cairo_runner.rs @@ -534,6 +534,40 @@ impl CairoRunner { Ok(()) } + /// Runs the self.program until it completes execution or reaches the steps_limit. + /// If the execution reaches the steps_limit without completing the execution + /// returns an error + pub fn run_until_pc_with_steps_limit( + &mut self, + address: Relocatable, + steps_limit: u64, + vm: &mut VirtualMachine, + hint_processor: &mut dyn HintProcessor, + ) -> Result<(), VirtualMachineError> { + let references = self.get_reference_list(); + let hint_data_dictionary = self.get_hint_data_dictionary(&references, hint_processor)?; + #[cfg(feature = "hooks")] + vm.execute_before_first_step(self, &hint_data_dictionary)?; + + if vm.run_context.pc == address { + return Ok(()); + } + + for _ in 0..steps_limit { + vm.step( + hint_processor, + &mut self.exec_scopes, + &hint_data_dictionary, + &self.program.constants, + )?; + + if vm.run_context.pc == address { + return Ok(()); + } + } + Err(VirtualMachineError::StepsLimit(steps_limit)) + } + /// Execute an exact number of steps on the program from the actual position. pub fn run_for_steps( &mut self, @@ -4853,4 +4887,117 @@ mod tests { assert_eq!(runner.get_program().data_len(), 2) } + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + fn test_run_until_pc_with_steps_limit() { + let program = Program::from_bytes( + include_bytes!("../../../cairo_programs/fibonacci.json"), + Some("main"), + ) + .unwrap(); + let mut runner = cairo_runner!(program); + let mut vm = vm!(); + let end = runner.initialize(&mut vm).unwrap(); + + // program takes 80 steps + assert_matches!( + runner + .run_until_pc_with_steps_limit(end, 20, &mut vm, &mut BuiltinHintProcessor::new_empty()), + Err(VirtualMachineError::StepsLimit(x)) if x == 20 + ); + assert_matches!( + runner + .run_until_pc_with_steps_limit(end, 20, &mut vm, &mut BuiltinHintProcessor::new_empty()), + Err(VirtualMachineError::StepsLimit(x)) if x == 20 + ); + assert_matches!( + runner + .run_until_pc_with_steps_limit(end, 20, &mut vm, &mut BuiltinHintProcessor::new_empty()), + Err(VirtualMachineError::StepsLimit(x)) if x == 20 + ); + assert_matches!( + runner.run_until_pc_with_steps_limit( + end, + 20, + &mut vm, + &mut BuiltinHintProcessor::new_empty(), + ), + Ok(()) + ); + + assert_matches!( + runner.run_until_pc_with_steps_limit( + end, + 0, + &mut vm, + &mut BuiltinHintProcessor::new_empty(), + ), + Ok(()) + ); + } + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + fn test_steps_limit_fibonacci_error() { + let program = Program::from_bytes( + include_bytes!("../../../cairo_programs/fibonacci.json"), + Some("main"), + ) + .unwrap(); + let mut runner = cairo_runner!(program); + let mut vm = vm!(); + let end = runner.initialize(&mut vm).unwrap(); + + // program takes 80 steps + assert_matches!( + runner + .run_until_pc_with_steps_limit(end, 0, &mut vm, &mut BuiltinHintProcessor::new_empty()), + Err(VirtualMachineError::StepsLimit(x)) if x == 0 + ) + } + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + fn test_steps_limit_fibonacci_error_2() { + let program = Program::from_bytes( + include_bytes!("../../../cairo_programs/fibonacci.json"), + Some("main"), + ) + .unwrap(); + let mut runner = cairo_runner!(program); + let mut vm = vm!(); + let end = runner.initialize(&mut vm).unwrap(); + + // program takes 80 steps + assert_matches!( + runner + .run_until_pc_with_steps_limit(end, 79, &mut vm, &mut BuiltinHintProcessor::new_empty()), + Err(VirtualMachineError::StepsLimit(x)) if x == 79 + ) + } + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + fn test_steps_limit_fibonacci_ok() { + let program = Program::from_bytes( + include_bytes!("../../../cairo_programs/fibonacci.json"), + Some("main"), + ) + .unwrap(); + let mut runner = cairo_runner!(program); + let mut vm = vm!(); + let end = runner.initialize(&mut vm).unwrap(); + + // program takes 80 steps + assert_matches!( + runner.run_until_pc_with_steps_limit( + end, + 80, + &mut vm, + &mut BuiltinHintProcessor::new_empty(), + ), + Ok(()) + ) + } }