diff --git a/CHANGELOG.md b/CHANGELOG.md index abb4be7a03..79e66243f3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,11 @@ #### Upcoming Changes +* BREAKING CHANGE: move `Program::identifiers` to `SharedProgramData::identifiers` [#1023](https://github.com/lambdaclass/cairo-rs/pull/1023) + * Optimizes `CairoRunner::new`, needed for sequencers and other workflows reusing the same `Program` instance across `CairoRunner`s + * Breaking change: make all fields in `Program` and `SharedProgramData` `pub(crate)`, since we break by moving the field let's make it the last break for this struct + * Add `Program::get_identifier(&self, id: &str) -> &Identifier` to get a single identifier by name + * Implement hints on field_arithmetic lib[#985](https://github.com/lambdaclass/cairo-rs/pull/983) `BuiltinHintProcessor` now supports the following hint: @@ -77,7 +82,7 @@ ids.res.high = res_split[1] ``` -* Add methor `Program::data_len(&self) -> usize` to get the number of data cells in a given program [#1022](https://github.com/lambdaclass/cairo-rs/pull/1022) +* Add method `Program::data_len(&self) -> usize` to get the number of data cells in a given program [#1022](https://github.com/lambdaclass/cairo-rs/pull/1022) * Add missing hint on uint256_improvements lib [#1013](https://github.com/lambdaclass/cairo-rs/pull/1013): diff --git a/src/serde/deserialize_program.rs b/src/serde/deserialize_program.rs index 95b3204d96..9f0b7bcf13 100644 --- a/src/serde/deserialize_program.rs +++ b/src/serde/deserialize_program.rs @@ -375,6 +375,17 @@ pub fn parse_program_json( None => None, }; + let mut constants = HashMap::new(); + for (key, value) in program_json.identifiers.iter() { + if value.type_.as_deref() == Some("const") { + let value = value + .value + .clone() + .ok_or_else(|| ProgramError::ConstWithoutValue(key.clone()))?; + constants.insert(key.clone(), value); + } + } + let shared_program_data = SharedProgramData { builtins: program_json.builtins, data: program_json.data, @@ -390,25 +401,12 @@ pub fn parse_program_json( instruction_locations: program_json .debug_info .map(|debug_info| debug_info.instruction_locations), + identifiers: program_json.identifiers, }; Ok(Program { shared_program_data: Arc::new(shared_program_data), - constants: { - let mut constants = HashMap::new(); - for (key, value) in program_json.identifiers.iter() { - if value.type_.as_deref() == Some("const") { - let value = value - .value - .clone() - .ok_or_else(|| ProgramError::ConstWithoutValue(key.clone()))?; - constants.insert(key.clone(), value); - } - } - - constants - }, + constants, reference_manager: program_json.reference_manager, - identifiers: program_json.identifiers, }) } diff --git a/src/types/program.rs b/src/types/program.rs index 0341858b9b..3683bb3cfe 100644 --- a/src/types/program.rs +++ b/src/types/program.rs @@ -35,23 +35,23 @@ 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 builtins: Vec, - pub data: Vec, - pub hints: HashMap>, - pub main: Option, + pub(crate) builtins: Vec, + pub(crate) data: Vec, + pub(crate) hints: HashMap>, + pub(crate) main: Option, //start and end labels will only be used in proof-mode - pub start: Option, - pub end: Option, - pub error_message_attributes: Vec, - pub instruction_locations: Option>, + pub(crate) start: Option, + pub(crate) end: Option, + pub(crate) error_message_attributes: Vec, + pub(crate) instruction_locations: Option>, + pub(crate) identifiers: HashMap, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct Program { pub(crate) shared_program_data: Arc, - pub constants: HashMap, - pub reference_manager: ReferenceManager, - pub identifiers: HashMap, + pub(crate) constants: HashMap, + pub(crate) reference_manager: ReferenceManager, } impl Program { @@ -66,6 +66,16 @@ impl Program { error_message_attributes: Vec, instruction_locations: Option>, ) -> Result { + let mut constants = HashMap::new(); + for (key, value) in identifiers.iter() { + if value.type_.as_deref() == Some("const") { + let value = value + .value + .clone() + .ok_or_else(|| ProgramError::ConstWithoutValue(key.clone()))?; + constants.insert(key.clone(), value); + } + } let shared_program_data = SharedProgramData { builtins, data, @@ -75,25 +85,12 @@ impl Program { end: None, error_message_attributes, instruction_locations, + identifiers, }; Ok(Self { shared_program_data: Arc::new(shared_program_data), - constants: { - let mut constants = HashMap::new(); - for (key, value) in identifiers.iter() { - if value.type_.as_deref() == Some("const") { - let value = value - .value - .clone() - .ok_or_else(|| ProgramError::ConstWithoutValue(key.clone()))?; - constants.insert(key.clone(), value); - } - } - - constants - }, + constants, reference_manager, - identifiers, }) } @@ -123,6 +120,10 @@ impl Program { pub fn data_len(&self) -> usize { self.shared_program_data.data.len() } + + pub fn get_identifier(&self, id: &str) -> Option<&Identifier> { + self.shared_program_data.identifiers.get(id) + } } impl Default for Program { @@ -133,7 +134,6 @@ impl Default for Program { reference_manager: ReferenceManager { references: Vec::new(), }, - identifiers: HashMap::new(), } } } @@ -181,7 +181,7 @@ mod tests { assert_eq!(program.shared_program_data.builtins, builtins); assert_eq!(program.shared_program_data.data, data); assert_eq!(program.shared_program_data.main, None); - assert_eq!(program.identifiers, HashMap::new()); + assert_eq!(program.shared_program_data.identifiers, HashMap::new()); } #[test] @@ -243,7 +243,7 @@ mod tests { assert_eq!(program.shared_program_data.builtins, builtins); assert_eq!(program.shared_program_data.data, data); assert_eq!(program.shared_program_data.main, None); - assert_eq!(program.identifiers, identifiers); + assert_eq!(program.shared_program_data.identifiers, identifiers); assert_eq!( program.constants, [("__main__.main.SIZEOF_LOCALS", Felt252::zero())] @@ -359,6 +359,76 @@ mod tests { assert_eq!(program.data_len(), data.len()); } + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + fn get_identifier() { + let reference_manager = ReferenceManager { + references: Vec::new(), + }; + + let builtins: Vec = Vec::new(); + + let data: Vec = vec![ + mayberelocatable!(5189976364521848832), + mayberelocatable!(1000), + mayberelocatable!(5189976364521848832), + mayberelocatable!(2000), + mayberelocatable!(5201798304953696256), + mayberelocatable!(2345108766317314046), + ]; + + let mut identifiers: HashMap = HashMap::new(); + + identifiers.insert( + String::from("__main__.main"), + Identifier { + pc: Some(0), + type_: Some(String::from("function")), + value: None, + full_name: None, + members: None, + cairo_type: None, + }, + ); + + identifiers.insert( + String::from("__main__.main.SIZEOF_LOCALS"), + Identifier { + pc: None, + type_: Some(String::from("const")), + value: Some(Felt252::zero()), + full_name: None, + members: None, + cairo_type: None, + }, + ); + + let program = Program::new( + builtins, + data, + None, + HashMap::new(), + reference_manager, + identifiers.clone(), + Vec::new(), + None, + ) + .unwrap(); + + assert_eq!( + program.get_identifier("__main__.main"), + identifiers.get("__main__.main"), + ); + assert_eq!( + program.get_identifier("__main__.main.SIZEOF_LOCALS"), + identifiers.get("__main__.main.SIZEOF_LOCALS"), + ); + assert_eq!( + program.get_identifier("missing"), + identifiers.get("missing"), + ); + } + #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn new_program_with_invalid_identifiers() { @@ -497,7 +567,7 @@ mod tests { assert_eq!(program.shared_program_data.builtins, builtins); assert_eq!(program.shared_program_data.data, data); assert_eq!(program.shared_program_data.main, Some(0)); - assert_eq!(program.identifiers, identifiers); + assert_eq!(program.shared_program_data.identifiers, identifiers); } /// Deserialize a program without an entrypoint. @@ -596,7 +666,7 @@ mod tests { assert_eq!(program.shared_program_data.builtins, builtins); assert_eq!(program.shared_program_data.data, data); assert_eq!(program.shared_program_data.main, None); - assert_eq!(program.identifiers, identifiers); + assert_eq!(program.shared_program_data.identifiers, identifiers); assert_eq!( program.shared_program_data.error_message_attributes, error_message_attributes @@ -654,6 +724,7 @@ mod tests { end: None, error_message_attributes: Vec::new(), instruction_locations: None, + identifiers: HashMap::new(), }; let program = Program { shared_program_data: Arc::new(shared_program_data), @@ -661,7 +732,6 @@ mod tests { reference_manager: ReferenceManager { references: Vec::new(), }, - identifiers: HashMap::new(), }; assert_eq!(program, Program::default()); diff --git a/src/utils.rs b/src/utils.rs index 0741892de6..7021f866ae 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -255,6 +255,7 @@ pub mod test_utils { end: None, error_message_attributes: crate::stdlib::vec::Vec::new(), instruction_locations: None, + identifiers: crate::stdlib::collections::HashMap::new(), }; Program { shared_program_data: Arc::new(shared_program_data), @@ -262,7 +263,6 @@ pub mod test_utils { reference_manager: ReferenceManager { references: crate::stdlib::vec::Vec::new(), }, - identifiers: crate::stdlib::collections::HashMap::new(), } }}; // Custom program definition @@ -282,14 +282,6 @@ pub mod test_utils { ..Default::default() } }; - ($(identifiers = $value:expr),* $(,)?) => { - Program { - $( - identifiers: $value, - )* - ..Default::default() - } - }; ($($field:ident = $value:expr),* $(,)?) => {{ let shared_data = SharedProgramData { $( @@ -883,6 +875,7 @@ mod test { end: None, error_message_attributes: Vec::new(), instruction_locations: None, + identifiers: HashMap::new(), }; let program = Program { shared_program_data: Arc::new(shared_data), @@ -890,7 +883,6 @@ mod test { reference_manager: ReferenceManager { references: Vec::new(), }, - identifiers: HashMap::new(), }; assert_eq!(program, program!()) } @@ -907,6 +899,7 @@ mod test { end: None, error_message_attributes: Vec::new(), instruction_locations: None, + identifiers: HashMap::new(), }; let program = Program { shared_program_data: Arc::new(shared_data), @@ -914,7 +907,6 @@ mod test { reference_manager: ReferenceManager { references: Vec::new(), }, - identifiers: HashMap::new(), }; assert_eq!(program, program![BuiltinName::range_check]) @@ -932,6 +924,7 @@ mod test { end: None, error_message_attributes: Vec::new(), instruction_locations: None, + identifiers: HashMap::new(), }; let program = Program { shared_program_data: Arc::new(shared_data), @@ -939,7 +932,6 @@ mod test { reference_manager: ReferenceManager { references: Vec::new(), }, - identifiers: HashMap::new(), }; assert_eq!( diff --git a/src/vm/runners/cairo_runner.rs b/src/vm/runners/cairo_runner.rs index 66085e9163..2b791a598e 100644 --- a/src/vm/runners/cairo_runner.rs +++ b/src/vm/runners/cairo_runner.rs @@ -985,6 +985,7 @@ impl CairoRunner { let new_entrypoint = new_entrypoint.unwrap_or("main"); self.entrypoint = Some( self.program + .shared_program_data .identifiers .get(&format!("__main__.{new_entrypoint}")) .and_then(|x| x.pc) @@ -4160,24 +4161,24 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn set_entrypoint_main_default() { - let program = program!(); + let program = program!( + identifiers = [( + "__main__.main", + Identifier { + pc: Some(0), + type_: None, + value: None, + full_name: None, + members: None, + cairo_type: None, + }, + )] + .into_iter() + .map(|(k, v)| (k.to_string(), v)) + .collect(), + ); let mut cairo_runner = cairo_runner!(program); - cairo_runner.program.identifiers = [( - "__main__.main", - Identifier { - pc: Some(0), - type_: None, - value: None, - full_name: None, - members: None, - cairo_type: None, - }, - )] - .into_iter() - .map(|(k, v)| (k.to_string(), v)) - .collect(); - cairo_runner .set_entrypoint(None) .expect("Call to `set_entrypoint()` failed."); @@ -4187,37 +4188,37 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn set_entrypoint_main() { - let program = program!(); + let program = program!( + identifiers = [ + ( + "__main__.main", + Identifier { + pc: Some(0), + type_: None, + value: None, + full_name: None, + members: None, + cairo_type: None, + }, + ), + ( + "__main__.alternate_main", + Identifier { + pc: Some(1), + type_: None, + value: None, + full_name: None, + members: None, + cairo_type: None, + }, + ), + ] + .into_iter() + .map(|(k, v)| (k.to_string(), v)) + .collect(), + ); let mut cairo_runner = cairo_runner!(program); - cairo_runner.program.identifiers = [ - ( - "__main__.main", - Identifier { - pc: Some(0), - type_: None, - value: None, - full_name: None, - members: None, - cairo_type: None, - }, - ), - ( - "__main__.alternate_main", - Identifier { - pc: Some(1), - type_: None, - value: None, - full_name: None, - members: None, - cairo_type: None, - }, - ), - ] - .into_iter() - .map(|(k, v)| (k.to_string(), v)) - .collect(); - cairo_runner .set_entrypoint(Some("alternate_main")) .expect("Call to `set_entrypoint()` failed."); @@ -4228,24 +4229,24 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn set_entrypoint_main_non_existent() { - let program = program!(); + let program = program!( + identifiers = [( + "__main__.main", + Identifier { + pc: Some(0), + type_: None, + value: None, + full_name: None, + members: None, + cairo_type: None, + }, + )] + .into_iter() + .map(|(k, v)| (k.to_string(), v)) + .collect(), + ); let mut cairo_runner = cairo_runner!(program); - cairo_runner.program.identifiers = [( - "__main__.main", - Identifier { - pc: Some(0), - type_: None, - value: None, - full_name: None, - members: None, - cairo_type: None, - }, - )] - .into_iter() - .map(|(k, v)| (k.to_string(), v)) - .collect(); - cairo_runner .set_entrypoint(Some("nonexistent_main")) .expect_err("Call to `set_entrypoint()` succeeded (should've failed)."); @@ -4459,6 +4460,7 @@ mod tests { //this entrypoint tells which function to run in the cairo program let main_entrypoint = program + .shared_program_data .identifiers .get("__main__.main") .unwrap() @@ -4490,6 +4492,7 @@ mod tests { new_cairo_runner.initialize_segments(&mut new_vm, None); let fib_entrypoint = program + .shared_program_data .identifiers .get("__main__.evaluate_fib") .unwrap() @@ -4605,6 +4608,7 @@ mod tests { //this entrypoint tells which function to run in the cairo program let main_entrypoint = program + .shared_program_data .identifiers .get("__main__.main") .unwrap()