diff --git a/Cargo.toml b/Cargo.toml index 72862d1..054cd3c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,3 +19,5 @@ thiserror-no-std = "2.0.2" assert_matches = "1.5.0" rstest = "0.19.0" +[patch."https://github.com/lambdaclass/cairo-vm"] +cairo-vm = { path = "../cairo-vm/vm" } diff --git a/src/hints/execute_task_hints.rs b/src/hints/execute_task_hints.rs index c1becaa..14b546d 100644 --- a/src/hints/execute_task_hints.rs +++ b/src/hints/execute_task_hints.rs @@ -4,7 +4,7 @@ use std::collections::HashMap; use cairo_vm::hint_processor::builtin_hint_processor::hint_utils::{ get_ptr_from_var_name, get_relocatable_from_var_name, insert_value_from_var_name, }; -use cairo_vm::hint_processor::hint_processor_definition::HintReference; +use cairo_vm::hint_processor::hint_processor_definition::{HintExtension, HintReference}; use cairo_vm::serde::deserialize_program::{ApTracking, Identifier}; use cairo_vm::types::builtin_name::BuiltinName; use cairo_vm::types::exec_scope::ExecutionScopes; @@ -70,7 +70,7 @@ pub fn load_program_hint( exec_scopes: &mut ExecutionScopes, ids_data: &HashMap, ap_tracking: &ApTracking, -) -> Result<(), HintError> { +) -> Result { let program_data_base: Relocatable = exec_scopes.get(vars::PROGRAM_DATA_BASE)?; let task: Task = exec_scopes.get(vars::TASK)?; let program = get_program_from_task(&task)?; @@ -85,6 +85,12 @@ pub fn load_program_hint( .load_program(program_header_ptr, &program, Some(bootloader_version)) .map_err(Into::::into)?; + // if task is a Program, attempt to load hints from it + let mut hint_extension = HintExtension::new(); + if let Task::Program(program) = task { + hint_extension.extend(program_loader.load_hints(&program, program_header_ptr, ap_tracking)?); + } + vm.segments.finalize( Some(loaded_program.size), program_data_base.segment_index as usize, @@ -93,7 +99,7 @@ pub fn load_program_hint( exec_scopes.insert_value(vars::PROGRAM_ADDRESS, loaded_program.code_address); - Ok(()) + Ok(hint_extension) } /// Implements diff --git a/src/hints/hint_processors.rs b/src/hints/hint_processors.rs index 4e85cc5..2196fbd 100644 --- a/src/hints/hint_processors.rs +++ b/src/hints/hint_processors.rs @@ -107,7 +107,6 @@ impl HintProcessorLogic for MinimalBootloaderHintProcessor { EXECUTE_TASK_ALLOCATE_PROGRAM_DATA_SEGMENT => { allocate_program_data_segment(vm, exec_scopes, ids_data, ap_tracking) } - EXECUTE_TASK_LOAD_PROGRAM => load_program_hint(vm, exec_scopes, ids_data, ap_tracking), EXECUTE_TASK_VALIDATE_HASH => validate_hash(vm, exec_scopes, ids_data, ap_tracking), EXECUTE_TASK_ASSERT_PROGRAM_ADDRESS => { assert_program_address(vm, exec_scopes, ids_data, ap_tracking) @@ -131,6 +130,29 @@ impl HintProcessorLogic for MinimalBootloaderHintProcessor { )), } } + + /// Executes extensive hints as appropriate, then falls back to non-extensive hints. + fn execute_hint_extensive( + &mut self, + vm: &mut VirtualMachine, + exec_scopes: &mut ExecutionScopes, + hint_data_any: &Box, + constants: &HashMap, + ) -> Result { + let hint_data = hint_data_any + .downcast_ref::() + .ok_or(HintError::WrongHintData)?; + + let ids_data = &hint_data.ids_data; + let ap_tracking = &hint_data.ap_tracking; + + let mut hint_extensions = HintExtension::default(); + match hint_data.code.as_str() { + EXECUTE_TASK_LOAD_PROGRAM => hint_extensions.extend(load_program_hint(vm, exec_scopes, ids_data, ap_tracking)?), + _ => self.execute_hint(vm, exec_scopes, hint_data_any, constants)?, + } + Ok(hint_extensions) + } } impl ResourceTracker for MinimalBootloaderHintProcessor {} diff --git a/src/hints/program_loader.rs b/src/hints/program_loader.rs index bbdadd7..b843207 100644 --- a/src/hints/program_loader.rs +++ b/src/hints/program_loader.rs @@ -1,11 +1,17 @@ +use std::collections::HashMap; + +use cairo_vm::hint_processor::builtin_hint_processor::builtin_hint_processor_definition::{BuiltinHintProcessor, HintProcessorData}; +use cairo_vm::hint_processor::hint_processor_definition::{HintExtension, HintProcessorLogic}; +use cairo_vm::serde::deserialize_program::ApTracking; use cairo_vm::types::builtin_name::BuiltinName; use cairo_vm::types::errors::math_errors::MathError; +use cairo_vm::types::program::Program; use cairo_vm::types::relocatable::Relocatable; use cairo_vm::vm::errors::hint_errors::HintError; use cairo_vm::vm::errors::memory_errors::MemoryError; use cairo_vm::vm::runners::cairo_pie::StrippedProgram; use cairo_vm::vm::vm_core::VirtualMachine; -use cairo_vm::Felt252; +use cairo_vm::{any_box, Felt252}; use crate::hints::types::BootloaderVersion; @@ -161,6 +167,54 @@ impl<'vm> ProgramLoader<'vm> { size: header_size + program.data.len(), }) } + + /// Extracts and relocates the hints found in the given program and returns the result in a way + /// that is suitable to be used in cairo-vm's "extensive hints" feature. + pub fn load_hints( + &self, + program: &Program, + program_base: Relocatable, + ap_tracking: &ApTracking, + ) -> Result { + // TODO: these structs are private (pub(crate)) + let hints_collection = program.shared_program_data.hints_collection; + let mut hint_extension = HintExtension::new(); + for (pc, hint_range) in hints_collection.hints_ranges { + let relocated_pc = pc + program_base; + let hint_code = "FIXME"; // TODO + let hint_processor_data = HintProcessorData { + code: hint_code.to_string(), + ap_tracking: ap_tracking.clone(), + ids_data: Default::default(), // TODO + }; + // TODO: more expressive way to do this + if !hint_extension.contains_key(relocated_pc) { + hint_extension.insert(relocated_pc.clone(), Vec::new()); + } + hint_extension + .get(relocated_pc) + .expect("value inserted if missing abev, QED") + .push(any_box!(hint_processor_data)); + } + + // TODO: we ultimately want these remapped_hints to be part of the hints map used during + // execution, and it should use the extensive hints feature added in + // https://github.com/lambdaclass/cairo-vm/pull/1491 + // + // this could either work with self.vm directly (?) or could return something that is used + // by the caller to initialize the hint processor. + // cairo-lang's load_hints works differently: it is called from load_program and stores + // the remapped results as a private member for later use. (our load_program() takes a + // StrippedProgram instead of a Program which makes this pattern impossible currently) + // + // Note: we are already in a hint (by nature of not being in cairo code), so we can use the hint extension + // feature of blockifier (where a hint returns the hint extensions), so that just means we need to + // propagate the remapped hints back to to caller until we return from a hint + + // load program hint is where we are + + Ok(hint_extension) + } } #[cfg(test)] @@ -312,6 +366,7 @@ mod tests { #[rstest] fn test_load_program(fibonacci: Program) { + let program = fibonacci.get_stripped_program().unwrap(); let mut vm = VirtualMachine::new(false);