cairo_vm/hint_processor/
hint_processor_definition.rs

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