diff --git a/CHANGELOG.md b/CHANGELOG.md index 1e1ca41804..9f2f757e4c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,9 +2,21 @@ #### Upcoming Changes +* feat: Add method `CairoRunner::initialize_function_runner_cairo_1` [#1151](https://github.com/lambdaclass/cairo-rs/pull/1151) + + * Add method `pub fn initialize_function_runner_cairo_1( + &mut self, + vm: &mut VirtualMachine, + program_builtins: &[BuiltinName], + ) -> Result<(), RunnerError>` to `CairoRunner` + + * BREAKING: Move field `builtins` from `SharedProgramData` to `Program` + * BREAKING: Remove argument `add_segment_arena_builtin` from `CairoRunner::initialize_function_runner`, it is now always false + * BREAKING: Add `segment_arena` enum variant to `BuiltinName` + * Fix implementation of `InitSquashData` and `ShouldSkipSquashLoop` -* Add more hints to `Cairo1HintProcessor` [#1143](https://github.com/lambdaclass/cairo-rs/pull/1098) +* Add more hints to `Cairo1HintProcessor` [#1143](https://github.com/lambdaclass/cairo-rs/pull/1143) * `Cairo1HintProcessor` can now run the following hints: * Felt252DictEntryInit diff --git a/src/serde/deserialize_program.rs b/src/serde/deserialize_program.rs index 0d9c607b67..a315566066 100644 --- a/src/serde/deserialize_program.rs +++ b/src/serde/deserialize_program.rs @@ -1,5 +1,6 @@ use crate::stdlib::{collections::HashMap, fmt, prelude::*, sync::Arc}; +use crate::vm::runners::builtin_runner::SEGMENT_ARENA_BUILTIN_NAME; use crate::{ serde::deserialize_utils, types::{ @@ -32,6 +33,7 @@ pub enum BuiltinName { bitwise, ec_op, poseidon, + segment_arena, } impl BuiltinName { @@ -45,6 +47,7 @@ impl BuiltinName { BuiltinName::bitwise => BITWISE_BUILTIN_NAME, BuiltinName::ec_op => EC_OP_BUILTIN_NAME, BuiltinName::poseidon => POSEIDON_BUILTIN_NAME, + BuiltinName::segment_arena => SEGMENT_ARENA_BUILTIN_NAME, } } } @@ -407,7 +410,6 @@ pub fn parse_program_json( } let shared_program_data = SharedProgramData { - builtins: program_json.builtins, data: program_json.data, hints: program_json.hints, main: entrypoint_pc, @@ -427,6 +429,7 @@ pub fn parse_program_json( shared_program_data: Arc::new(shared_program_data), constants, reference_manager: program_json.reference_manager, + builtins: program_json.builtins, }) } @@ -845,7 +848,7 @@ mod tests { }], ); - assert_eq!(program.shared_program_data.builtins, builtins); + assert_eq!(program.builtins, builtins); assert_eq!(program.shared_program_data.data, data); assert_eq!(program.shared_program_data.main, Some(0)); assert_eq!(program.shared_program_data.hints, hints); @@ -903,7 +906,7 @@ mod tests { }], ); - assert_eq!(program.shared_program_data.builtins, builtins); + assert_eq!(program.builtins, builtins); assert_eq!(program.shared_program_data.data, data); assert_eq!(program.shared_program_data.main, None); assert_eq!(program.shared_program_data.hints, hints); diff --git a/src/tests/mod.rs b/src/tests/mod.rs index bb853ea15c..c1c718819c 100644 --- a/src/tests/mod.rs +++ b/src/tests/mod.rs @@ -1,6 +1,7 @@ #[cfg(feature = "cairo-1-hints")] use crate::{ hint_processor::cairo_1_hint_processor::hint_processor::Cairo1HintProcessor, + serde::deserialize_program::BuiltinName, types::relocatable::MaybeRelocatable, vm::{ runners::cairo_runner::{CairoArg, CairoRunner}, @@ -23,6 +24,9 @@ use crate::{ #[cfg(target_arch = "wasm32")] use wasm_bindgen_test::*; +#[cfg(all(not(feature = "std"), feature = "alloc"))] +use alloc::{string::String, vec::Vec}; + mod bitwise_test; #[cfg(feature = "cairo-1-hints")] @@ -117,28 +121,24 @@ pub(self) fn run_cairo_1_entrypoint( .unwrap(); let mut vm = VirtualMachine::new(false); - runner.initialize_function_runner(&mut vm, true).unwrap(); - - // Get builtin bases - // Extract builtins from CasmContractClass entrypoint data from the entrypoint which's offset is being ran - let builtins: Vec = contract_class - .entry_points_by_type - .external - .iter() - .find(|e| e.offset == entrypoint_offset) - .unwrap() - .builtins - .iter() - .map(|n| format!("{}_builtin", n)) - .collect(); + let program_builtins = get_casm_contract_builtins(&contract_class, entrypoint_offset); + runner + .initialize_function_runner_cairo_1(&mut vm, &program_builtins) + .unwrap(); // Implicit Args let syscall_segment = MaybeRelocatable::from(vm.add_memory_segment()); + let builtins: Vec<&'static str> = runner + .get_program_builtins() + .iter() + .map(|b| b.name()) + .collect(); + let builtin_segment: Vec = vm .get_builtin_runners() .iter() - .filter(|b| builtins.contains(&(b.name().to_string()))) + .filter(|b| builtins.contains(&b.name())) .flat_map(|b| b.initial_stack()) .collect(); @@ -205,3 +205,36 @@ pub(self) fn run_cairo_1_entrypoint( .collect(); assert_eq!(expected_retdata, &retdata); } + +#[cfg(feature = "cairo-1-hints")] +fn get_casm_contract_builtins( + contract_class: &CasmContractClass, + entrypoint_offset: usize, +) -> Vec { + contract_class + .entry_points_by_type + .external + .iter() + .find(|e| e.offset == entrypoint_offset) + .unwrap() + .builtins + .iter() + .map(|n| format!("{}_builtin", n)) + .map(|s| match &*s { + crate::vm::runners::builtin_runner::OUTPUT_BUILTIN_NAME => BuiltinName::output, + crate::vm::runners::builtin_runner::RANGE_CHECK_BUILTIN_NAME => { + BuiltinName::range_check + } + crate::vm::runners::builtin_runner::HASH_BUILTIN_NAME => BuiltinName::pedersen, + crate::vm::runners::builtin_runner::SIGNATURE_BUILTIN_NAME => BuiltinName::ecdsa, + crate::vm::runners::builtin_runner::KECCAK_BUILTIN_NAME => BuiltinName::keccak, + crate::vm::runners::builtin_runner::BITWISE_BUILTIN_NAME => BuiltinName::bitwise, + crate::vm::runners::builtin_runner::EC_OP_BUILTIN_NAME => BuiltinName::ec_op, + crate::vm::runners::builtin_runner::POSEIDON_BUILTIN_NAME => BuiltinName::poseidon, + crate::vm::runners::builtin_runner::SEGMENT_ARENA_BUILTIN_NAME => { + BuiltinName::segment_arena + } + _ => panic!("Invalid builtin {}", s), + }) + .collect() +} diff --git a/src/types/program.rs b/src/types/program.rs index 26750a179f..a9677e4259 100644 --- a/src/types/program.rs +++ b/src/types/program.rs @@ -39,7 +39,6 @@ use std::path::Path; // Fields in `Program` (other than `SharedProgramData` itself) are used by the main logic. #[derive(Clone, Default, Debug, PartialEq, Eq)] pub(crate) struct SharedProgramData { - pub(crate) builtins: Vec, pub(crate) data: Vec, pub(crate) hints: HashMap>, pub(crate) main: Option, @@ -55,6 +54,7 @@ pub(crate) struct SharedProgramData { pub struct Program { pub(crate) shared_program_data: Arc, pub(crate) constants: HashMap, + pub(crate) builtins: Vec, pub(crate) reference_manager: ReferenceManager, } @@ -81,7 +81,6 @@ impl Program { } } let shared_program_data = SharedProgramData { - builtins, data, hints, main, @@ -95,6 +94,7 @@ impl Program { shared_program_data: Arc::new(shared_program_data), constants, reference_manager, + builtins, }) } @@ -114,7 +114,7 @@ impl Program { } pub fn iter_builtins(&self) -> impl Iterator { - self.shared_program_data.builtins.iter() + self.builtins.iter() } pub fn iter_data(&self) -> impl Iterator { @@ -145,6 +145,7 @@ impl Default for Program { reference_manager: ReferenceManager { references: Vec::new(), }, + builtins: Vec::new(), } } } @@ -235,7 +236,7 @@ mod tests { ) .unwrap(); - assert_eq!(program.shared_program_data.builtins, builtins); + assert_eq!(program.builtins, builtins); assert_eq!(program.shared_program_data.data, data); assert_eq!(program.shared_program_data.main, None); assert_eq!(program.shared_program_data.identifiers, HashMap::new()); @@ -297,7 +298,7 @@ mod tests { ) .unwrap(); - assert_eq!(program.shared_program_data.builtins, builtins); + assert_eq!(program.builtins, builtins); assert_eq!(program.shared_program_data.data, data); assert_eq!(program.shared_program_data.main, None); assert_eq!(program.shared_program_data.identifiers, identifiers); @@ -685,7 +686,7 @@ mod tests { }, ); - assert_eq!(program.shared_program_data.builtins, builtins); + assert_eq!(program.builtins, builtins); assert_eq!(program.shared_program_data.data, data); assert_eq!(program.shared_program_data.main, Some(0)); assert_eq!(program.shared_program_data.identifiers, identifiers); @@ -784,7 +785,7 @@ mod tests { }, ); - assert_eq!(program.shared_program_data.builtins, builtins); + assert_eq!(program.builtins, builtins); assert_eq!(program.shared_program_data.data, data); assert_eq!(program.shared_program_data.main, None); assert_eq!(program.shared_program_data.identifiers, identifiers); @@ -837,7 +838,6 @@ mod tests { #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn default_program() { let shared_program_data = SharedProgramData { - builtins: Vec::new(), data: Vec::new(), hints: HashMap::new(), main: None, @@ -853,6 +853,7 @@ mod tests { reference_manager: ReferenceManager { references: Vec::new(), }, + builtins: Vec::new(), }; assert_eq!(program, Program::default()); diff --git a/src/utils.rs b/src/utils.rs index 9b222fd2bc..9c6b25a4ef 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -257,7 +257,6 @@ pub mod test_utils { //Program with builtins ( $( $builtin_name: expr ),* ) => {{ let shared_program_data = SharedProgramData { - builtins: vec![$( $builtin_name ),*], data: crate::stdlib::vec::Vec::new(), hints: crate::stdlib::collections::HashMap::new(), main: None, @@ -270,43 +269,97 @@ pub mod test_utils { Program { shared_program_data: Arc::new(shared_program_data), constants: crate::stdlib::collections::HashMap::new(), + builtins: vec![$( $builtin_name ),*], reference_manager: ReferenceManager { references: crate::stdlib::vec::Vec::new(), }, } }}; - // Custom program definition - ($(constants = $value:expr),* $(,)?) => { - Program { - $( - constants: $value, - )* - ..Default::default() - } - }; - ($(refence_manager = $value:expr),* $(,)?) => { - Program { - $( - reference_manager: $value, - )* - ..Default::default() - } - }; ($($field:ident = $value:expr),* $(,)?) => {{ - let shared_data = SharedProgramData { + + let program_flat = crate::utils::test_utils::ProgramFlat { $( $field: $value, )* ..Default::default() }; - Program { - shared_program_data: Arc::new(shared_data), - ..Default::default() - } + + Into::::into(program_flat) }}; } + pub(crate) use program; + pub(crate) struct ProgramFlat { + pub(crate) data: crate::utils::Vec, + pub(crate) hints: crate::stdlib::collections::HashMap< + usize, + crate::utils::Vec, + >, + pub(crate) main: Option, + //start and end labels will only be used in proof-mode + pub(crate) start: Option, + pub(crate) end: Option, + pub(crate) error_message_attributes: + crate::utils::Vec, + pub(crate) instruction_locations: Option< + crate::stdlib::collections::HashMap< + usize, + crate::serde::deserialize_program::InstructionLocation, + >, + >, + pub(crate) identifiers: crate::stdlib::collections::HashMap< + crate::stdlib::string::String, + crate::serde::deserialize_program::Identifier, + >, + pub(crate) constants: crate::stdlib::collections::HashMap< + crate::stdlib::string::String, + crate::utils::Felt252, + >, + pub(crate) builtins: crate::utils::Vec, + pub(crate) reference_manager: crate::serde::deserialize_program::ReferenceManager, + } + + impl Default for ProgramFlat { + fn default() -> Self { + Self { + data: Default::default(), + hints: Default::default(), + main: Default::default(), + start: Default::default(), + end: Default::default(), + error_message_attributes: Default::default(), + instruction_locations: Default::default(), + identifiers: Default::default(), + constants: Default::default(), + builtins: Default::default(), + reference_manager: crate::serde::deserialize_program::ReferenceManager { + references: crate::utils::Vec::new(), + }, + } + } + } + + impl From for Program { + fn from(val: ProgramFlat) -> Self { + Program { + shared_program_data: Arc::new(SharedProgramData { + data: val.data, + hints: val.hints, + main: val.main, + start: val.start, + end: val.end, + error_message_attributes: val.error_message_attributes, + instruction_locations: val.instruction_locations, + identifiers: val.identifiers, + }), + constants: val.constants, + builtins: val.builtins, + reference_manager: val.reference_manager, + } + } + } + macro_rules! vm { () => {{ VirtualMachine::new(false) @@ -865,7 +918,6 @@ mod test { #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn program_macro() { let shared_data = SharedProgramData { - builtins: Vec::new(), data: Vec::new(), hints: HashMap::new(), main: None, @@ -881,6 +933,7 @@ mod test { reference_manager: ReferenceManager { references: Vec::new(), }, + builtins: Vec::new(), }; assert_eq!(program, program!()) } @@ -889,7 +942,6 @@ mod test { #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn program_macro_with_builtin() { let shared_data = SharedProgramData { - builtins: vec![BuiltinName::range_check], data: Vec::new(), hints: HashMap::new(), main: None, @@ -905,6 +957,7 @@ mod test { reference_manager: ReferenceManager { references: Vec::new(), }, + builtins: vec![BuiltinName::range_check], }; assert_eq!(program, program![BuiltinName::range_check]) @@ -914,7 +967,6 @@ mod test { #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn program_macro_custom_definition() { let shared_data = SharedProgramData { - builtins: vec![BuiltinName::range_check], data: Vec::new(), hints: HashMap::new(), main: Some(2), @@ -930,6 +982,7 @@ mod test { reference_manager: ReferenceManager { references: Vec::new(), }, + builtins: vec![BuiltinName::range_check], }; assert_eq!( diff --git a/src/vm/runners/cairo_runner.rs b/src/vm/runners/cairo_runner.rs index 428e7d5ae4..93e01a0b01 100644 --- a/src/vm/runners/cairo_runner.rs +++ b/src/vm/runners/cairo_runner.rs @@ -149,14 +149,10 @@ impl CairoRunner { BuiltinName::keccak, BuiltinName::poseidon, ]; - if !is_subsequence( - &self.program.shared_program_data.builtins, - &builtin_ordered_list, - ) { + if !is_subsequence(&self.program.builtins, &builtin_ordered_list) { return Err(RunnerError::DisorderedBuiltins); }; - let mut program_builtins: HashSet<&BuiltinName> = - self.program.shared_program_data.builtins.iter().collect(); + let mut program_builtins: HashSet<&BuiltinName> = self.program.builtins.iter().collect(); let mut builtin_runners = Vec::::new(); if self.layout.builtins.output { @@ -251,7 +247,11 @@ impl CairoRunner { BuiltinName::poseidon, ]; - fn initialize_builtin(name: BuiltinName, vm: &mut VirtualMachine) { + fn initialize_builtin( + name: BuiltinName, + vm: &mut VirtualMachine, + add_segment_arena_builtin: bool, + ) { match name { BuiltinName::pedersen => vm .builtin_runners @@ -278,26 +278,23 @@ impl CairoRunner { BuiltinName::poseidon => vm .builtin_runners .push(PoseidonBuiltinRunner::new(Some(1), true).into()), + BuiltinName::segment_arena => { + if add_segment_arena_builtin { + vm.builtin_runners + .push(SegmentArenaBuiltinRunner::new(true).into()) + } + } } } - for builtin_name in &self.program.shared_program_data.builtins { - initialize_builtin(*builtin_name, vm); + for builtin_name in &self.program.builtins { + initialize_builtin(*builtin_name, vm, add_segment_arena_builtin); } for builtin_name in starknet_preset_builtins { - if !self - .program - .shared_program_data - .builtins - .contains(&builtin_name) - { - initialize_builtin(builtin_name, vm) + if !self.program.builtins.contains(&builtin_name) { + initialize_builtin(builtin_name, vm, add_segment_arena_builtin) } } - if add_segment_arena_builtin { - vm.builtin_runners - .push(SegmentArenaBuiltinRunner::new(true).into()) - } Ok(()) } @@ -513,7 +510,7 @@ impl CairoRunner { } pub fn get_program_builtins(&self) -> &Vec { - &self.program.shared_program_data.builtins + &self.program.builtins } pub fn run_until_pc( @@ -969,12 +966,30 @@ impl CairoRunner { Ok(()) } + /// Intitializes the runner in order to run cairo 1 contract entrypoints + /// Initializes builtins & segments + /// All builtins are initialized regardless of use + /// Swaps the program's builtins field with program_builtins + /// Adds the segment_arena builtin if present in the program_builtins + pub fn initialize_function_runner_cairo_1( + &mut self, + vm: &mut VirtualMachine, + program_builtins: &[BuiltinName], + ) -> Result<(), RunnerError> { + self.program.builtins = program_builtins.to_vec(); + self.initialize_all_builtins(vm, true)?; + self.initialize_segments(vm, self.program_base); + Ok(()) + } + + /// Intitializes the runner in order to run cairo 0 contract entrypoints + /// Initializes builtins & segments + /// All builtins are initialized regardless of use pub fn initialize_function_runner( &mut self, vm: &mut VirtualMachine, - add_segment_arena_builtin: bool, ) -> Result<(), RunnerError> { - self.initialize_all_builtins(vm, add_segment_arena_builtin)?; + self.initialize_all_builtins(vm, false)?; self.initialize_segments(vm, self.program_base); Ok(()) } @@ -3955,7 +3970,8 @@ mod tests { let program = program![ BuiltinName::pedersen, BuiltinName::range_check, - BuiltinName::ecdsa + BuiltinName::ecdsa, + BuiltinName::segment_arena ]; let cairo_runner = cairo_runner!(program); @@ -3970,11 +3986,11 @@ mod tests { assert_eq!(given_output[0].name(), HASH_BUILTIN_NAME); assert_eq!(given_output[1].name(), RANGE_CHECK_BUILTIN_NAME); assert_eq!(given_output[2].name(), SIGNATURE_BUILTIN_NAME); - assert_eq!(given_output[3].name(), OUTPUT_BUILTIN_NAME); - assert_eq!(given_output[4].name(), BITWISE_BUILTIN_NAME); - assert_eq!(given_output[5].name(), EC_OP_BUILTIN_NAME); - assert_eq!(given_output[6].name(), KECCAK_BUILTIN_NAME); - assert_eq!(given_output[8].name(), SEGMENT_ARENA_BUILTIN_NAME); + assert_eq!(given_output[3].name(), SEGMENT_ARENA_BUILTIN_NAME); + assert_eq!(given_output[4].name(), OUTPUT_BUILTIN_NAME); + assert_eq!(given_output[5].name(), BITWISE_BUILTIN_NAME); + assert_eq!(given_output[6].name(), EC_OP_BUILTIN_NAME); + assert_eq!(given_output[7].name(), KECCAK_BUILTIN_NAME); } #[test] @@ -3986,7 +4002,7 @@ mod tests { let mut vm = vm!(); cairo_runner - .initialize_function_runner(&mut vm, false) + .initialize_function_runner(&mut vm) .expect("initialize_function_runner failed."); let builtin_runners = vm.get_builtin_runners(); @@ -4026,20 +4042,20 @@ mod tests { let mut vm = vm!(); cairo_runner - .initialize_function_runner(&mut vm, true) + .initialize_function_runner_cairo_1(&mut vm, &[BuiltinName::segment_arena]) .expect("initialize_function_runner failed."); let builtin_runners = vm.get_builtin_runners(); - assert_eq!(builtin_runners[0].name(), HASH_BUILTIN_NAME); - assert_eq!(builtin_runners[1].name(), RANGE_CHECK_BUILTIN_NAME); - assert_eq!(builtin_runners[2].name(), OUTPUT_BUILTIN_NAME); - assert_eq!(builtin_runners[3].name(), SIGNATURE_BUILTIN_NAME); - assert_eq!(builtin_runners[4].name(), BITWISE_BUILTIN_NAME); - assert_eq!(builtin_runners[5].name(), EC_OP_BUILTIN_NAME); - assert_eq!(builtin_runners[6].name(), KECCAK_BUILTIN_NAME); - assert_eq!(builtin_runners[7].name(), POSEIDON_BUILTIN_NAME); - assert_eq!(builtin_runners[8].name(), SEGMENT_ARENA_BUILTIN_NAME); + assert_eq!(builtin_runners[0].name(), SEGMENT_ARENA_BUILTIN_NAME); + assert_eq!(builtin_runners[1].name(), HASH_BUILTIN_NAME); + assert_eq!(builtin_runners[2].name(), RANGE_CHECK_BUILTIN_NAME); + assert_eq!(builtin_runners[3].name(), OUTPUT_BUILTIN_NAME); + assert_eq!(builtin_runners[4].name(), SIGNATURE_BUILTIN_NAME); + assert_eq!(builtin_runners[5].name(), BITWISE_BUILTIN_NAME); + assert_eq!(builtin_runners[6].name(), EC_OP_BUILTIN_NAME); + assert_eq!(builtin_runners[7].name(), KECCAK_BUILTIN_NAME); + assert_eq!(builtin_runners[8].name(), POSEIDON_BUILTIN_NAME); assert_eq!( cairo_runner.program_base, @@ -4157,10 +4173,7 @@ mod tests { ); let runner = cairo_runner!(program); - assert_eq!( - &program.shared_program_data.builtins, - runner.get_program_builtins() - ); + assert_eq!(&program.builtins, runner.get_program_builtins()); } #[test] @@ -4541,9 +4554,7 @@ mod tests { .pc .unwrap(); - cairo_runner - .initialize_function_runner(&mut vm, false) - .unwrap(); + cairo_runner.initialize_function_runner(&mut vm).unwrap(); assert!(cairo_runner .run_from_entrypoint(