diff --git a/cairo_programs/manually_compiled/pie_additional_data_test.json b/cairo_programs/manually_compiled/pie_additional_data_test.json new file mode 100644 index 0000000000..a0c245b9c1 --- /dev/null +++ b/cairo_programs/manually_compiled/pie_additional_data_test.json @@ -0,0 +1,50 @@ +{ + "output_builtin": { + "pages": { + "1": [ + 18, + 46 + ] + }, + "attributes": { + "gps_fact_topology": [ + 2, + 1, + 0, + 2 + ] + } + }, + "pedersen_builtin": [ + [ + 3, + 2 + ], + [ + 3, + 5 + ], + [ + 3, + 8 + ], + [ + 3, + 11 + ], + [ + 3, + 14 + ], + [ + 3, + 17 + ] + ], + "range_check_builtin": null, + "ecdsa_builtin": [], + "bitwise_builtin": null, + "ec_op_builtin": null, + "keccak_builtin": null, + "poseidon_builtin": null +} \ No newline at end of file diff --git a/vm/src/hint_processor/builtin_hint_processor/bootloader/bootloader_hints.rs b/vm/src/hint_processor/builtin_hint_processor/bootloader/bootloader_hints.rs index ff26da9814..f57e49175a 100644 --- a/vm/src/hint_processor/builtin_hint_processor/bootloader/bootloader_hints.rs +++ b/vm/src/hint_processor/builtin_hint_processor/bootloader/bootloader_hints.rs @@ -87,7 +87,10 @@ pub fn prepare_simple_bootloader_output_segment( /// Implements %{ simple_bootloader_input = bootloader_input %} pub fn prepare_simple_bootloader_input(exec_scopes: &mut ExecutionScopes) -> Result<(), HintError> { let bootloader_input: BootloaderInput = exec_scopes.get(vars::BOOTLOADER_INPUT)?; - exec_scopes.insert_value(vars::SIMPLE_BOOTLOADER_INPUT, bootloader_input); + exec_scopes.insert_value( + vars::SIMPLE_BOOTLOADER_INPUT, + bootloader_input.simple_bootloader_input, + ); Ok(()) } @@ -226,6 +229,7 @@ pub fn import_packed_output_schemas() -> Result<(), HintError> { } /// Implements %{ isinstance(packed_output, PlainPackedOutput) %} +/// (compiled to %{ memory[ap] = to_felt_or_relocatable(isinstance(packed_output, PlainPackedOutput)) %}). /// /// Stores the result in the `ap` register to be accessed by the program. pub fn is_plain_packed_output( @@ -564,18 +568,21 @@ mod tests { prepare_simple_bootloader_input(&mut exec_scopes).expect("Hint failed unexpectedly"); - let simple_bootloader_input: BootloaderInput = exec_scopes + let simple_bootloader_input: SimpleBootloaderInput = exec_scopes .get(vars::SIMPLE_BOOTLOADER_INPUT) .expect("Simple bootloader input not in scope"); - assert_eq!(simple_bootloader_input, bootloader_input); + assert_eq!( + simple_bootloader_input, + bootloader_input.simple_bootloader_input + ); } #[test] fn test_restore_bootloader_output() { let mut vm: VirtualMachine = vm!(); // The VM must have an existing output segment - vm.builtin_runners = - vec![OutputBuiltinRunner::from_segment(&vm.add_memory_segment(), true).into()]; + let output_segment = vm.add_memory_segment(); + vm.builtin_runners = vec![OutputBuiltinRunner::from_segment(&output_segment, true).into()]; let mut exec_scopes = ExecutionScopes::new(); let new_segment = vm.add_memory_segment(); diff --git a/vm/src/hint_processor/builtin_hint_processor/bootloader/execute_task_hints.rs b/vm/src/hint_processor/builtin_hint_processor/bootloader/execute_task_hints.rs index d36cdcd9f6..b40a4157d8 100644 --- a/vm/src/hint_processor/builtin_hint_processor/bootloader/execute_task_hints.rs +++ b/vm/src/hint_processor/builtin_hint_processor/bootloader/execute_task_hints.rs @@ -1,14 +1,13 @@ use std::any::Any; use std::collections::HashMap; -use num_traits::ToPrimitive; use starknet_crypto::FieldElement; use crate::Felt252; use crate::any_box; use crate::hint_processor::builtin_hint_processor::bootloader::fact_topologies::{ - get_program_task_fact_topology, FactTopology, + get_task_fact_topology, FactTopology, }; use crate::hint_processor::builtin_hint_processor::bootloader::load_cairo_pie::load_cairo_pie; use crate::hint_processor::builtin_hint_processor::bootloader::program_hash::compute_program_hash_chain; @@ -19,11 +18,12 @@ use crate::hint_processor::builtin_hint_processor::hint_utils::{ get_ptr_from_var_name, get_relocatable_from_var_name, insert_value_from_var_name, }; use crate::hint_processor::hint_processor_definition::HintReference; -use crate::serde::deserialize_program::{ApTracking, BuiltinName}; -use crate::types::errors::math_errors::MathError; +use crate::serde::deserialize_program::{ApTracking, BuiltinName, Identifier}; use crate::types::exec_scope::ExecutionScopes; +use crate::types::program::Program; use crate::types::relocatable::Relocatable; use crate::vm::errors::hint_errors::HintError; +use crate::vm::errors::memory_errors::MemoryError; use crate::vm::runners::cairo_pie::{CairoPie, OutputBuiltinAdditionalData, StrippedProgram}; use crate::vm::vm_core::VirtualMachine; use crate::vm::vm_memory::memory::Memory; @@ -119,7 +119,7 @@ pub fn append_fact_topologies( ap_tracking: &ApTracking, ) -> Result<(), HintError> { let task: Task = exec_scopes.get(vars::TASK)?; - let output_runner_data: OutputBuiltinAdditionalData = + let output_runner_data: Option = exec_scopes.get(vars::OUTPUT_RUNNER_DATA)?; let fact_topologies: &mut Vec = exec_scopes.get_mut_ref(vars::FACT_TOPOLOGIES)?; @@ -129,22 +129,13 @@ pub fn append_fact_topologies( get_relocatable_from_var_name("return_builtin_ptrs", vm, ids_data, ap_tracking)?; // The output field is the first one in the BuiltinData struct - let output_start = vm - .get_integer(pre_execution_builtin_ptrs_addr)? - .into_owned(); - let output_end = vm.get_integer(return_builtin_ptrs_addr)?.into_owned(); - let output_size = { - let output_size_felt = output_end - output_start; - output_size_felt - .to_usize() - .ok_or(MathError::Felt252ToUsizeConversion(Box::new( - output_size_felt, - ))) - }?; + let output_start = vm.get_relocatable(pre_execution_builtin_ptrs_addr)?; + let output_end = vm.get_relocatable(return_builtin_ptrs_addr)?; + let output_size = (output_end - output_start)?; let output_builtin = vm.get_output_builtin()?; let fact_topology = - get_program_task_fact_topology(output_size, &task, output_builtin, output_runner_data) + get_task_fact_topology(output_size, &task, output_builtin, output_runner_data) .map_err(Into::::into)?; fact_topologies.push(fact_topology); @@ -214,19 +205,16 @@ fn check_cairo_pie_builtin_usage( return_builtins_addr: &Relocatable, pre_execution_builtins_addr: &Relocatable, ) -> Result<(), HintError> { - let return_builtin_value = memory - .get_integer(return_builtins_addr + builtin_index)? - .into_owned(); - let pre_execution_builtin_value = memory - .get_integer(pre_execution_builtins_addr + builtin_index)? - .into_owned(); - let expected_builtin_size = return_builtin_value - pre_execution_builtin_value; + let return_builtin_value = memory.get_relocatable(return_builtins_addr + builtin_index)?; + let pre_execution_builtin_value = + memory.get_relocatable(pre_execution_builtins_addr + builtin_index)?; + let expected_builtin_size = (return_builtin_value - pre_execution_builtin_value)?; let builtin_name = builtin .name() .strip_suffix("_builtin") .unwrap_or(builtin.name()); - let builtin_size = Felt252::from(cairo_pie.metadata.builtin_segments[builtin_name].size); + let builtin_size = cairo_pie.metadata.builtin_segments[builtin_name].size; if builtin_size != expected_builtin_size { return Err(HintError::AssertionFailed( @@ -254,9 +242,7 @@ fn write_return_builtins( let mut used_builtin_offset: usize = 0; for (index, builtin) in ALL_BUILTINS.iter().enumerate() { if used_builtins.contains(builtin) { - let builtin_value = memory - .get_integer(used_builtins_addr + used_builtin_offset)? - .into_owned(); + let builtin_value = memory.get_relocatable(used_builtins_addr + used_builtin_offset)?; memory.insert_value(return_builtins_addr + index, builtin_value)?; used_builtin_offset += 1; @@ -273,8 +259,12 @@ fn write_return_builtins( } // The builtin is unused, hence its value is the same as before calling the program. else { + let pre_execution_builtin_addr = pre_execution_builtins_addr + index; let pre_execution_value = memory - .get_integer(pre_execution_builtins_addr + index)? + .get(&pre_execution_builtin_addr) + .ok_or_else(|| { + MemoryError::UnknownMemoryCell(Box::new(pre_execution_builtin_addr)) + })? .into_owned(); memory.insert_value(return_builtins_addr + index, pre_execution_value)?; } @@ -340,6 +330,33 @@ pub fn write_return_builtins_hint( Ok(()) } +fn get_bootloader_program(exec_scopes: &ExecutionScopes) -> Result<&Program, HintError> { + if let Some(boxed_program) = exec_scopes.data[0].get(vars::BOOTLOADER_PROGRAM) { + if let Some(program) = boxed_program.downcast_ref::() { + return Ok(program); + } + } + + Err(HintError::VariableNotInScopeError( + vars::BOOTLOADER_PROGRAM.to_string().into_boxed_str(), + )) +} + +fn get_identifier( + identifiers: &HashMap, + name: &str, +) -> Result { + if let Some(identifier) = identifiers.get(name) { + if let Some(pc) = identifier.pc { + return Ok(pc); + } + } + + Err(HintError::VariableNotInScopeError( + name.to_string().into_boxed_str(), + )) +} + /* Implements hint: %{ @@ -387,11 +404,11 @@ pub fn call_task( let task: Task = exec_scopes.get(vars::TASK)?; // n_builtins = len(task.get_program().builtins) - let num_builtins = get_program_from_task(&task)?.builtins.len(); + let n_builtins = get_program_from_task(&task)?.builtins.len(); + exec_scopes.insert_value(vars::N_BUILTINS, n_builtins); let mut new_task_locals = HashMap::new(); - // TODO: remove clone here when RunProgramTask has proper variant data (not String) match &task { // if isinstance(task, RunProgramTask): Task::Program(_program) => { @@ -410,6 +427,17 @@ pub fn call_task( let program_address: Relocatable = exec_scopes.get("program_address")?; // ret_pc = ids.ret_pc_label.instruction_offset_ - ids.call_task.instruction_offset_ + pc + let bootloader_program = get_bootloader_program(exec_scopes)?; + let identifiers = &bootloader_program.shared_program_data.identifiers; + let ret_pc_label = get_identifier(identifiers, "starkware.cairo.bootloaders.simple_bootloader.execute_task.execute_task.ret_pc_label")?; + let call_task = get_identifier( + identifiers, + "starkware.cairo.bootloaders.simple_bootloader.execute_task.execute_task.call_task", + )?; + + let ret_pc_offset = ret_pc_label - call_task; + let ret_pc = (vm.run_context.pc + ret_pc_offset)?; + // load_cairo_pie( // task=task.cairo_pie, memory=memory, segments=segments, // program_address=program_address, execution_segment_address= ap - n_builtins, @@ -418,9 +446,9 @@ pub fn call_task( cairo_pie, vm, program_address, - (vm.get_ap() - num_builtins)?, + (vm.get_ap() - n_builtins)?, vm.get_fp(), - vm.get_pc(), + ret_pc, ) .map_err(Into::::into)?; } @@ -432,16 +460,12 @@ pub fn call_task( // output_ptr=ids.pre_execution_builtin_ptrs.output) let pre_execution_builtin_ptrs_addr = get_relocatable_from_var_name(vars::PRE_EXECUTION_BUILTIN_PTRS, vm, ids_data, ap_tracking)?; - let output = vm - .get_integer(pre_execution_builtin_ptrs_addr)? - .into_owned(); - let output_ptr = output - .to_usize() - .ok_or(MathError::Felt252ToUsizeConversion(Box::new(output)))?; + // The output field is the first one in the BuiltinData struct + let output_ptr = vm.get_relocatable(&pre_execution_builtin_ptrs_addr + 0)?; let output_runner_data = util::prepare_output_runner(&task, vm.get_output_builtin()?, output_ptr)?; - exec_scopes.insert_box(vars::OUTPUT_RUNNER_DATA, any_box!(output_runner_data)); + exec_scopes.insert_value(vars::OUTPUT_RUNNER_DATA, output_runner_data); exec_scopes.enter_scope(new_task_locals); @@ -465,7 +489,7 @@ mod util { pub(crate) fn prepare_output_runner( task: &Task, output_builtin: &mut OutputBuiltinRunner, - output_ptr: usize, + output_ptr: Relocatable, ) -> Result, HintError> { return match task { Task::Program(_) => { @@ -477,7 +501,7 @@ mod util { .into_boxed_str(), )), }?; - output_builtin.base = output_ptr; + output_builtin.new_state(output_ptr.segment_index as usize, true); Ok(Some(output_state)) } Task::Pie(_) => Ok(None), @@ -492,18 +516,16 @@ mod tests { use assert_matches::assert_matches; use rstest::{fixture, rstest}; - use crate::Felt252; - use crate::any_box; use crate::hint_processor::builtin_hint_processor::builtin_hint_processor_definition::BuiltinHintProcessor; use crate::hint_processor::builtin_hint_processor::builtin_hint_processor_definition::HintProcessorData; use crate::hint_processor::builtin_hint_processor::hint_code; use crate::hint_processor::builtin_hint_processor::hint_utils::get_ptr_from_var_name; use crate::hint_processor::hint_processor_definition::HintProcessorLogic; - use crate::types::relocatable::Relocatable; + use crate::types::relocatable::{MaybeRelocatable, Relocatable}; use crate::utils::test_utils::*; use crate::vm::runners::builtin_runner::{BuiltinRunner, OutputBuiltinRunner}; - use crate::vm::runners::cairo_pie::{BuiltinAdditionalData, PublicMemoryPage}; + use crate::vm::runners::cairo_pie::{BuiltinAdditionalData, CairoPie, PublicMemoryPage}; use super::*; @@ -615,7 +637,7 @@ mod tests { // Allocate space for pre-execution (8 felts), which mimics the `BuiltinData` struct in the // Bootloader's Cairo code. Our code only uses the first felt (`output` field in the struct) - vm.segments = segments![((1, 0), 0)]; + vm.segments = segments![((1, 0), (2, 0))]; vm.run_context.fp = 8; add_segments!(vm, 1); @@ -642,6 +664,42 @@ mod tests { ); } + /// Creates a fake Program struct to act as a placeholder for the `BOOTLOADER_PROGRAM` variable. + /// These other options have been considered: + /// * a `HasIdentifiers` trait cannot be used as exec_scopes requires to cast to `Box`, + /// making casting back to the trait impossible. + /// * using an enum requires defining test-only variants. + fn mock_program_with_identifiers(symbols: HashMap) -> Program { + let identifiers = symbols + .into_iter() + .map(|(name, pc)| { + ( + name, + Identifier { + pc: Some(pc), + type_: None, + value: None, + full_name: None, + members: None, + cairo_type: None, + }, + ) + }) + .collect(); + + let shared_program_data = SharedProgramData { + identifiers, + ..Default::default() + }; + let program = Program { + shared_program_data: Arc::new(shared_program_data), + constants: Default::default(), + builtins: vec![], + }; + + program + } + #[rstest] fn test_call_cairo_pie_task(fibonacci_pie: CairoPie) { let mut vm = vm!(); @@ -651,7 +709,7 @@ mod tests { // the Bootloader Cairo code. Our code only uses the first felt (`output` field in the // struct). Finally, we put the mocked output of `select_input_builtins` in the next // memory address and increase the AP register accordingly. - vm.segments = segments![((1, 0), (2, 0)), ((1, 1), 42), ((1, 9), (4, 0))]; + vm.segments = segments![((1, 0), (2, 0)), ((1, 1), (4, 0)), ((1, 9), (4, 42))]; vm.run_context.ap = 10; vm.run_context.fp = 9; add_segments!(vm, 3); @@ -672,7 +730,15 @@ mod tests { let task = Task::Pie(fibonacci_pie); exec_scopes.insert_value(vars::TASK, task); + let bootloader_identifiers = HashMap::from( + [ + ("starkware.cairo.bootloaders.simple_bootloader.execute_task.execute_task.ret_pc_label".to_string(), 10usize), + ("starkware.cairo.bootloaders.simple_bootloader.execute_task.execute_task.call_task".to_string(), 8usize) + ] + ); + let bootloader_program = mock_program_with_identifiers(bootloader_identifiers); exec_scopes.insert_value(vars::PROGRAM_DATA_BASE, program_header_ptr.clone()); + exec_scopes.insert_value(vars::BOOTLOADER_PROGRAM, bootloader_program); // Load the program in memory load_program_hint(&mut vm, &mut exec_scopes, &ids_data, &ap_tracking) @@ -691,8 +757,9 @@ mod tests { // Allocate space for the pre-execution and return builtin structs (2 x 8 felts). // The pre-execution struct starts at (1, 0) and the return struct at (1, 8). - // We only set the output values to 0 and 10, respectively, to get an output size of 10. - vm.segments = segments![((1, 0), 0), ((1, 8), 10),]; + // We only set the output values to (2, 0) and (2, 10), respectively, to get an output size + // of 10. + vm.segments = segments![((1, 0), (2, 0)), ((1, 8), (2, 10)),]; vm.run_context.fp = 16; add_segments!(vm, 1); @@ -725,7 +792,7 @@ mod tests { pages: HashMap::new(), attributes: HashMap::new(), }; - exec_scopes.insert_value(vars::OUTPUT_RUNNER_DATA, output_runner_data.clone()); + exec_scopes.insert_value(vars::OUTPUT_RUNNER_DATA, Some(output_runner_data.clone())); exec_scopes.insert_value(vars::TASK, task); exec_scopes.insert_value(vars::FACT_TOPOLOGIES, Vec::::new()); @@ -761,16 +828,16 @@ mod tests { // are used by the field arithmetic program. Note that the used builtins list // does not contain empty elements (i.e. offsets are 8 and 9 instead of 10 and 12). vm.segments = segments![ - ((1, 0), 1), - ((1, 1), 2), - ((1, 2), 3), - ((1, 3), 4), - ((1, 4), 5), - ((1, 5), 6), - ((1, 6), 7), - ((1, 7), 8), - ((1, 8), 30), - ((1, 9), 50), + ((1, 0), (2, 1)), + ((1, 1), (2, 2)), + ((1, 2), (2, 3)), + ((1, 3), (2, 4)), + ((1, 4), (2, 5)), + ((1, 5), (2, 6)), + ((1, 6), (2, 7)), + ((1, 7), (2, 8)), + ((1, 8), (2, 30)), + ((1, 9), (2, 50)), ((1, 24), (1, 8)), ]; vm.run_context.fp = 25; @@ -796,12 +863,21 @@ mod tests { let return_builtins = vm .segments .memory - .get_integer_range(Relocatable::from((1, 16)), 8) + .get_continuous_range(Relocatable::from((1, 16)), 8) .expect("Return builtin was not properly written to memory."); - let expected_builtins = vec![1, 2, 30, 4, 50, 6, 7, 8]; + let expected_builtins = vec![ + Relocatable::from((2, 1)), + Relocatable::from((2, 2)), + Relocatable::from((2, 30)), + Relocatable::from((2, 4)), + Relocatable::from((2, 50)), + Relocatable::from((2, 6)), + Relocatable::from((2, 7)), + Relocatable::from((2, 8)), + ]; for (expected, actual) in std::iter::zip(expected_builtins, return_builtins) { - assert_eq!(Felt252::from(expected), actual.into_owned()); + assert_eq!(MaybeRelocatable::RelocatableValue(expected), actual); } // Check that the exec scope changed diff --git a/vm/src/hint_processor/builtin_hint_processor/bootloader/fact_topologies.rs b/vm/src/hint_processor/builtin_hint_processor/bootloader/fact_topologies.rs index ca3dc8dd0f..0430453ce2 100644 --- a/vm/src/hint_processor/builtin_hint_processor/bootloader/fact_topologies.rs +++ b/vm/src/hint_processor/builtin_hint_processor/bootloader/fact_topologies.rs @@ -75,6 +75,9 @@ pub enum FactTopologyError { #[error("Could not add page to output: {0}")] FailedToAddOutputPage(#[from] RunnerError), + #[error("Could not load output builtin additional data from Cairo PIE")] + CairoPieHasNoOutputBuiltinData, + #[error("Unexpected error: {0}")] Internal(Box), } @@ -333,9 +336,8 @@ fn get_fact_topology_from_additional_data( } // TODO: implement for CairoPieTask -pub fn get_program_task_fact_topology( +fn get_program_task_fact_topology( output_size: usize, - _task: &Task, output_builtin: &mut OutputBuiltinRunner, output_runner_data: OutputBuiltinAdditionalData, ) -> Result { @@ -357,6 +359,39 @@ pub fn get_program_task_fact_topology( Ok(fact_topology) } +pub fn get_task_fact_topology( + output_size: usize, + task: &Task, + output_builtin: &mut OutputBuiltinRunner, + output_runner_data: Option, +) -> Result { + match task { + Task::Program(_program) => { + let output_runner_data = output_runner_data.ok_or(FactTopologyError::Internal( + "Output runner data not set for program task" + .to_string() + .into_boxed_str(), + ))?; + get_program_task_fact_topology(output_size, output_builtin, output_runner_data) + } + Task::Pie(cairo_pie) => { + if let Some(_) = output_runner_data { + return Err(FactTopologyError::Internal( + "Output runner data set for Cairo PIE task" + .to_string() + .into_boxed_str(), + )); + } + let additional_data = cairo_pie + .additional_data + .output_builtin + .as_ref() + .ok_or(FactTopologyError::CairoPieHasNoOutputBuiltinData)?; + get_fact_topology_from_additional_data(output_size, &additional_data) + } + } +} + /// Writes fact topologies to a file, as JSON. /// /// * `path`: File path. diff --git a/vm/src/hint_processor/builtin_hint_processor/bootloader/load_cairo_pie.rs b/vm/src/hint_processor/builtin_hint_processor/bootloader/load_cairo_pie.rs index 6cc0d063b8..383fe4e2e7 100644 --- a/vm/src/hint_processor/builtin_hint_processor/bootloader/load_cairo_pie.rs +++ b/vm/src/hint_processor/builtin_hint_processor/bootloader/load_cairo_pie.rs @@ -2,7 +2,7 @@ use crate::types::relocatable::{MaybeRelocatable, Relocatable}; use crate::vm::errors::hint_errors::HintError; use crate::vm::errors::memory_errors::MemoryError; use crate::vm::runners::builtin_runner::SignatureBuiltinRunner; -use crate::vm::runners::cairo_pie::{BuiltinAdditionalData, CairoPie, CairoPieMemory}; +use crate::vm::runners::cairo_pie::{CairoPie, CairoPieMemory}; use crate::vm::vm_core::VirtualMachine; use crate::Felt252; use std::collections::HashMap; @@ -28,9 +28,6 @@ pub enum SignatureRelocationError { #[error("The PIE requires ECDSA but the VM is not configured to use it")] EcdsaBuiltinNotFound, - #[error("The data of the Cairo PIE ECDSA builtin does not match the expected type")] - UnexpectedBuiltinDataType, - #[error("Relocated signature data ({0} not on signature builtin segment {1}")] RelocatedDataNotOnBuiltinSegment(Relocatable, isize), } @@ -226,19 +223,16 @@ fn relocate_builtin_additional_data( vm: &mut VirtualMachine, relocation_table: &RelocationTable, ) -> Result<(), SignatureRelocationError> { - let ecdsa_additional_data = match cairo_pie.additional_data.get("ecdsa_builtin") { - Some(additional_data) => match additional_data { - BuiltinAdditionalData::Signature(data) => data, - _ => return Err(SignatureRelocationError::UnexpectedBuiltinDataType), - }, + let ecdsa_additional_data = match &cairo_pie.additional_data.ecdsa_builtin { None => return Ok(()), + Some(data) => data, }; let ecdsa_builtin = vm .get_signature_builtin() .map_err(|_| SignatureRelocationError::EcdsaBuiltinNotFound)?; - extend_additional_data(ecdsa_builtin, ecdsa_additional_data, relocation_table)?; + extend_additional_data(ecdsa_builtin, &ecdsa_additional_data.0, relocation_table)?; Ok(()) } diff --git a/vm/src/hint_processor/builtin_hint_processor/bootloader/mod.rs b/vm/src/hint_processor/builtin_hint_processor/bootloader/mod.rs index aedfca2518..21e02f8186 100644 --- a/vm/src/hint_processor/builtin_hint_processor/bootloader/mod.rs +++ b/vm/src/hint_processor/builtin_hint_processor/bootloader/mod.rs @@ -7,5 +7,5 @@ mod program_hash; mod program_loader; pub(crate) mod select_builtins; pub(crate) mod simple_bootloader_hints; -pub(crate) mod types; +pub mod types; pub(crate) mod vars; diff --git a/vm/src/hint_processor/builtin_hint_processor/bootloader/program_loader.rs b/vm/src/hint_processor/builtin_hint_processor/bootloader/program_loader.rs index c41d51bdb8..61fed717da 100644 --- a/vm/src/hint_processor/builtin_hint_processor/bootloader/program_loader.rs +++ b/vm/src/hint_processor/builtin_hint_processor/bootloader/program_loader.rs @@ -35,13 +35,7 @@ fn builtin_to_felt(builtin: &BuiltinName) -> Result .strip_suffix("_builtin") .unwrap_or(builtin.name()); - // let buf = { - // let mut padding: Vec = vec![0; 32 - builtin_name.len()]; - // padding.extend_from_slice(builtin_name.as_bytes()); - // padding - // }; - let buf = builtin_name.as_bytes(); - Ok(Felt252::from_bytes_be_slice(&buf)) + Ok(Felt252::from_bytes_be_slice(builtin_name.as_bytes())) } pub struct LoadedProgram { @@ -200,7 +194,7 @@ mod tests { "Could not decode builtin from memory (expected {})", builtin ) - .as_ref(), + .as_ref(), ); // Compare the last N characters, builtin_from_felt is padded left with zeroes assert_eq!(&builtin_from_felt[32 - builtin.len()..32], builtin); diff --git a/vm/src/hint_processor/builtin_hint_processor/bootloader/simple_bootloader_hints.rs b/vm/src/hint_processor/builtin_hint_processor/bootloader/simple_bootloader_hints.rs index 65bdcbe499..005ae91e4c 100644 --- a/vm/src/hint_processor/builtin_hint_processor/bootloader/simple_bootloader_hints.rs +++ b/vm/src/hint_processor/builtin_hint_processor/bootloader/simple_bootloader_hints.rs @@ -74,8 +74,8 @@ pub fn set_tasks_variable(exec_scopes: &mut ExecutionScopes) -> Result<(), HintE Ok(()) } -/// Implements -/// %{ ids.num // 2 %} +/// Implements %{ ids.num // 2 %} +/// (compiled to %{ memory[ap] = to_felt_or_relocatable(ids.num // 2) %}). pub fn divide_num_by_2( vm: &mut VirtualMachine, ids_data: &HashMap, @@ -91,7 +91,7 @@ pub fn divide_num_by_2( Ok(()) } -/// Implements %{ 0 %}. +/// Implements %{ 0 %} (compiled to %{ memory[ap] = to_felt_or_relocatable(0) %}). /// /// Stores 0 in the AP and returns. /// Used as `tempvar use_poseidon = nondet %{ 0 %}`. diff --git a/vm/src/hint_processor/builtin_hint_processor/bootloader/vars.rs b/vm/src/hint_processor/builtin_hint_processor/bootloader/vars.rs index c34e8fb143..324fffb84e 100644 --- a/vm/src/hint_processor/builtin_hint_processor/bootloader/vars.rs +++ b/vm/src/hint_processor/builtin_hint_processor/bootloader/vars.rs @@ -1,6 +1,9 @@ /// Deserialized bootloader input. pub(crate) const BOOTLOADER_INPUT: &str = "bootloader_input"; +/// The bootloader program, as a Program object. +pub(crate) const BOOTLOADER_PROGRAM: &str = "bootloader_program"; + /// Saved state of the output builtin. pub(crate) const OUTPUT_BUILTIN_STATE: &str = "output_builtin_state"; diff --git a/vm/src/hint_processor/builtin_hint_processor/builtin_hint_processor_definition.rs b/vm/src/hint_processor/builtin_hint_processor/builtin_hint_processor_definition.rs index 85a1bbff8d..0a114a8a94 100644 --- a/vm/src/hint_processor/builtin_hint_processor/builtin_hint_processor_definition.rs +++ b/vm/src/hint_processor/builtin_hint_processor/builtin_hint_processor_definition.rs @@ -922,15 +922,15 @@ impl HintProcessorLogic for BuiltinHintProcessor { hint_code::EXECUTE_TASK_APPEND_FACT_TOPOLOGIES => { append_fact_topologies(vm, exec_scopes, &hint_data.ids_data, &hint_data.ap_tracking) } - hint_code::INNER_SELECT_BUILTINS_SELECT_BUILTIN => { - select_builtin(vm, exec_scopes, &hint_data.ids_data, &hint_data.ap_tracking) - } hint_code::SELECT_BUILTINS_ENTER_SCOPE => select_builtins_enter_scope( vm, exec_scopes, &hint_data.ids_data, &hint_data.ap_tracking, ), + hint_code::INNER_SELECT_BUILTINS_SELECT_BUILTIN => { + select_builtin(vm, exec_scopes, &hint_data.ids_data, &hint_data.ap_tracking) + } #[cfg(feature = "skip_next_instruction_hint")] hint_code::SKIP_NEXT_INSTRUCTION => skip_next_instruction(vm), #[cfg(feature = "print")] diff --git a/vm/src/hint_processor/builtin_hint_processor/hint_code.rs b/vm/src/hint_processor/builtin_hint_processor/hint_code.rs index bfbb95740e..553b9a2698 100644 --- a/vm/src/hint_processor/builtin_hint_processor/hint_code.rs +++ b/vm/src/hint_processor/builtin_hint_processor/hint_code.rs @@ -1466,7 +1466,9 @@ pub const BOOTLOADER_IMPORT_PACKED_OUTPUT_SCHEMAS: &str = PlainPackedOutput, )"; -pub const BOOTLOADER_IS_PLAIN_PACKED_OUTPUT: &str = "isinstance(packed_output, PlainPackedOutput)"; +// Appears as nondet %{ isinstance(packed_output, PlainPackedOutput) %} in the code. +pub const BOOTLOADER_IS_PLAIN_PACKED_OUTPUT: &str = + "memory[ap] = to_felt_or_relocatable(isinstance(packed_output, PlainPackedOutput))"; pub const BOOTLOADER_SAVE_OUTPUT_POINTER: &str = "output_start = ids.output_ptr"; @@ -1525,7 +1527,9 @@ fact_topologies = []"; pub const SIMPLE_BOOTLOADER_SET_TASKS_VARIABLE: &str = "tasks = simple_bootloader_input.tasks"; -pub const SIMPLE_BOOTLOADER_DIVIDE_NUM_BY_2: &str = "ids.num // 2"; +// Appears as nondet %{ ids.num // 2 %} in the code. +pub const SIMPLE_BOOTLOADER_DIVIDE_NUM_BY_2: &str = + "memory[ap] = to_felt_or_relocatable(ids.num // 2)"; pub const SIMPLE_BOOTLOADER_SET_CURRENT_TASK: &str = "from starkware.cairo.bootloaders.simple_bootloader.objects import Task @@ -1534,7 +1538,8 @@ pub const SIMPLE_BOOTLOADER_SET_CURRENT_TASK: &str = task_id = len(simple_bootloader_input.tasks) - ids.n_tasks task = simple_bootloader_input.tasks[task_id].load_task()"; -pub const SIMPLE_BOOTLOADER_ZERO: &str = "0"; +// Appears as nondet %{ 0 %} in the code. +pub const SIMPLE_BOOTLOADER_ZERO: &str = "memory[ap] = to_felt_or_relocatable(0)"; pub const EXECUTE_TASK_ALLOCATE_PROGRAM_DATA_SEGMENT: &str = "ids.program_data_ptr = program_data_base = segments.add()"; @@ -1551,7 +1556,7 @@ segments.finalize(program_data_base.segment_index, program_data_size)"; pub const EXECUTE_TASK_VALIDATE_HASH: &str = "# Validate hash. from starkware.cairo.bootloaders.hash_program import compute_program_hash_chain -assert memory[ids.output_ptr + 1] == compute_program_hash_chain(task.get_program()), \ +assert memory[ids.output_ptr + 1] == compute_program_hash_chain(task.get_program()), \\ 'Computed hash does not match input.'"; pub const EXECUTE_TASK_ASSERT_PROGRAM_ADDRESS: &str = "# Sanity check. @@ -1619,6 +1624,9 @@ fact_topologies.append(get_task_fact_topology( output_runner_data=output_runner_data, ))"; +pub const SELECT_BUILTINS_ENTER_SCOPE: &str = + "vm_enter_scope({'n_selected_builtins': ids.n_selected_builtins})"; + pub const INNER_SELECT_BUILTINS_SELECT_BUILTIN: &str = "# A builtin should be selected iff its encoding appears in the selected encodings list # and the list wasn't exhausted. @@ -1627,6 +1635,3 @@ ids.select_builtin = int( n_selected_builtins > 0 and memory[ids.selected_encodings] == memory[ids.all_encodings]) if ids.select_builtin: n_selected_builtins = n_selected_builtins - 1"; - -pub const SELECT_BUILTINS_ENTER_SCOPE: &str = - "vm_enter_scope({'n_selected_builtins': ids.n_selected_builtins})"; diff --git a/vm/src/hint_processor/builtin_hint_processor/mod.rs b/vm/src/hint_processor/builtin_hint_processor/mod.rs index 271ad444bc..76d5c78854 100644 --- a/vm/src/hint_processor/builtin_hint_processor/mod.rs +++ b/vm/src/hint_processor/builtin_hint_processor/mod.rs @@ -1,7 +1,7 @@ pub mod bigint; pub mod blake2s_hash; pub mod blake2s_utils; -mod bootloader; +pub mod bootloader; pub mod builtin_hint_processor_definition; pub mod cairo_keccak; pub mod dict_hint_utils; diff --git a/vm/src/tests/cairo_pie_test.rs b/vm/src/tests/cairo_pie_test.rs index 639b28b93d..e6935d5efd 100644 --- a/vm/src/tests/cairo_pie_test.rs +++ b/vm/src/tests/cairo_pie_test.rs @@ -19,13 +19,12 @@ use crate::{ HASH_BUILTIN_NAME, OUTPUT_BUILTIN_NAME, RANGE_CHECK_BUILTIN_NAME, SIGNATURE_BUILTIN_NAME, }, - cairo_pie::{ - BuiltinAdditionalData, CairoPieMemory, OutputBuiltinAdditionalData, SegmentInfo, - }, + cairo_pie::{CairoPieMemory, OutputBuiltinAdditionalData, SegmentInfo}, cairo_runner::ExecutionResources, }, }; +use crate::vm::runners::cairo_pie::{AdditionalData, SignatureBuiltinAdditionalData}; #[cfg(all(not(feature = "std"), feature = "alloc"))] use alloc::{ string::{String, ToString}, @@ -92,24 +91,15 @@ fn pedersen_test() { }; assert_eq!(cairo_pie.execution_resources, expected_execution_resources); // additional_data - let expected_additional_data = HashMap::from([ - ( - OUTPUT_BUILTIN_NAME.to_string(), - BuiltinAdditionalData::Output(OutputBuiltinAdditionalData { - base: 2, - pages: HashMap::new(), - attributes: HashMap::new(), - }), - ), - ( - HASH_BUILTIN_NAME.to_string(), - BuiltinAdditionalData::Hash(vec![Relocatable::from((3, 2))]), - ), - ( - RANGE_CHECK_BUILTIN_NAME.to_string(), - BuiltinAdditionalData::None, - ), - ]); + let expected_additional_data = AdditionalData { + output_builtin: Some(OutputBuiltinAdditionalData { + base: 2, + pages: HashMap::new(), + attributes: HashMap::new(), + }), + pedersen_builtin: Some(vec![Relocatable::from((3, 2))]), + ..Default::default() + }; assert_eq!(cairo_pie.additional_data, expected_additional_data); // memory assert_eq!( @@ -171,9 +161,8 @@ fn common_signature() { }; assert_eq!(cairo_pie.execution_resources, expected_execution_resources); // additional_data - let expected_additional_data = HashMap::from([( - SIGNATURE_BUILTIN_NAME.to_string(), - BuiltinAdditionalData::Signature(HashMap::from([( + let expected_additional_data = AdditionalData { + ecdsa_builtin: Some(SignatureBuiltinAdditionalData(HashMap::from([( Relocatable::from((2, 0)), ( felt_str!( @@ -183,8 +172,10 @@ fn common_signature() { "598673427589502599949712887611119751108407514580626464031881322743364689811" ), ), - )])), - )]); + )]))), + ..Default::default() + }; + assert_eq!(cairo_pie.additional_data, expected_additional_data); // memory assert_eq!( diff --git a/vm/src/vm/runners/builtin_runner/output.rs b/vm/src/vm/runners/builtin_runner/output.rs index 19aaf36e85..7b6c54f920 100644 --- a/vm/src/vm/runners/builtin_runner/output.rs +++ b/vm/src/vm/runners/builtin_runner/output.rs @@ -41,6 +41,14 @@ impl OutputBuiltinRunner { } } + pub fn new_state(&mut self, base: usize, included: bool) { + self.base = base; + self.pages = HashMap::default(); + self.attributes = HashMap::default(); + self.stop_ptr = None; + self.included = included; + } + pub fn initialize_segments(&mut self, segments: &mut MemorySegmentManager) { self.base = segments.add().segment_index as usize // segments.add() always returns a positive index } @@ -162,6 +170,21 @@ impl OutputBuiltinRunner { Ok(()) } + + pub fn get_public_memory(&self) -> Result, RunnerError> { + let size = self + .stop_ptr + .ok_or(RunnerError::NoStopPointer(Box::new(OUTPUT_BUILTIN_NAME)))?; + + let mut public_memory: Vec<(usize, usize)> = (0..size).map(|i| (i, 0)).collect(); + for (page_id, page) in self.pages.iter() { + for index in 0..page.size { + public_memory[page.start + index].1 = page_id.clone(); + } + } + + Ok(public_memory) + } } impl Default for OutputBuiltinRunner { @@ -543,4 +566,22 @@ mod tests { matches!(result, Err(RunnerError::PageNotOnSegment(relocatable, base)) if relocatable == page_start && base == builtin.base()) ) } + + #[test] + fn get_public_memory() { + let mut builtin = OutputBuiltinRunner::new(true); + let page_start = Relocatable { + segment_index: builtin.base() as isize, + offset: 2, + }; + builtin + .add_page(1, page_start.clone(), 2) + .expect("Failed to add page"); + + // Mock the effects of `final_stack()` + builtin.stop_ptr = Some(4); + + let public_memory = builtin.get_public_memory().unwrap(); + assert_eq!(public_memory, vec![(0, 0), (1, 0), (2, 1), (3, 1)]); + } } diff --git a/vm/src/vm/runners/builtin_runner/signature.rs b/vm/src/vm/runners/builtin_runner/signature.rs index d854afa05a..b2ccfad7e2 100644 --- a/vm/src/vm/runners/builtin_runner/signature.rs +++ b/vm/src/vm/runners/builtin_runner/signature.rs @@ -4,7 +4,7 @@ use crate::stdlib::{cell::RefCell, collections::HashMap, prelude::*, rc::Rc}; use crate::types::errors::math_errors::MathError; use crate::types::instance_definitions::ecdsa_instance_def::CELLS_PER_SIGNATURE; -use crate::vm::runners::cairo_pie::BuiltinAdditionalData; +use crate::vm::runners::cairo_pie::{BuiltinAdditionalData, SignatureBuiltinAdditionalData}; use crate::Felt252; use crate::{ types::{ @@ -239,7 +239,7 @@ impl SignatureBuiltinRunner { ) }) .collect(); - BuiltinAdditionalData::Signature(signatures) + BuiltinAdditionalData::Signature(SignatureBuiltinAdditionalData(signatures)) } pub fn air_private_input(&self, memory: &Memory) -> Vec { @@ -289,6 +289,7 @@ mod tests { }; use crate::felt_str; + use crate::vm::runners::cairo_pie::SignatureBuiltinAdditionalData; #[cfg(target_arch = "wasm32")] use wasm_bindgen_test::*; @@ -640,7 +641,7 @@ mod tests { )]); assert_eq!( builtin.get_additional_data(), - BuiltinAdditionalData::Signature(signatures) + BuiltinAdditionalData::Signature(SignatureBuiltinAdditionalData(signatures)) ) } } diff --git a/vm/src/vm/runners/cairo_pie.rs b/vm/src/vm/runners/cairo_pie.rs index 385cbefb48..b822805eef 100644 --- a/vm/src/vm/runners/cairo_pie.rs +++ b/vm/src/vm/runners/cairo_pie.rs @@ -9,6 +9,9 @@ use super::cairo_runner::ExecutionResources; use crate::serde::deserialize_utils::deserialize_biguint_from_number; use crate::stdlib::prelude::{String, Vec}; use crate::types::errors::cairo_pie_error::{CairoPieError, DeserializeMemoryError}; +use crate::vm::runners::builtin_runner::{ + HASH_BUILTIN_NAME, OUTPUT_BUILTIN_NAME, SIGNATURE_BUILTIN_NAME, +}; use crate::{ serde::deserialize_program::BuiltinName, stdlib::{collections::HashMap, prelude::*}, @@ -66,6 +69,15 @@ pub struct OutputBuiltinAdditionalData { pub attributes: Attributes, } +#[derive(Serialize, Clone, Debug, PartialEq, Eq)] +pub struct SignatureBuiltinAdditionalData(pub HashMap); + +impl Default for SignatureBuiltinAdditionalData { + fn default() -> Self { + Self(HashMap::default()) + } +} + #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)] #[serde(untagged)] pub enum BuiltinAdditionalData { @@ -74,17 +86,61 @@ pub enum BuiltinAdditionalData { Hash(Vec), Output(OutputBuiltinAdditionalData), // Signatures are composed of (r, s) tuples - #[serde(serialize_with = "serde_impl::serialize_signature_additional_data")] - Signature(HashMap), + Signature(SignatureBuiltinAdditionalData), None, } +#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq, Eq)] +pub struct AdditionalData { + #[serde(skip_serializing_if = "Option::is_none")] + pub output_builtin: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub pedersen_builtin: Option>, + #[serde(skip_serializing_if = "Option::is_none")] + pub ecdsa_builtin: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub range_check_builtin: Option<()>, +} + +impl AdditionalData { + pub fn is_empty(&self) -> bool { + self.output_builtin.is_none() + && self.pedersen_builtin.is_none() + && self.ecdsa_builtin.is_none() + && self.range_check_builtin.is_none() + } +} + +impl From> for AdditionalData { + fn from(mut value: HashMap) -> Self { + let output_builtin_data = match value.remove(OUTPUT_BUILTIN_NAME) { + Some(BuiltinAdditionalData::Output(output_data)) => Some(output_data), + _ => None, + }; + let ecdsa_builtin_data = match value.remove(SIGNATURE_BUILTIN_NAME) { + Some(BuiltinAdditionalData::Signature(signature_data)) => Some(signature_data), + _ => None, + }; + let pedersen_builtin_data = match value.remove(HASH_BUILTIN_NAME) { + Some(BuiltinAdditionalData::Hash(pedersen_data)) => Some(pedersen_data), + _ => None, + }; + + Self { + output_builtin: output_builtin_data, + ecdsa_builtin: ecdsa_builtin_data, + pedersen_builtin: pedersen_builtin_data, + range_check_builtin: None, + } + } +} + #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)] pub struct CairoPie { pub metadata: CairoPieMetadata, pub memory: CairoPieMemory, pub execution_resources: ExecutionResources, - pub additional_data: HashMap, + pub additional_data: AdditionalData, pub version: CairoPieVersion, } @@ -127,23 +183,23 @@ fn read_memory_file( addr_size: usize, felt_size: usize, ) -> Result { - let memory_cell_size = addr_size + felt_size; + let pair_size = addr_size + felt_size; let mut memory = CairoPieMemory::new(); let mut pos: usize = 0; loop { - let mut element = vec![0; memory_cell_size]; - match reader.read(&mut element) { - Ok(n) => { - if n == 0 { - break; - } - if n != memory_cell_size { - return Err(DeserializeMemoryError::UnexpectedEof); - } - } - Err(e) => return Err(e.into()), + let mut element = Vec::with_capacity(pair_size); + let n = reader + .by_ref() + .take(pair_size as u64) + .read_to_end(&mut element)?; + if n == 0 { + break; } + if n != pair_size { + return Err(DeserializeMemoryError::UnexpectedEof); + } + let (address_bytes, value_bytes) = element.split_at(addr_size); let address = maybe_relocatable_from_le_bytes(address_bytes); let value = maybe_relocatable_from_le_bytes(value_bytes); @@ -159,7 +215,7 @@ fn read_memory_file( return Err(DeserializeMemoryError::AddressIsNotRelocatable(pos)); } } - pos += memory_cell_size; + pos += pair_size; } Ok(memory) @@ -169,12 +225,11 @@ impl CairoPie { #[cfg(feature = "std")] pub fn from_zip_archive( mut zip: zip::ZipArchive, - ) -> Result { + ) -> Result { let metadata: CairoPieMetadata = parse_zip_file(zip.by_name("metadata.json")?)?; let execution_resources: ExecutionResources = parse_zip_file(zip.by_name("execution_resources.json")?)?; - let additional_data: HashMap = - parse_zip_file(zip.by_name("additional_data.json")?)?; + let additional_data: AdditionalData = parse_zip_file(zip.by_name("additional_data.json")?)?; let version: CairoPieVersion = parse_zip_file(zip.by_name("version.json")?)?; let addr_size: usize = 8; @@ -187,7 +242,7 @@ impl CairoPie { }; let memory = read_memory_file(zip.by_name("memory.bin")?, addr_size, felt_bytes)?; - Ok(CairoPie { + Ok(Self { metadata, memory, execution_resources, @@ -197,11 +252,19 @@ impl CairoPie { } #[cfg(feature = "std")] - pub fn from_file(path: &Path) -> Result { + pub fn from_bytes(bytes: &[u8]) -> Result { + let reader = std::io::Cursor::new(bytes); + let zip_archive = zip::ZipArchive::new(reader)?; + + Self::from_zip_archive(zip_archive) + } + + #[cfg(feature = "std")] + pub fn from_file(path: &Path) -> Result { let file = std::fs::File::open(path)?; let zip = zip::ZipArchive::new(file)?; - CairoPie::from_zip_archive(zip) + Self::from_zip_archive(zip) } } @@ -266,14 +329,16 @@ mod serde_impl { Felt252, }; use num_bigint::BigUint; - use serde::de::SeqAccess; - use serde::{de, ser::SerializeSeq, Deserializer, Serialize, Serializer}; + use serde::de::{MapAccess, SeqAccess}; + use serde::{de, ser::SerializeSeq, Deserialize, Deserializer, Serialize, Serializer}; use serde_json::Number; use std::fmt; + use std::fmt::Formatter; use crate::serde::deserialize_utils::felt_from_number; use crate::utils::CAIRO_PRIME; + use crate::vm::runners::cairo_pie::SignatureBuiltinAdditionalData; pub const ADDR_BYTE_LEN: usize = 8; pub const FIELD_BYTE_LEN: usize = 32; @@ -455,27 +520,6 @@ mod serde_impl { d.deserialize_seq(MaybeRelocatableNumberVisitor) } - pub fn serialize_signature_additional_data( - values: &HashMap, - serializer: S, - ) -> Result - where - S: Serializer, - { - let mut seq_serializer = serializer.serialize_seq(Some(values.len()))?; - - for (key, (x, y)) in values { - seq_serializer.serialize_element(&[ - [ - Felt252Wrapper(&Felt252::from(key.segment_index)), - Felt252Wrapper(&Felt252::from(key.offset)), - ], - [Felt252Wrapper(x), Felt252Wrapper(y)], - ])?; - } - seq_serializer.end() - } - pub fn serialize_hash_additional_data( values: &[Relocatable], serializer: S, @@ -491,6 +535,61 @@ mod serde_impl { seq_serializer.end() } + + struct SignatureBuiltinAdditionalDataVisitor; + + impl<'de> de::Visitor<'de> for SignatureBuiltinAdditionalDataVisitor { + type Value = SignatureBuiltinAdditionalData; + + fn expecting(&self, formatter: &mut Formatter) -> fmt::Result { + write!( + formatter, + "a Vec<(Relocatable, (Felt252, Felt252))> or a HashMap" + ) + } + + fn visit_seq(self, mut seq: A) -> Result + where + A: SeqAccess<'de>, + { + let mut map = HashMap::with_capacity(seq.size_hint().unwrap_or(0)); + + // While there are entries remaining in the input, add them + // into our map. + while let Some((key, value)) = seq.next_element()? { + map.insert(key, value); + } + + Ok(SignatureBuiltinAdditionalData(map)) + } + + fn visit_map(self, mut access: A) -> Result + where + A: MapAccess<'de>, + { + let mut map = HashMap::with_capacity(access.size_hint().unwrap_or(0)); + + // While there are entries remaining in the input, add them + // into our map. + while let Some((key, value)) = access.next_entry()? { + map.insert(key, value); + } + + Ok(SignatureBuiltinAdditionalData(map)) + } + } + + // This is the trait that informs Serde how to deserialize MyMap. + impl<'de> Deserialize<'de> for SignatureBuiltinAdditionalData { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + // Instantiate our Visitor and ask the Deserializer to drive + // it over the input data, resulting in an instance of MyMap. + deserializer.deserialize_any(SignatureBuiltinAdditionalDataVisitor {}) + } + } } #[cfg(test)] @@ -658,16 +757,59 @@ mod test { assert_eq!( cairo_pie.additional_data, - HashMap::from([( - "output_builtin".to_string(), - BuiltinAdditionalData::Output(OutputBuiltinAdditionalData { + AdditionalData { + output_builtin: Some(OutputBuiltinAdditionalData { base: 0, pages: Default::default(), attributes: Default::default(), - }) - )]) + }), + pedersen_builtin: None, + ecdsa_builtin: None, + range_check_builtin: None, + } ); assert_eq!(cairo_pie.version.cairo_pie, CAIRO_PIE_VERSION); } + + #[test] + fn test_deserialize_additional_data() { + let data = include_bytes!( + "../../../../cairo_programs/manually_compiled/pie_additional_data_test.json" + ); + let additional_data: AdditionalData = serde_json::from_slice(data).unwrap(); + let output_data = additional_data.output_builtin.unwrap(); + assert_eq!( + output_data.pages, + HashMap::from([( + 1, + PublicMemoryPage { + start: 18, + size: 46, + } + )]) + ); + assert_eq!( + output_data.attributes, + HashMap::from([("gps_fact_topology".to_string(), vec![2, 1, 0, 2])]) + ); + let pedersen_data = additional_data.pedersen_builtin.unwrap(); + assert_eq!( + pedersen_data, + vec![ + Relocatable::from((3, 2)), + Relocatable::from((3, 5)), + Relocatable::from((3, 8)), + Relocatable::from((3, 11)), + Relocatable::from((3, 14)), + Relocatable::from((3, 17)), + ] + ); + // TODO: add a test case with signature data + let expected_signature_additional_data = Some(SignatureBuiltinAdditionalData::default()); + assert_eq!( + additional_data.ecdsa_builtin, + expected_signature_additional_data + ); + } } diff --git a/vm/src/vm/runners/cairo_runner.rs b/vm/src/vm/runners/cairo_runner.rs index 15cd1c2088..ee3e050478 100644 --- a/vm/src/vm/runners/cairo_runner.rs +++ b/vm/src/vm/runners/cairo_runner.rs @@ -14,7 +14,7 @@ use crate::{ }, }; -use crate::vm::runners::cairo_pie::CAIRO_PIE_VERSION; +use crate::vm::runners::cairo_pie::{BuiltinAdditionalData, CAIRO_PIE_VERSION}; use crate::Felt252; use crate::{ hint_processor::hint_processor_definition::{HintProcessor, HintReference}, @@ -56,7 +56,7 @@ use num_traits::{ToPrimitive, Zero}; use serde::{Deserialize, Serialize}; use super::{ - builtin_runner::{KeccakBuiltinRunner, PoseidonBuiltinRunner, OUTPUT_BUILTIN_NAME}, + builtin_runner::{KeccakBuiltinRunner, PoseidonBuiltinRunner}, cairo_pie::{self, CairoPie, CairoPieMetadata, CairoPieVersion}, }; @@ -1091,8 +1091,8 @@ impl CairoRunner { let (_, size) = builtin_runner .get_used_cells_and_allocated_size(vm) .map_err(RunnerError::FinalizeSegements)?; - if builtin_runner.name() == OUTPUT_BUILTIN_NAME { - let public_memory = (0..size).map(|i| (i, 0)).collect(); + if let BuiltinRunner::Output(output_builtin) = builtin_runner { + let public_memory = output_builtin.get_public_memory()?; vm.segments .finalize(Some(size), builtin_runner.base(), Some(&public_memory)) } else { @@ -1403,7 +1403,8 @@ impl CairoRunner { .builtin_runners .iter() .map(|b| (b.name().to_string(), b.get_additional_data())) - .collect(), + .collect::>() + .into(), version: CairoPieVersion { cairo_pie: CAIRO_PIE_VERSION.to_string(), },