Skip to main content

cairo_vm/hint_processor/
hint_processor_utils.rs

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