1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
use crate::stdlib::{any::Any, boxed::Box, collections::HashMap, prelude::*};

use crate::any_box;
use crate::serde::deserialize_program::ApTracking;
use crate::serde::deserialize_program::OffsetValue;
use crate::serde::deserialize_program::Reference;
use crate::types::exec_scope::ExecutionScopes;
use crate::types::instruction::Register;
use crate::types::relocatable::Relocatable;
use crate::vm::errors::hint_errors::HintError;
use crate::vm::errors::vm_errors::VirtualMachineError;
use crate::vm::runners::cairo_runner::ResourceTracker;
use crate::vm::vm_core::VirtualMachine;

use super::builtin_hint_processor::builtin_hint_processor_definition::HintProcessorData;
use crate::Felt252;

#[cfg(feature = "test_utils")]
use arbitrary::Arbitrary;

pub trait HintProcessorLogic {
    // Executes the hint which's data is provided by a dynamic structure previously created by compile_hint
    // 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.
    fn execute_hint(
        &mut self,
        vm: &mut VirtualMachine,
        exec_scopes: &mut ExecutionScopes,
        //Data structure that can be downcasted to the structure generated by compile_hint
        hint_data: &Box<dyn Any>,
        //Constant values extracted from the program specification.
        constants: &HashMap<String, Felt252>,
    ) -> Result<(), HintError>;

    //Transforms hint data outputed by the VM into whichever format will be later used by execute_hint
    fn compile_hint(
        &self,
        //Block of hint code as String
        hint_code: &str,
        //Ap Tracking Data corresponding to the Hint
        ap_tracking_data: &ApTracking,
        //Map from variable name to reference id number
        //(may contain other variables aside from those used by the hint)
        reference_ids: &HashMap<String, usize>,
        //List of all references (key corresponds to element of the previous dictionary)
        references: &[HintReference],
    ) -> Result<Box<dyn Any>, VirtualMachineError> {
        Ok(any_box!(HintProcessorData {
            code: hint_code.to_string(),
            ap_tracking: ap_tracking_data.clone(),
            ids_data: get_ids_data(reference_ids, references)?,
        }))
    }

    #[cfg(feature = "extensive_hints")]
    // Executes the hint which's data is provided by a dynamic structure previously created by compile_hint
    // Also returns a map of hints to be loaded after the current hint is executed
    // Note: This is the method used by the vm to execute hints,
    // if you chose to implement this method instead of using the default implementation, then `execute_hint` will not be used
    fn execute_hint_extensive(
        &mut self,
        vm: &mut VirtualMachine,
        exec_scopes: &mut ExecutionScopes,
        //Data structure that can be downcasted to the structure generated by compile_hint
        hint_data: &Box<dyn Any>,
        //Constant values extracted from the program specification.
        constants: &HashMap<String, Felt252>,
    ) -> Result<HintExtension, HintError> {
        self.execute_hint(vm, exec_scopes, hint_data, constants)?;
        Ok(HintExtension::default())
    }
}

// A map of hints that can be used to extend the current map of hints for the vm run
// The map matches the pc at which the hints should be executed to a vec of compiled hints (Outputed by HintProcessor::CompileHint)
pub type HintExtension = HashMap<Relocatable, Vec<Box<dyn Any>>>;

pub trait HintProcessor: HintProcessorLogic + ResourceTracker {}
impl<T> HintProcessor for T where T: HintProcessorLogic + ResourceTracker {}

fn get_ids_data(
    reference_ids: &HashMap<String, usize>,
    references: &[HintReference],
) -> Result<HashMap<String, HintReference>, VirtualMachineError> {
    let mut ids_data = HashMap::<String, HintReference>::new();
    for (path, ref_id) in reference_ids {
        let name = path
            .rsplit('.')
            .next()
            .ok_or(VirtualMachineError::Unexpected)?;
        ids_data.insert(
            name.to_string(),
            references
                .get(*ref_id)
                .ok_or(VirtualMachineError::Unexpected)?
                .clone(),
        );
    }
    Ok(ids_data)
}

#[cfg_attr(feature = "test_utils", derive(Arbitrary))]
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct HintReference {
    pub offset1: OffsetValue,
    pub offset2: OffsetValue,
    pub inner_dereference: bool,
    pub outer_dereference: bool,
    pub ap_tracking_data: Option<ApTracking>,
    pub cairo_type: Option<String>,
}

impl HintReference {
    pub fn new_simple(offset1: i32) -> Self {
        HintReference {
            offset1: OffsetValue::Reference(Register::FP, offset1, false),
            offset2: OffsetValue::Value(0),
            ap_tracking_data: None,
            outer_dereference: true,
            inner_dereference: false,
            cairo_type: None,
        }
    }

    pub fn new(offset1: i32, offset2: i32, inner_dereference: bool, dereference: bool) -> Self {
        HintReference {
            offset1: OffsetValue::Reference(Register::FP, offset1, inner_dereference),
            offset2: OffsetValue::Value(offset2),
            ap_tracking_data: None,
            outer_dereference: dereference,
            inner_dereference: false,
            cairo_type: None,
        }
    }
}

impl From<Reference> for HintReference {
    fn from(reference: Reference) -> Self {
        HintReference {
            offset1: reference.value_address.offset1.clone(),
            offset2: reference.value_address.offset2.clone(),
            outer_dereference: reference.value_address.outer_dereference,
            inner_dereference: reference.value_address.inner_dereference,
            // only store `ap` tracking data if the reference is referred to it
            ap_tracking_data: match (
                &reference.value_address.offset1,
                &reference.value_address.offset2,
            ) {
                (OffsetValue::Reference(Register::AP, _, _), _)
                | (_, OffsetValue::Reference(Register::AP, _, _)) => {
                    Some(reference.ap_tracking_data.clone())
                }
                _ => None,
            },
            cairo_type: Some(reference.value_address.value_type.clone()),
        }
    }
}