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
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::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 felt::Felt252;

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

pub trait HintProcessorLogic {
    //Executes the hint which's data is provided by a dynamic structure previously created by compile_hint
    fn execute_hint(
        &mut self,
        //Proxy to VM, contains refrences to necessary data
        //+ MemoryProxy, which provides the necessary methods to manipulate memory
        vm: &mut VirtualMachine,
        //Proxy to ExecutionScopes, provides the necessary methods to manipulate the scopes and
        //access current scope variables
        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)?,
        }))
    }
}

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 = "arbitrary", derive(Arbitrary))]
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct HintReference {
    pub offset1: OffsetValue,
    pub offset2: OffsetValue,
    pub 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,
            dereference: true,
            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,
            dereference,
            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(),
            dereference: reference.value_address.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()),
        }
    }
}