cairo_vm/hint_processor/
hint_processor_definition.rs

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