diff --git a/CHANGELOG.md b/CHANGELOG.md index bcb69595e9..b873eb5471 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ #### Upcoming Changes +* feat: Allow running instructions from pcs outside the program segement [#1493](https://github.com/lambdaclass/cairo-vm/pull/14923) + * feat: Add HintProcessor::execute_hint_extensive + refactor hint_ranges [#1491](https://github.com/lambdaclass/cairo-vm/pull/1491) * Add trait method `HintProcessorLogic::execute_hint_extensive`: diff --git a/fuzzer/Cargo.lock b/fuzzer/Cargo.lock index 3fd0694e0e..5c99acc4da 100644 --- a/fuzzer/Cargo.lock +++ b/fuzzer/Cargo.lock @@ -118,7 +118,8 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "bincode" version = "2.0.0-rc.3" -source = "git+https://github.com/bincode-org/bincode.git?tag=v2.0.0-rc.3#aada4bb4cb457677a4b8e47572ae7ca8dd44927c" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f11ea1a0346b94ef188834a65c068a03aec181c94896d481d7a0a40d85b0ce95" dependencies = [ "serde", ] @@ -185,7 +186,7 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] name = "cairo-felt" -version = "0.8.7" +version = "0.9.1" dependencies = [ "arbitrary", "lazy_static", @@ -198,7 +199,7 @@ dependencies = [ [[package]] name = "cairo-vm" -version = "0.8.7" +version = "0.9.1" dependencies = [ "anyhow", "arbitrary", @@ -997,9 +998,9 @@ checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" [[package]] name = "starknet-crypto" -version = "0.5.1" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "693e6362f150f9276e429a910481fb7f3bcb8d6aa643743f587cfece0b374874" +checksum = "33c03f5ac70f9b067f48db7d2d70bdf18ee0f731e8192b6cfa679136becfcdb0" dependencies = [ "crypto-bigint", "hex", @@ -1010,7 +1011,7 @@ dependencies = [ "rfc6979", "sha2", "starknet-crypto-codegen", - "starknet-curve 0.3.0", + "starknet-curve", "starknet-ff", "zeroize", ] @@ -1021,20 +1022,11 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af6527b845423542c8a16e060ea1bc43f67229848e7cd4c4d80be994a84220ce" dependencies = [ - "starknet-curve 0.4.0", + "starknet-curve", "starknet-ff", "syn 2.0.28", ] -[[package]] -name = "starknet-curve" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "252610baff59e4c4332ce3569f7469c5d3f9b415a2240d698fb238b2b4fc0942" -dependencies = [ - "starknet-ff", -] - [[package]] name = "starknet-curve" version = "0.4.0" diff --git a/vm/src/vm/vm_core.rs b/vm/src/vm/vm_core.rs index bfe5ffea65..8e18713eb1 100644 --- a/vm/src/vm/vm_core.rs +++ b/vm/src/vm/vm_core.rs @@ -475,27 +475,41 @@ impl VirtualMachine { } pub fn step_instruction(&mut self) -> Result<(), VirtualMachineError> { - let pc = self.run_context.pc.offset; + if self.run_context.pc.segment_index == 0 { + // Run instructions from program segment, using instruction cache + let pc = self.run_context.pc.offset; - if self.segments.memory.data[0].len() <= pc { - return Err(MemoryError::UnknownMemoryCell(Box::new((0, pc).into())))?; - } + if self.segments.memory.data[0].len() <= pc { + return Err(MemoryError::UnknownMemoryCell(Box::new((0, pc).into())))?; + } - let mut inst_cache = core::mem::take(&mut self.instruction_cache); - inst_cache.resize((pc + 1).max(inst_cache.len()), None); + let mut inst_cache = core::mem::take(&mut self.instruction_cache); + inst_cache.resize((pc + 1).max(inst_cache.len()), None); - let instruction = inst_cache.get_mut(pc).unwrap(); - if instruction.is_none() { - *instruction = Some(self.decode_current_instruction()?); - } - let instruction = instruction.as_ref().unwrap(); - if !self.skip_instruction_execution { - self.run_instruction(instruction)?; + let instruction = inst_cache.get_mut(pc).unwrap(); + if instruction.is_none() { + *instruction = Some(self.decode_current_instruction()?); + } + let instruction = instruction.as_ref().unwrap(); + + if !self.skip_instruction_execution { + self.run_instruction(instruction)?; + } else { + self.run_context.pc += instruction.size(); + self.skip_instruction_execution = false; + } + self.instruction_cache = inst_cache; } else { - self.run_context.pc += instruction.size(); - self.skip_instruction_execution = false; + // Run instructions from programs loaded in other segments, without instruction cache + let instruction = self.decode_current_instruction()?; + + if !self.skip_instruction_execution { + self.run_instruction(&instruction)?; + } else { + self.run_context.pc += instruction.size(); + self.skip_instruction_execution = false; + } } - self.instruction_cache = inst_cache; Ok(()) } @@ -4301,4 +4315,180 @@ mod tests { .is_err()); } } + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + /// Test for a simple program execution + /// Used program code: + /// func main(): + /// let a = 1 + /// let b = 2 + /// let c = a + b + /// return() + /// end + /// Memory taken from original vm + /// {RelocatableValue(segment_index=0, offset=0): 2345108766317314046, + /// RelocatableValue(segment_index=1, offset=0): RelocatableValue(segment_index=2, offset=0), + /// RelocatableValue(segment_index=1, offset=1): RelocatableValue(segment_index=3, offset=0)} + /// Current register values: + /// AP 1:2 + /// FP 1:2 + /// PC 0:0 + fn test_step_for_preset_memory_program_loaded_into_user_segment() { + let mut vm = vm!(true); + + let mut hint_processor = BuiltinHintProcessor::new_empty(); + + run_context!(vm, 0, 2, 2); + + vm.segments = segments![ + ((2, 0), 2345108766317314046_u64), // Load program into new segment + ((1, 0), (2, 0)), + ((1, 1), (3, 0)) + ]; + // set starting pc on new segemt to run loaded program + vm.run_context.pc.segment_index = 2; + + assert_matches!( + vm.step( + &mut hint_processor, + exec_scopes_ref!(), + &mut Vec::new(), + &mut HashMap::new(), + &HashMap::new() + ), + Ok(()) + ); + let trace = vm.trace.unwrap(); + trace_check(&trace, &[((2, 0).into(), 2, 2)]); + + assert_eq!(vm.run_context.pc, Relocatable::from((3, 0))); + assert_eq!(vm.run_context.ap, 2); + assert_eq!(vm.run_context.fp, 0); + + //Check that the following addresses have been accessed: + // Addresses have been copied from python execution: + let mem = vm.segments.memory.data; + assert!(mem[1][0].as_ref().unwrap().is_accessed()); + assert!(mem[1][1].as_ref().unwrap().is_accessed()); + } + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + /* + Test for a simple program execution + Used program code: + func myfunc(a: felt) -> (r: felt): + let b = a * 2 + return(b) + end + func main(): + let a = 1 + let b = myfunc(a) + return() + end + Memory taken from original vm: + {RelocatableValue(segment_index=0, offset=0): 5207990763031199744, + RelocatableValue(segment_index=0, offset=1): 2, + RelocatableValue(segment_index=0, offset=2): 2345108766317314046, + RelocatableValue(segment_index=0, offset=3): 5189976364521848832, + RelocatableValue(segment_index=0, offset=4): 1, + RelocatableValue(segment_index=0, offset=5): 1226245742482522112, + RelocatableValue(segment_index=0, offset=6): 3618502788666131213697322783095070105623107215331596699973092056135872020476, + RelocatableValue(segment_index=0, offset=7): 2345108766317314046, + RelocatableValue(segment_index=1, offset=0): RelocatableValue(segment_index=2, offset=0), + RelocatableValue(segment_index=1, offset=1): RelocatableValue(segment_index=3, offset=0)} + Current register values: + AP 1:2 + FP 1:2 + PC 0:3 + Final Pc (not executed): 3:0 + This program consists of 5 steps + */ + fn test_step_for_preset_memory_function_call_program_loaded_into_user_segment() { + let mut vm = vm!(true); + + run_context!(vm, 3, 2, 2); + // set starting pc on new segemt to run loaded program + vm.run_context.pc.segment_index = 4; + + //Insert values into memory + vm.segments.memory = + memory![ + // Load program into new segment + ((4, 0), 5207990763031199744_i64), + ((4, 1), 2), + ((4, 2), 2345108766317314046_i64), + ((4, 3), 5189976364521848832_i64), + ((4, 4), 1), + ((4, 5), 1226245742482522112_i64), + ( + (4, 6), + ("3618502788666131213697322783095070105623107215331596699973092056135872020476",10) + ), + ((4, 7), 2345108766317314046_i64), + ((1, 0), (2, 0)), + ((1, 1), (3, 0)) + ]; + + let final_pc = Relocatable::from((3, 0)); + let mut hint_processor = BuiltinHintProcessor::new_empty(); + //Run steps + while vm.run_context.pc != final_pc { + assert_matches!( + vm.step( + &mut hint_processor, + exec_scopes_ref!(), + &mut Vec::new(), + &mut HashMap::new(), + &HashMap::new() + ), + Ok(()) + ); + } + + //Check final register values + assert_eq!(vm.run_context.pc, Relocatable::from((3, 0))); + + assert_eq!(vm.run_context.ap, 6); + + assert_eq!(vm.run_context.fp, 0); + //Check each TraceEntry in trace + let trace = vm.trace.unwrap(); + assert_eq!(trace.len(), 5); + trace_check( + &trace, + &[ + ((4, 3).into(), 2, 2), + ((4, 5).into(), 3, 2), + ((4, 0).into(), 5, 5), + ((4, 2).into(), 6, 5), + ((4, 7).into(), 6, 2), + ], + ); + //Check that the following addresses have been accessed: + // Addresses have been copied from python execution: + let mem = &vm.segments.memory.data; + assert!(mem[4][1].as_ref().unwrap().is_accessed()); + assert!(mem[4][4].as_ref().unwrap().is_accessed()); + assert!(mem[4][6].as_ref().unwrap().is_accessed()); + assert!(mem[1][0].as_ref().unwrap().is_accessed()); + assert!(mem[1][1].as_ref().unwrap().is_accessed()); + assert!(mem[1][2].as_ref().unwrap().is_accessed()); + assert!(mem[1][3].as_ref().unwrap().is_accessed()); + assert!(mem[1][4].as_ref().unwrap().is_accessed()); + assert!(mem[1][5].as_ref().unwrap().is_accessed()); + assert_eq!( + vm.segments + .memory + .get_amount_of_accessed_addresses_for_segment(4), + Some(3) + ); + assert_eq!( + vm.segments + .memory + .get_amount_of_accessed_addresses_for_segment(1), + Some(6) + ); + } }