cairo_vm/hint_processor/builtin_hint_processor/
hint_utils.rs

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