Skip to main content

cairo_vm/hint_processor/builtin_hint_processor/
hint_utils.rs

1use std::{any::Any, collections::HashMap};
2
3use crate::Felt252;
4
5use crate::hint_processor::hint_processor_definition::HintReference;
6use crate::hint_processor::hint_processor_utils::{
7    compute_addr_from_reference, get_ptr_from_reference,
8};
9use crate::hint_processor::hint_processor_utils::{
10    get_integer_from_reference, get_maybe_relocatable_from_reference,
11};
12use crate::serde::deserialize_program::ApTracking;
13use crate::types::exec_scope::ExecutionScopes;
14use crate::types::relocatable::MaybeRelocatable;
15use crate::types::relocatable::Relocatable;
16use crate::vm::errors::hint_errors::HintError;
17use crate::vm::vm_core::VirtualMachine;
18
19/// Generates a const string for each hint, and a lazy_static HashMap that maps the const name to
20/// the hint string.
21/// Allows gating specific hints behind feature gates.
22///
23/// # Examples
24///
25/// ```
26/// # #[macro_use] extern crate cairo_vm;
27/// # use std::collections::HashMap;
28/// cairo_vm::define_hint_string_map!(
29///     FOO_HINTS,
30///     (FOO_HINT_ADD_X_Y, "x + y"),
31///     (FOO_HINT_PRINT_X, "print(x)", "test_utils")
32/// );
33/// ```
34///
35/// This will generate the following code:
36///
37/// ```
38/// # use std::collections::HashMap;
39/// pub const FOO_HINT_ADD_X_Y: &str = "x + y";
40/// #[cfg(feature = "test_utils")]
41/// pub const FOO_HINT_PRINT_X: &str = "print(x)";
42///
43/// lazy_static::lazy_static! {
44///     pub static ref FOO_HINTS: HashMap<&'static str, &'static str> = {
45///         let mut map = HashMap::new();
46///         map.insert("FOO_HINT_ADD_X_Y", FOO_HINT_ADD_X_Y);
47///         #[cfg(feature = "test_utils")]
48///         map.insert("FOO_HINT_PRINT_X", FOO_HINT_PRINT_X);
49///         map
50///     };
51/// }
52/// ```
53#[macro_export]
54macro_rules! define_hint_string_map {
55    ($hint_set_name:ident, $(($hint_name:ident, $hint_str:expr $(, $feature_gate:expr)?)),+) => {
56        $(
57            $(#[cfg(feature = $feature_gate)])?
58            pub const $hint_name: &str = $hint_str;
59        )+
60
61        lazy_static::lazy_static! {
62            pub static ref $hint_set_name: HashMap<&'static str, &'static str> = {
63                let mut map = HashMap::new();
64                $(
65                    $(#[cfg(feature = $feature_gate)])?
66                    map.insert(stringify!($hint_name), $hint_name);
67                )+
68                map
69            };
70        }
71    }
72}
73
74/// Enters a new execution scope containing a single variable `n` sourced from `ids.<source_var_name>`.
75pub(crate) fn enter_scope_with_n_from_var_name(
76    source_var_name: &str,
77    vm: &mut VirtualMachine,
78    exec_scopes: &mut ExecutionScopes,
79    ids_data: &HashMap<String, HintReference>,
80    ap_tracking: &ApTracking,
81) -> Result<(), HintError> {
82    let n: Box<dyn Any> = Box::new(get_integer_from_var_name(
83        source_var_name,
84        vm,
85        ids_data,
86        ap_tracking,
87    )?);
88    exec_scopes.enter_scope(HashMap::from([(String::from("n"), n)]));
89    Ok(())
90}
91
92//Inserts value into the address of the given ids variable
93pub fn insert_value_from_var_name(
94    var_name: &str,
95    value: impl Into<MaybeRelocatable>,
96    vm: &mut VirtualMachine,
97    ids_data: &HashMap<String, HintReference>,
98    ap_tracking: &ApTracking,
99) -> Result<(), HintError> {
100    let var_address = get_relocatable_from_var_name(var_name, vm, ids_data, ap_tracking)?;
101    vm.insert_value(var_address, value)
102        .map_err(HintError::Memory)
103}
104
105//Inserts value into ap
106pub fn insert_value_into_ap(
107    vm: &mut VirtualMachine,
108    value: impl Into<MaybeRelocatable>,
109) -> Result<(), HintError> {
110    vm.insert_value(vm.get_ap(), value)
111        .map_err(HintError::Memory)
112}
113
114//Returns the Relocatable value stored in the given ids variable
115pub fn get_ptr_from_var_name(
116    var_name: &str,
117    vm: &VirtualMachine,
118    ids_data: &HashMap<String, HintReference>,
119    ap_tracking: &ApTracking,
120) -> Result<Relocatable, HintError> {
121    let reference = get_reference_from_var_name(var_name, ids_data)?;
122    match get_ptr_from_reference(vm, reference, ap_tracking) {
123        // Map internal errors into more descriptive variants
124        Ok(val) => Ok(val),
125        Err(HintError::WrongIdentifierTypeInternal) => Err(HintError::IdentifierNotRelocatable(
126            Box::<str>::from(var_name),
127        )),
128        _ => Err(HintError::UnknownIdentifier(Box::<str>::from(var_name))),
129    }
130}
131
132//Gets the address, as a MaybeRelocatable of the variable given by the ids name
133pub fn get_address_from_var_name(
134    var_name: &str,
135    vm: &mut VirtualMachine,
136    ids_data: &HashMap<String, HintReference>,
137    ap_tracking: &ApTracking,
138) -> Result<MaybeRelocatable, HintError> {
139    get_relocatable_from_var_name(var_name, vm, ids_data, ap_tracking).map(|x| x.into())
140}
141
142//Gets the address, as a Relocatable of the variable given by the ids name
143pub fn get_relocatable_from_var_name(
144    var_name: &str,
145    vm: &VirtualMachine,
146    ids_data: &HashMap<String, HintReference>,
147    ap_tracking: &ApTracking,
148) -> Result<Relocatable, HintError> {
149    ids_data
150        .get(var_name)
151        .and_then(|x| compute_addr_from_reference(x, vm, ap_tracking))
152        .ok_or_else(|| HintError::UnknownIdentifier(Box::<str>::from(var_name)))
153}
154
155//Gets the value of a variable name.
156//If the value is an MaybeRelocatable::Int(Bigint) return &Bigint
157//else raises Err
158pub fn get_integer_from_var_name(
159    var_name: &str,
160    vm: &VirtualMachine,
161    ids_data: &HashMap<String, HintReference>,
162    ap_tracking: &ApTracking,
163) -> Result<Felt252, HintError> {
164    let reference = get_reference_from_var_name(var_name, ids_data)?;
165    match get_integer_from_reference(vm, reference, ap_tracking) {
166        // Map internal errors into more descriptive variants
167        Ok(val) => Ok(val),
168        Err(HintError::WrongIdentifierTypeInternal) => {
169            Err(HintError::IdentifierNotInteger(Box::<str>::from(var_name)))
170        }
171        _ => Err(HintError::UnknownIdentifier(Box::<str>::from(var_name))),
172    }
173}
174
175//Gets the value of a variable name as a MaybeRelocatable
176pub fn get_maybe_relocatable_from_var_name<'a>(
177    var_name: &str,
178    vm: &'a VirtualMachine,
179    ids_data: &'a HashMap<String, HintReference>,
180    ap_tracking: &ApTracking,
181) -> Result<MaybeRelocatable, HintError> {
182    let reference = get_reference_from_var_name(var_name, ids_data)?;
183    get_maybe_relocatable_from_reference(vm, reference, ap_tracking)
184        .ok_or_else(|| HintError::UnknownIdentifier(Box::<str>::from(var_name)))
185}
186
187pub fn get_reference_from_var_name<'a>(
188    var_name: &'a str,
189    ids_data: &'a HashMap<String, HintReference>,
190) -> Result<&'a HintReference, HintError> {
191    ids_data
192        .get(var_name)
193        .ok_or_else(|| HintError::UnknownIdentifier(Box::<str>::from(var_name)))
194}
195
196pub fn get_constant_from_var_name<'a>(
197    var_name: &'static str,
198    constants: &'a HashMap<String, Felt252>,
199) -> Result<&'a Felt252, HintError> {
200    constants
201        .iter()
202        .find(|(k, _)| k.rsplit('.').next() == Some(var_name))
203        .map(|(_, n)| n)
204        .ok_or_else(|| HintError::MissingConstant(Box::new(var_name)))
205}
206
207#[cfg(test)]
208mod tests {
209    use super::*;
210
211    use crate::{
212        hint_processor::hint_processor_definition::HintReference, relocatable,
213        serde::deserialize_program::OffsetValue, utils::test_utils::*,
214        vm::vm_memory::memory::Memory,
215    };
216    use assert_matches::assert_matches;
217
218    #[test]
219    fn get_ptr_from_var_name_immediate_value() {
220        let mut vm = vm!();
221        vm.segments = segments![((1, 0), (0, 0))];
222        let mut hint_ref = HintReference::new(0, 0, true, false, true);
223        hint_ref.offset2 = OffsetValue::Value(2);
224        let ids_data = HashMap::from([("imm".to_string(), hint_ref)]);
225
226        assert_matches!(
227            get_ptr_from_var_name("imm", &vm, &ids_data, &ApTracking::new()),
228            Ok(x) if x == relocatable!(0, 2)
229        );
230    }
231
232    #[test]
233    fn get_maybe_relocatable_from_var_name_valid() {
234        let mut vm = vm!();
235        vm.segments = segments![((1, 0), (0, 0))];
236        let hint_ref = HintReference::new_simple(0);
237        let ids_data = HashMap::from([("value".to_string(), hint_ref)]);
238
239        assert_matches!(
240            get_maybe_relocatable_from_var_name("value", &vm, &ids_data, &ApTracking::new()),
241            Ok(x) if x == mayberelocatable!(0, 0)
242        );
243    }
244
245    #[test]
246    fn get_maybe_relocatable_from_var_name_invalid() {
247        let mut vm = vm!();
248        vm.segments.memory = Memory::new();
249        let hint_ref = HintReference::new_simple(0);
250        let ids_data = HashMap::from([("value".to_string(), hint_ref)]);
251
252        assert_matches!(
253            get_maybe_relocatable_from_var_name("value", &vm, &ids_data, &ApTracking::new()),
254            Err(HintError::UnknownIdentifier(bx)) if bx.as_ref() == "value"
255        );
256    }
257
258    #[test]
259    fn get_ptr_from_var_name_valid() {
260        let mut vm = vm!();
261        vm.segments = segments![((1, 0), (0, 0))];
262        let hint_ref = HintReference::new_simple(0);
263        let ids_data = HashMap::from([("value".to_string(), hint_ref)]);
264
265        assert_matches!(
266            get_ptr_from_var_name("value", &vm, &ids_data, &ApTracking::new()),
267            Ok(x) if x == relocatable!(0, 0)
268        );
269    }
270
271    #[test]
272    fn get_ptr_from_var_name_invalid() {
273        let mut vm = vm!();
274        vm.segments = segments![((1, 0), 0)];
275        let hint_ref = HintReference::new_simple(0);
276        let ids_data = HashMap::from([("value".to_string(), hint_ref)]);
277
278        assert_matches!(
279            get_ptr_from_var_name("value", &vm, &ids_data, &ApTracking::new()),
280            Err(HintError::IdentifierNotRelocatable(bx)) if bx.as_ref() == "value"
281        );
282    }
283
284    #[test]
285    fn get_relocatable_from_var_name_valid() {
286        let mut vm = vm!();
287        vm.segments = segments![((1, 0), (0, 0))];
288        let hint_ref = HintReference::new_simple(0);
289        let ids_data = HashMap::from([("value".to_string(), hint_ref)]);
290
291        assert_matches!(
292            get_relocatable_from_var_name("value", &vm, &ids_data, &ApTracking::new()),
293            Ok(x) if x == relocatable!(1, 0)
294        );
295    }
296
297    #[test]
298    fn get_relocatable_from_var_name_invalid() {
299        let mut vm = vm!();
300        vm.segments.memory = Memory::new();
301        let hint_ref = HintReference::new_simple(-8);
302        let ids_data = HashMap::from([("value".to_string(), hint_ref)]);
303
304        assert_matches!(
305            get_relocatable_from_var_name("value", &vm, &ids_data, &ApTracking::new()),
306            Err(HintError::UnknownIdentifier(bx)) if bx.as_ref() == "value"
307        );
308    }
309
310    #[test]
311    fn get_integer_from_var_name_valid() {
312        let mut vm = vm!();
313        vm.segments = segments![((1, 0), 1)];
314        let hint_ref = HintReference::new_simple(0);
315        let ids_data = HashMap::from([("value".to_string(), hint_ref)]);
316
317        assert_matches!(
318            get_integer_from_var_name("value", &vm, &ids_data, &ApTracking::new()),
319            Ok(x) if x == Felt252::from(1)
320        );
321    }
322
323    #[test]
324    fn get_integer_from_var_name_invalid() {
325        let mut vm = vm!();
326        vm.segments = segments![((1, 0), (0, 0))];
327        let hint_ref = HintReference::new_simple(0);
328        let ids_data = HashMap::from([("value".to_string(), hint_ref)]);
329
330        assert_matches!(
331            get_integer_from_var_name("value", &vm, &ids_data, &ApTracking::new()),
332            Err(HintError::IdentifierNotInteger(bx)) if bx.as_ref() == "value"
333        );
334    }
335}