cairo_vm/hint_processor/
hint_processor_definition.rs

1use crate::stdlib::{any::Any, boxed::Box, collections::HashMap, prelude::*};
2
3use crate::any_box;
4use crate::serde::deserialize_program::ApTracking;
5use crate::serde::deserialize_program::OffsetValue;
6use crate::serde::deserialize_program::Reference;
7use crate::types::exec_scope::ExecutionScopes;
8use crate::types::instruction::Register;
9use crate::types::relocatable::Relocatable;
10use crate::vm::errors::hint_errors::HintError;
11use crate::vm::errors::vm_errors::VirtualMachineError;
12use crate::vm::runners::cairo_runner::ResourceTracker;
13use crate::vm::vm_core::VirtualMachine;
14
15use super::builtin_hint_processor::builtin_hint_processor_definition::HintProcessorData;
16use crate::Felt252;
17
18#[cfg(feature = "test_utils")]
19use arbitrary::Arbitrary;
20
21pub trait HintProcessorLogic {
22    // Executes the hint which's data is provided by a dynamic structure previously created by compile_hint
23    // Note: if the `extensive_hints` feature is activated the method used by the vm to execute hints is `execute_hint_extensive`, which's default implementation calls this method.
24    fn execute_hint(
25        &mut self,
26        vm: &mut VirtualMachine,
27        exec_scopes: &mut ExecutionScopes,
28        //Data structure that can be downcasted to the structure generated by compile_hint
29        hint_data: &Box<dyn Any>,
30        //Constant values extracted from the program specification.
31        constants: &HashMap<String, Felt252>,
32    ) -> Result<(), HintError>;
33
34    //Transforms hint data outputed by the VM into whichever format will be later used by execute_hint
35    fn compile_hint(
36        &self,
37        //Block of hint code as String
38        hint_code: &str,
39        //Ap Tracking Data corresponding to the Hint
40        ap_tracking_data: &ApTracking,
41        //Map from variable name to reference id number
42        //(may contain other variables aside from those used by the hint)
43        reference_ids: &HashMap<String, usize>,
44        //List of all references (key corresponds to element of the previous dictionary)
45        references: &[HintReference],
46    ) -> Result<Box<dyn Any>, VirtualMachineError> {
47        Ok(any_box!(HintProcessorData {
48            code: hint_code.to_string(),
49            ap_tracking: ap_tracking_data.clone(),
50            ids_data: get_ids_data(reference_ids, references)?,
51        }))
52    }
53
54    #[cfg(feature = "extensive_hints")]
55    // Executes the hint which's data is provided by a dynamic structure previously created by compile_hint
56    // Also returns a map of hints to be loaded after the current hint is executed
57    // Note: This is the method used by the vm to execute hints,
58    // if you chose to implement this method instead of using the default implementation, then `execute_hint` will not be used
59    fn execute_hint_extensive(
60        &mut self,
61        vm: &mut VirtualMachine,
62        exec_scopes: &mut ExecutionScopes,
63        //Data structure that can be downcasted to the structure generated by compile_hint
64        hint_data: &Box<dyn Any>,
65        //Constant values extracted from the program specification.
66        constants: &HashMap<String, Felt252>,
67    ) -> Result<HintExtension, HintError> {
68        self.execute_hint(vm, exec_scopes, hint_data, constants)?;
69        Ok(HintExtension::default())
70    }
71}
72
73// A map of hints that can be used to extend the current map of hints for the vm run
74// The map matches the pc at which the hints should be executed to a vec of compiled hints (Outputed by HintProcessor::CompileHint)
75pub type HintExtension = HashMap<Relocatable, Vec<Box<dyn Any>>>;
76
77pub trait HintProcessor: HintProcessorLogic + ResourceTracker {}
78impl<T> HintProcessor for T where T: HintProcessorLogic + ResourceTracker {}
79
80fn get_ids_data(
81    reference_ids: &HashMap<String, usize>,
82    references: &[HintReference],
83) -> Result<HashMap<String, HintReference>, VirtualMachineError> {
84    let mut ids_data = HashMap::<String, HintReference>::new();
85    for (path, ref_id) in reference_ids {
86        let name = path
87            .rsplit('.')
88            .next()
89            .ok_or(VirtualMachineError::Unexpected)?;
90        ids_data.insert(
91            name.to_string(),
92            references
93                .get(*ref_id)
94                .ok_or(VirtualMachineError::Unexpected)?
95                .clone(),
96        );
97    }
98    Ok(ids_data)
99}
100
101#[cfg_attr(feature = "test_utils", derive(Arbitrary))]
102#[derive(Debug, PartialEq, Eq, Clone)]
103pub struct HintReference {
104    pub offset1: OffsetValue,
105    pub offset2: OffsetValue,
106    pub inner_dereference: bool,
107    pub outer_dereference: bool,
108    pub ap_tracking_data: Option<ApTracking>,
109    pub cairo_type: Option<String>,
110}
111
112impl HintReference {
113    pub fn new_simple(offset1: i32) -> Self {
114        HintReference {
115            offset1: OffsetValue::Reference(Register::FP, offset1, false, true),
116            offset2: OffsetValue::Value(0),
117            ap_tracking_data: None,
118            outer_dereference: true,
119            inner_dereference: false,
120            cairo_type: None,
121        }
122    }
123
124    pub fn new(
125        offset1: i32,
126        offset2: i32,
127        inner_dereference: bool,
128        dereference: bool,
129        is_positive: bool,
130    ) -> Self {
131        HintReference {
132            offset1: OffsetValue::Reference(Register::FP, offset1, inner_dereference, is_positive),
133            offset2: OffsetValue::Value(offset2),
134            ap_tracking_data: None,
135            outer_dereference: dereference,
136            inner_dereference: false,
137            cairo_type: None,
138        }
139    }
140}
141
142impl From<Reference> for HintReference {
143    fn from(reference: Reference) -> Self {
144        HintReference {
145            offset1: reference.value_address.offset1.clone(),
146            offset2: reference.value_address.offset2.clone(),
147            outer_dereference: reference.value_address.outer_dereference,
148            inner_dereference: reference.value_address.inner_dereference,
149            // only store `ap` tracking data if the reference is referred to it
150            ap_tracking_data: match (
151                &reference.value_address.offset1,
152                &reference.value_address.offset2,
153            ) {
154                (OffsetValue::Reference(Register::AP, _, _, _), _)
155                | (_, OffsetValue::Reference(Register::AP, _, _, _)) => {
156                    Some(reference.ap_tracking_data.clone())
157                }
158                _ => None,
159            },
160            cairo_type: Some(reference.value_address.value_type.clone()),
161        }
162    }
163}