cairo_vm/hint_processor/
hint_processor_utils.rs

1use crate::stdlib::boxed::Box;
2
3use crate::{
4    serde::deserialize_program::{ApTracking, OffsetValue},
5    types::{
6        errors::math_errors::MathError,
7        instruction::Register,
8        relocatable::{MaybeRelocatable, Relocatable},
9    },
10    vm::{errors::hint_errors::HintError, vm_core::VirtualMachine},
11};
12
13use super::hint_processor_definition::HintReference;
14use crate::Felt252;
15
16use num_traits::ToPrimitive;
17
18/// Inserts value into the address of the given ids variable
19pub fn insert_value_from_reference(
20    value: impl Into<MaybeRelocatable>,
21    vm: &mut VirtualMachine,
22    hint_reference: &HintReference,
23    ap_tracking: &ApTracking,
24) -> Result<(), HintError> {
25    let addr = compute_addr_from_reference(hint_reference, vm, ap_tracking)
26        .ok_or(HintError::UnknownIdentifierInternal)?;
27    vm.insert_value(addr, value).map_err(HintError::Memory)
28}
29
30///Returns the Integer value stored in the given ids variable
31/// Returns an internal error, users should map it into a more informative type
32pub fn get_integer_from_reference(
33    vm: &VirtualMachine,
34    hint_reference: &HintReference,
35    ap_tracking: &ApTracking,
36) -> Result<Felt252, HintError> {
37    get_maybe_relocatable_from_reference(vm, hint_reference, ap_tracking)
38        .ok_or(HintError::UnknownIdentifierInternal)?
39        .get_int()
40        .ok_or(HintError::WrongIdentifierTypeInternal)
41}
42
43///Returns the Relocatable value stored in the given ids variable
44pub fn get_ptr_from_reference(
45    vm: &VirtualMachine,
46    hint_reference: &HintReference,
47    ap_tracking: &ApTracking,
48) -> Result<Relocatable, HintError> {
49    get_maybe_relocatable_from_reference(vm, hint_reference, ap_tracking)
50        .ok_or(HintError::UnknownIdentifierInternal)?
51        .get_relocatable()
52        .ok_or(HintError::WrongIdentifierTypeInternal)
53}
54
55///Returns the value given by a reference as [MaybeRelocatable]
56pub fn get_maybe_relocatable_from_reference(
57    vm: &VirtualMachine,
58    hint_reference: &HintReference,
59    ap_tracking: &ApTracking,
60) -> Option<MaybeRelocatable> {
61    let offset1 = get_offset_value(
62        vm,
63        &hint_reference.offset1,
64        &hint_reference.ap_tracking_data,
65        ap_tracking,
66    )?;
67    let offset2 = get_offset_value(
68        vm,
69        &hint_reference.offset2,
70        &hint_reference.ap_tracking_data,
71        ap_tracking,
72    )?;
73    let mut val = match hint_reference.offset2 {
74        OffsetValue::Reference(_, _, _, true)
75        | OffsetValue::Immediate(_)
76        | OffsetValue::Value(_) => offset1.add(&offset2).ok()?,
77        OffsetValue::Reference(_, _, _, false) => offset1.sub(&offset2).ok()?,
78    };
79    if hint_reference.inner_dereference && hint_reference.outer_dereference {
80        val = vm.get_maybe(&val)?;
81    }
82    if hint_reference.inner_dereference || hint_reference.outer_dereference {
83        val = vm.get_maybe(&val)?;
84    }
85    Some(val)
86}
87
88/// Computes the memory address of the ids variable indicated by the HintReference as a [Relocatable]
89pub fn compute_addr_from_reference(
90    hint_reference: &HintReference,
91    vm: &VirtualMachine,
92    ap_tracking: &ApTracking,
93) -> Option<Relocatable> {
94    let offset1 = get_offset_value(
95        vm,
96        &hint_reference.offset1,
97        &hint_reference.ap_tracking_data,
98        ap_tracking,
99    )?;
100    let offset2 = get_offset_value(
101        vm,
102        &hint_reference.offset2,
103        &hint_reference.ap_tracking_data,
104        ap_tracking,
105    )?;
106    let mut val = offset1.add(&offset2).ok()?;
107    if hint_reference.inner_dereference {
108        val = vm.get_maybe(&val)?;
109    };
110    val.get_relocatable()
111}
112
113fn apply_ap_tracking_correction(
114    ap: Relocatable,
115    ref_ap_tracking: &ApTracking,
116    hint_ap_tracking: &ApTracking,
117) -> Option<Relocatable> {
118    // check that both groups are the same
119    if ref_ap_tracking.group != hint_ap_tracking.group {
120        return None;
121    }
122    let ap_diff = hint_ap_tracking.offset - ref_ap_tracking.offset;
123    (ap - ap_diff).ok()
124}
125
126//Tries to convert a Felt252 value to usize
127pub fn felt_to_usize(felt: &Felt252) -> Result<usize, MathError> {
128    felt.to_usize()
129        .ok_or_else(|| MathError::Felt252ToUsizeConversion(Box::new(*felt)))
130}
131
132///Tries to convert a Felt252 value to u32
133pub fn felt_to_u32(felt: &Felt252) -> Result<u32, MathError> {
134    felt.to_u32()
135        .ok_or_else(|| MathError::Felt252ToU32Conversion(Box::new(*felt)))
136}
137
138fn get_offset_value(
139    vm: &VirtualMachine,
140    offset_value: &OffsetValue,
141    reference_ap_tracking: &Option<ApTracking>,
142    hint_ap_tracking: &ApTracking,
143) -> Option<MaybeRelocatable> {
144    match offset_value {
145        OffsetValue::Immediate(f) => Some(f.into()),
146        OffsetValue::Value(v) => Some(Felt252::from(*v).into()),
147        OffsetValue::Reference(register, offset, deref, _) => {
148            let addr = (if matches!(register, Register::FP) {
149                vm.get_fp()
150            } else {
151                apply_ap_tracking_correction(
152                    vm.get_ap(),
153                    reference_ap_tracking.as_ref()?,
154                    hint_ap_tracking,
155                )?
156            } + *offset)
157                .ok()?;
158
159            if *deref {
160                vm.get_maybe(&addr)
161            } else {
162                Some(addr.into())
163            }
164        }
165    }
166}
167
168#[cfg(test)]
169mod tests {
170    use super::*;
171
172    use crate::{relocatable, utils::test_utils::*, vm::vm_memory::memory::Memory};
173    use assert_matches::assert_matches;
174
175    #[cfg(target_arch = "wasm32")]
176    use wasm_bindgen_test::*;
177
178    #[test]
179    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
180    fn get_integer_from_reference_with_immediate_value() {
181        // Reference: cast(2, felt)
182        let mut vm = vm!();
183        vm.segments = segments![((1, 0), 0)];
184        let mut hint_ref = HintReference::new(0, 0, false, false, true);
185        hint_ref.offset1 = OffsetValue::Immediate(Felt252::from(2));
186
187        assert_eq!(
188            get_integer_from_reference(&vm, &hint_ref, &ApTracking::new())
189                .expect("Unexpected get integer fail"),
190            Felt252::from(2)
191        );
192    }
193
194    #[test]
195    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
196    fn get_offset_value_reference_valid() {
197        let mut vm = vm!();
198        vm.segments = segments![((1, 0), 0)];
199        let mut hint_ref = HintReference::new(0, 0, false, true, true);
200        hint_ref.offset1 = OffsetValue::Reference(Register::FP, 2_i32, false, true);
201
202        assert_matches!(
203            get_offset_value(&vm, &hint_ref.offset1, &hint_ref.ap_tracking_data, &ApTracking::new()),
204            Some(x) if x == mayberelocatable!(1, 2)
205        );
206    }
207
208    #[test]
209    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
210    fn get_offset_value_invalid() {
211        let mut vm = vm!();
212        vm.segments = segments![((1, 0), 0)];
213        let mut hint_ref = HintReference::new(0, 0, false, true, true);
214        hint_ref.offset1 = OffsetValue::Reference(Register::FP, -2_i32, false, true);
215
216        assert_matches!(
217            get_offset_value(
218                &vm,
219                &hint_ref.offset1,
220                &hint_ref.ap_tracking_data,
221                &ApTracking::new()
222            ),
223            None
224        );
225    }
226
227    #[test]
228    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
229    fn get_ptr_from_reference_short_path() {
230        let mut vm = vm!();
231        vm.segments = segments![((1, 0), (2, 0))];
232
233        assert_matches!(
234            get_ptr_from_reference(
235                &vm,
236                &HintReference::new(0, 0, false, false, true),
237                &ApTracking::new()
238            ),
239            Ok(x) if x == relocatable!(1, 0)
240        );
241    }
242
243    #[test]
244    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
245    fn get_ptr_from_reference_with_dereference() {
246        let mut vm = vm!();
247        vm.segments = segments![((1, 0), (3, 0))];
248
249        assert_matches!(
250            get_ptr_from_reference(
251                &vm,
252                &HintReference::new(0, 0, false, true, true),
253                &ApTracking::new()
254            ),
255            Ok(x) if x == relocatable!(3, 0)
256        );
257    }
258
259    #[test]
260    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
261    fn get_ptr_from_reference_with_dereference_and_imm() {
262        let mut vm = vm!();
263        vm.segments = segments![((1, 0), (4, 0))];
264        let mut hint_ref = HintReference::new(0, 0, true, false, true);
265        hint_ref.offset2 = OffsetValue::Value(2);
266
267        assert_matches!(
268            get_ptr_from_reference(&vm, &hint_ref, &ApTracking::new()),
269            Ok(x) if x == relocatable!(4, 2)
270        );
271    }
272
273    #[test]
274    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
275    fn compute_addr_from_reference_no_regiter_in_reference() {
276        let mut vm = vm!();
277        vm.segments = segments![((1, 0), (4, 0))];
278        let mut hint_reference = HintReference::new(0, 0, false, false, true);
279        hint_reference.offset1 = OffsetValue::Immediate(Felt252::from(2_i32));
280
281        assert!(compute_addr_from_reference(&hint_reference, &vm, &ApTracking::new()).is_none());
282    }
283
284    #[test]
285    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
286    fn compute_addr_from_reference_failed_to_get_ids() {
287        let mut vm = vm!();
288        vm.segments = segments![((1, 0), 4)];
289        // vm.run_context.fp = -1;
290        let mut hint_reference = HintReference::new(0, 0, false, false, true);
291        hint_reference.offset1 = OffsetValue::Reference(Register::FP, -1, true, true);
292
293        assert_matches!(
294            compute_addr_from_reference(&hint_reference, &vm, &ApTracking::new()),
295            None
296        );
297    }
298
299    #[test]
300    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
301    fn tracking_correction_valid() {
302        let mut ref_ap_tracking = ApTracking::new();
303        ref_ap_tracking.group = 1;
304        let mut hint_ap_tracking = ApTracking::new();
305        hint_ap_tracking.group = 1;
306
307        assert_matches!(
308            apply_ap_tracking_correction(relocatable!(1, 0), &ref_ap_tracking, &hint_ap_tracking),
309            Some(relocatable!(1, 0))
310        );
311    }
312
313    #[test]
314    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
315    fn tracking_correction_invalid_group() {
316        let mut ref_ap_tracking = ApTracking::new();
317        ref_ap_tracking.group = 1;
318        let mut hint_ap_tracking = ApTracking::new();
319        hint_ap_tracking.group = 2;
320
321        assert!(apply_ap_tracking_correction(
322            relocatable!(1, 0),
323            &ref_ap_tracking,
324            &hint_ap_tracking
325        )
326        .is_none());
327    }
328
329    #[test]
330    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
331    fn get_maybe_relocatable_from_reference_valid() {
332        let mut vm = vm!();
333        vm.segments = segments![((1, 0), (0, 0))];
334        let hint_ref = HintReference::new_simple(0);
335        assert_matches!(
336            get_maybe_relocatable_from_reference(&vm, &hint_ref, &ApTracking::new()),
337            Some(x) if x == mayberelocatable!(0, 0)
338        );
339    }
340
341    #[test]
342    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
343    fn get_maybe_relocatable_from_reference_invalid() {
344        let mut vm = vm!();
345        vm.segments.memory = Memory::new();
346        let hint_ref = HintReference::new_simple(0);
347        assert_matches!(
348            get_maybe_relocatable_from_reference(&vm, &hint_ref, &ApTracking::new()),
349            None
350        );
351    }
352
353    #[test]
354    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
355    fn get_integer_from_reference_with_triple_deref() {
356        // Reference: [cast([[fp + 2)] + 2], felt*)]
357        let mut vm = vm!();
358        vm.segments = segments![
359            ((1, 2), (0, 0)), // [fp + 2] -> [(1, 0) + 2] -> [(1, 2)] -> (0, 0)
360            ((0, 2), (0, 5)), // [[fp + 2] + 2] -> [(0, 0) + 2] -> [(0, 2)] -> (0, 5)
361            ((0, 5), 3)       // [[[fp + 2] + 2]] -> [(0, 5)] -> 3
362        ];
363        let hint_ref = HintReference {
364            offset1: OffsetValue::Reference(Register::FP, 2, true, true),
365            offset2: OffsetValue::Value(2),
366            outer_dereference: true,
367            inner_dereference: true,
368            ap_tracking_data: Default::default(),
369            cairo_type: None,
370        };
371
372        assert_eq!(
373            get_integer_from_reference(&vm, &hint_ref, &ApTracking::new())
374                .expect("Unexpected get integer fail"),
375            Felt252::THREE
376        );
377    }
378
379    #[test]
380    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
381    fn get_integer_from_reference_without_outer_defer() {
382        // Reference: cast([fp + 4] + (-5), felt)
383        let mut vm = vm!();
384        vm.segments = segments![
385            ((1, 4), 8), // [fp + 4]
386        ];
387        // [fp + 4] + (-5) = 8 - 5 = 3
388        let hint_ref = HintReference {
389            offset1: OffsetValue::Reference(Register::FP, 4, true, true),
390            offset2: OffsetValue::Immediate(Felt252::from(-5)),
391            outer_dereference: false,
392            inner_dereference: false,
393            ap_tracking_data: Default::default(),
394            cairo_type: None,
395        };
396
397        assert_eq!(
398            get_integer_from_reference(&vm, &hint_ref, &ApTracking::new())
399                .expect("Unexpected get integer fail"),
400            Felt252::THREE
401        );
402    }
403}