cairo_vm/hint_processor/builtin_hint_processor/
memset_utils.rs

1use crate::stdlib::{any::Any, collections::HashMap, prelude::*};
2
3use crate::Felt252;
4use crate::{
5    hint_processor::{
6        builtin_hint_processor::hint_utils::{
7            get_integer_from_var_name, insert_value_from_var_name,
8        },
9        hint_processor_definition::HintReference,
10    },
11    serde::deserialize_program::ApTracking,
12    types::exec_scope::ExecutionScopes,
13    vm::{errors::hint_errors::HintError, vm_core::VirtualMachine},
14};
15
16//  Implements hint:
17//  %{ vm_enter_scope({'n': ids.n}) %}
18pub fn memset_enter_scope(
19    vm: &mut VirtualMachine,
20    exec_scopes: &mut ExecutionScopes,
21    ids_data: &HashMap<String, HintReference>,
22    ap_tracking: &ApTracking,
23) -> Result<(), HintError> {
24    let n: Box<dyn Any> = Box::new(get_integer_from_var_name("n", vm, ids_data, ap_tracking)?);
25    exec_scopes.enter_scope(HashMap::from([(String::from("n"), n)]));
26    Ok(())
27}
28
29/* Implements hint:
30%{
31    n -= 1
32    ids.`i_name` = 1 if n > 0 else 0
33%}
34*/
35pub fn memset_step_loop(
36    vm: &mut VirtualMachine,
37    exec_scopes: &mut ExecutionScopes,
38    ids_data: &HashMap<String, HintReference>,
39    ap_tracking: &ApTracking,
40    i_name: &'static str,
41) -> Result<(), HintError> {
42    // get `n` variable from vm scope
43    let n = exec_scopes.get_mut_ref::<Felt252>("n")?;
44    // this variable will hold the value of `n - 1`
45    *n -= Felt252::ONE;
46    // if `new_n` is positive, insert 1 in the address of `continue_loop`
47    // else, insert 0
48    let flag = Felt252::from((*n > Felt252::ZERO) as u8);
49    insert_value_from_var_name(i_name, flag, vm, ids_data, ap_tracking)?;
50    // Reassign `n` with `n - 1`
51    // we do it at the end of the function so that the borrow checker doesn't complain
52    Ok(())
53}
54
55#[cfg(test)]
56mod tests {
57    use super::*;
58    use crate::types::relocatable::Relocatable;
59    use crate::{
60        any_box,
61        hint_processor::{
62            builtin_hint_processor::builtin_hint_processor_definition::{
63                BuiltinHintProcessor, HintProcessorData,
64            },
65            hint_processor_definition::HintProcessorLogic,
66        },
67        types::{exec_scope::ExecutionScopes, relocatable::MaybeRelocatable},
68        utils::test_utils::*,
69        vm::errors::memory_errors::MemoryError,
70    };
71    use assert_matches::assert_matches;
72
73    #[cfg(target_arch = "wasm32")]
74    use wasm_bindgen_test::*;
75
76    #[test]
77    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
78    fn memset_enter_scope_valid() {
79        let hint_code = "vm_enter_scope({'n': ids.n})";
80        let mut vm = vm!();
81        // initialize fp
82        vm.run_context.fp = 2;
83        // insert ids into memory
84        vm.segments = segments![((1, 1), 5)];
85        let ids_data = ids_data!["n"];
86        assert!(run_hint!(vm, ids_data, hint_code).is_ok());
87    }
88
89    #[test]
90    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
91    fn memset_enter_scope_invalid() {
92        let hint_code = "vm_enter_scope({'n': ids.n})";
93        let mut vm = vm!();
94        // initialize fp
95        vm.run_context.fp = 2;
96        // insert ids.n into memory
97        // insert a relocatable value in the address of ids.len so that it raises an error.
98        vm.segments = segments![((1, 1), (1, 0))];
99        let ids_data = ids_data!["n"];
100        assert_matches!(
101            run_hint!(vm, ids_data, hint_code),
102            Err(HintError::IdentifierNotInteger(bx)) if bx.as_ref() == "n"
103        );
104    }
105
106    #[test]
107    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
108    fn memset_continue_loop_valid_continue_loop_equal_1() {
109        let hint_code = "n -= 1\nids.continue_loop = 1 if n > 0 else 0";
110        let mut vm = vm!();
111        // initialize fp
112        vm.run_context.fp = 1;
113        // initialize vm scope with variable `n` = 1
114        let mut exec_scopes = scope![("n", Felt252::ONE)];
115        // initialize ids.continue_loop
116        // we create a memory gap so that there is None in (1, 0), the actual addr of continue_loop
117        vm.segments = segments![((1, 1), 5)];
118        let ids_data = ids_data!["continue_loop"];
119        assert!(run_hint!(vm, ids_data, hint_code, &mut exec_scopes).is_ok());
120        // assert ids.continue_loop = 0
121        check_memory![vm.segments.memory, ((1, 0), 0)];
122    }
123
124    #[test]
125    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
126    fn memset_continue_loop_valid_continue_loop_equal_5() {
127        let hint_code = "n -= 1\nids.continue_loop = 1 if n > 0 else 0";
128        let mut vm = vm!();
129        // initialize fp
130        vm.run_context.fp = 1;
131        // initialize vm scope with variable `n` = 5
132        let mut exec_scopes = scope![("n", Felt252::from(5))];
133        // initialize ids.continue_loop
134        // we create a memory gap so that there is None in (0, 0), the actual addr of continue_loop
135        vm.segments = segments![((1, 2), 5)];
136        let ids_data = ids_data!["continue_loop"];
137        assert!(run_hint!(vm, ids_data, hint_code, &mut exec_scopes).is_ok());
138
139        // assert ids.continue_loop = 1
140        check_memory![vm.segments.memory, ((1, 0), 1)];
141    }
142
143    #[test]
144    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
145    fn memset_continue_loop_variable_not_in_scope_error() {
146        let hint_code = "n -= 1\nids.continue_loop = 1 if n > 0 else 0";
147        let mut vm = vm!();
148        // initialize fp
149        vm.run_context.fp = 3;
150
151        // we don't initialize `n` now:
152        /*  vm.exec_scopes
153        .assign_or_update_variable("n",  Felt252::ONE));  */
154
155        // initialize ids.continue_loop
156        // we create a memory gap so that there is None in (0, 1), the actual addr of continue_loop
157        vm.segments = segments![((1, 2), 5)];
158        let ids_data = ids_data!["continue_loop"];
159        assert_matches!(
160            run_hint!(vm, ids_data, hint_code),
161            Err(HintError::VariableNotInScopeError(bx)) if bx.as_ref() == "n"
162        );
163    }
164
165    #[test]
166    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
167    fn memset_continue_loop_insert_error() {
168        let hint_code = "n -= 1\nids.continue_loop = 1 if n > 0 else 0";
169        let mut vm = vm!();
170        // initialize fp
171        vm.run_context.fp = 1;
172        // initialize with variable `n`
173        let mut exec_scopes = scope![("n", Felt252::ONE)];
174        // initialize ids.continue_loop
175        // a value is written in the address so the hint cant insert value there
176        vm.segments = segments![((1, 0), 5)];
177        let ids_data = ids_data!["continue_loop"];
178        assert_matches!(
179            run_hint!(vm, ids_data, hint_code, &mut exec_scopes),
180            Err(HintError::Memory(
181                MemoryError::InconsistentMemory(bx)
182            )) if *bx == (Relocatable::from((1, 0)),
183                    MaybeRelocatable::from(Felt252::from(5)),
184                    MaybeRelocatable::from(Felt252::ZERO))
185        );
186    }
187}