cairo_vm/hint_processor/builtin_hint_processor/secp/
signature.rs

1use crate::Felt252;
2use crate::{
3    any_box,
4    hint_processor::{
5        builtin_hint_processor::{hint_utils::get_integer_from_var_name, secp::secp_utils::BETA},
6        hint_processor_definition::HintReference,
7    },
8    math_utils::{div_mod, safe_div_bigint},
9    serde::deserialize_program::ApTracking,
10    stdlib::{collections::HashMap, ops::Shr, prelude::*},
11    types::exec_scope::ExecutionScopes,
12    vm::{errors::hint_errors::HintError, vm_core::VirtualMachine},
13};
14use core::ops::Add;
15use num_bigint::BigInt;
16use num_integer::Integer;
17
18use super::{
19    bigint_utils::Uint384,
20    secp_utils::{N, SECP_P},
21};
22
23/* Implements hint:
24from starkware.cairo.common.cairo_secp.secp_utils import N, pack
25from starkware.python.math_utils import div_mod, safe_div
26
27a = pack(ids.a, PRIME)
28b = pack(ids.b, PRIME)
29value = res = div_mod(a, b, N)
30*/
31pub fn div_mod_n_packed(
32    vm: &mut VirtualMachine,
33    exec_scopes: &mut ExecutionScopes,
34    ids_data: &HashMap<String, HintReference>,
35    ap_tracking: &ApTracking,
36    n: &BigInt,
37) -> Result<(), HintError> {
38    let a = Uint384::from_var_name("a", vm, ids_data, ap_tracking)?.pack86();
39    let b = Uint384::from_var_name("b", vm, ids_data, ap_tracking)?.pack86();
40
41    let value = div_mod(&a, &b, n)?;
42    exec_scopes.insert_value("a", a);
43    exec_scopes.insert_value("b", b);
44    exec_scopes.insert_value("value", value.clone());
45    exec_scopes.insert_value("res", value);
46    Ok(())
47}
48
49pub fn div_mod_n_packed_divmod(
50    vm: &mut VirtualMachine,
51    exec_scopes: &mut ExecutionScopes,
52    ids_data: &HashMap<String, HintReference>,
53    ap_tracking: &ApTracking,
54) -> Result<(), HintError> {
55    exec_scopes.assign_or_update_variable("N", any_box!(N.clone()));
56    div_mod_n_packed(vm, exec_scopes, ids_data, ap_tracking, &N)
57}
58
59pub fn div_mod_n_packed_external_n(
60    vm: &mut VirtualMachine,
61    exec_scopes: &mut ExecutionScopes,
62    ids_data: &HashMap<String, HintReference>,
63    ap_tracking: &ApTracking,
64) -> Result<(), HintError> {
65    let n = exec_scopes.get::<BigInt>("N")?;
66    div_mod_n_packed(vm, exec_scopes, ids_data, ap_tracking, &n)
67}
68
69// Implements hint:
70// value = k = safe_div(res * b - a, N)
71pub fn div_mod_n_safe_div(
72    exec_scopes: &mut ExecutionScopes,
73    a_alias: &str,
74    b_alias: &str,
75    to_add: u64,
76) -> Result<(), HintError> {
77    let a = exec_scopes.get_ref::<BigInt>(a_alias)?;
78    let b = exec_scopes.get_ref::<BigInt>(b_alias)?;
79    let res = exec_scopes.get_ref::<BigInt>("res")?;
80
81    let n = exec_scopes.get("N")?;
82
83    let value = safe_div_bigint(&(res * b - a), &n)?.add(to_add);
84
85    exec_scopes.insert_value("value", value);
86    Ok(())
87}
88
89/* Implements hint:
90%{
91    from starkware.cairo.common.cairo_secp.secp_utils import SECP_P, pack
92
93    x_cube_int = pack(ids.x_cube, PRIME) % SECP_P
94    y_square_int = (x_cube_int + ids.BETA) % SECP_P
95    y = pow(y_square_int, (SECP_P + 1) // 4, SECP_P)
96
97    # We need to decide whether to take y or SECP_P - y.
98    if ids.v % 2 == y % 2:
99        value = y
100    else:
101        value = (-y) % SECP_P
102%}
103*/
104pub fn get_point_from_x(
105    vm: &mut VirtualMachine,
106    exec_scopes: &mut ExecutionScopes,
107    ids_data: &HashMap<String, HintReference>,
108    ap_tracking: &ApTracking,
109    constants: &HashMap<String, Felt252>,
110) -> Result<(), HintError> {
111    exec_scopes.insert_value("SECP_P", SECP_P.clone());
112    let beta = constants
113        .get(BETA)
114        .ok_or_else(|| HintError::MissingConstant(Box::new(BETA)))?
115        .to_bigint();
116
117    let x_cube_int = Uint384::from_var_name("x_cube", vm, ids_data, ap_tracking)?
118        .pack86()
119        .mod_floor(&SECP_P);
120    let y_cube_int = (x_cube_int + beta).mod_floor(&SECP_P);
121    exec_scopes.insert_value("y_square_int", y_cube_int.clone());
122    // Divide by 4
123    let mut y = y_cube_int.modpow(&(&*SECP_P + 1_u32).shr(2_u32), &SECP_P);
124    exec_scopes.insert_value::<BigInt>("y", y.clone());
125
126    let v = get_integer_from_var_name("v", vm, ids_data, ap_tracking)?.to_bigint();
127    if v.is_even() != y.is_even() {
128        y = &*SECP_P - y;
129    }
130    exec_scopes.insert_value("value", y);
131    Ok(())
132}
133/* Implements hint:
134    from starkware.cairo.common.cairo_secp.secp_utils import pack
135    from starkware.python.math_utils import div_mod, safe_div
136
137    N = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141
138    x = pack(ids.x, PRIME) % N
139    s = pack(ids.s, PRIME) % N
140    value = res = div_mod(x, s, N)
141*/
142pub fn pack_modn_div_modn(
143    vm: &mut VirtualMachine,
144    exec_scopes: &mut ExecutionScopes,
145    ids_data: &HashMap<String, HintReference>,
146    ap_tracking: &ApTracking,
147) -> Result<(), HintError> {
148    let x = Uint384::from_var_name("x", vm, ids_data, ap_tracking)?
149        .pack86()
150        .mod_floor(&N);
151    let s = Uint384::from_var_name("s", vm, ids_data, ap_tracking)?
152        .pack86()
153        .mod_floor(&N);
154
155    let value = div_mod(&x, &s, &N)?;
156    exec_scopes.insert_value("x", x);
157    exec_scopes.insert_value("s", s);
158    exec_scopes.insert_value("N", N.clone());
159    exec_scopes.insert_value("value", value.clone());
160    exec_scopes.insert_value("res", value);
161    Ok(())
162}
163
164#[cfg(test)]
165mod tests {
166    use super::*;
167    use crate::stdlib::string::ToString;
168    use crate::types::errors::math_errors::MathError;
169
170    use crate::{
171        any_box,
172        hint_processor::{
173            builtin_hint_processor::{
174                builtin_hint_processor_definition::{BuiltinHintProcessor, HintProcessorData},
175                hint_code,
176            },
177            hint_processor_definition::HintProcessorLogic,
178        },
179        types::exec_scope::ExecutionScopes,
180        utils::test_utils::*,
181    };
182    use assert_matches::assert_matches;
183    use num_traits::{One, Zero};
184
185    #[cfg(target_arch = "wasm32")]
186    use wasm_bindgen_test::*;
187
188    #[test]
189    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
190    fn safe_div_ok() {
191        // "import N"
192        let mut exec_scopes = ExecutionScopes::new();
193        exec_scopes.assign_or_update_variable("N", any_box!(N.clone()));
194
195        let hint_codes = vec![
196            hint_code::DIV_MOD_N_PACKED_DIVMOD_V1,
197            hint_code::DIV_MOD_N_PACKED_DIVMOD_EXTERNAL_N,
198        ];
199        for hint_code in hint_codes {
200            let mut vm = vm!();
201
202            vm.segments = segments![
203                ((1, 0), 15),
204                ((1, 1), 3),
205                ((1, 2), 40),
206                ((1, 3), 0),
207                ((1, 4), 10),
208                ((1, 5), 1)
209            ];
210            vm.run_context.fp = 3;
211            let ids_data = non_continuous_ids_data![("a", -3), ("b", 0)];
212
213            assert_matches!(run_hint!(vm, ids_data, hint_code, &mut exec_scopes), Ok(()));
214
215            assert_matches!(div_mod_n_safe_div(&mut exec_scopes, "a", "b", 0), Ok(()));
216            assert_matches!(div_mod_n_safe_div(&mut exec_scopes, "a", "b", 1), Ok(()));
217        }
218    }
219
220    #[test]
221    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
222    fn safe_div_fail() {
223        let mut exec_scopes = scope![
224            ("a", BigInt::zero()),
225            ("b", BigInt::one()),
226            ("res", BigInt::one()),
227            ("N", N.clone())
228        ];
229        assert_matches!(
230            div_mod_n_safe_div(
231                &mut exec_scopes,
232                "a",
233                "b",
234                0,
235            ),
236            Err(
237                HintError::Math(MathError::SafeDivFailBigInt(bx)
238            )) if *bx == (BigInt::one(), bigint_str!("115792089237316195423570985008687907852837564279074904382605163141518161494337"))
239        );
240    }
241
242    #[test]
243    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
244    fn get_point_from_x_ok() {
245        let hint_code = hint_code::GET_POINT_FROM_X;
246        let mut vm = vm!();
247        vm.segments = segments![
248            ((1, 0), 18),
249            ((1, 1), 2147483647),
250            ((1, 2), 2147483647),
251            ((1, 3), 2147483647)
252        ];
253        vm.run_context.fp = 1;
254        let ids_data = non_continuous_ids_data![("v", -1), ("x_cube", 0)];
255        assert_matches!(
256            run_hint!(
257                vm,
258                ids_data,
259                hint_code,
260                exec_scopes_ref!(),
261                &[(BETA, Felt252::from(7)),]
262                    .into_iter()
263                    .map(|(k, v)| (k.to_string(), v))
264                    .collect()
265            ),
266            Ok(())
267        )
268    }
269
270    #[test]
271    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
272    fn get_point_from_x_negative_y() {
273        let hint_code = hint_code::GET_POINT_FROM_X;
274        let mut vm = vm!();
275        let mut exec_scopes = ExecutionScopes::new();
276        vm.segments = segments![
277            ((1, 0), 1),
278            ((1, 1), 2147483647),
279            ((1, 2), 2147483647),
280            ((1, 3), 2147483647)
281        ];
282        vm.run_context.fp = 2;
283
284        let ids_data = ids_data!["v", "x_cube"];
285        assert_matches!(
286            run_hint!(
287                vm,
288                ids_data,
289                hint_code,
290                &mut exec_scopes,
291                &[(BETA, Felt252::from(7)),]
292                    .into_iter()
293                    .map(|(k, v)| (k.to_string(), v))
294                    .collect()
295            ),
296            Ok(())
297        );
298
299        check_scope!(
300            &exec_scopes,
301            [(
302                "value",
303                bigint_str!(
304                    "94274691440067846579164151740284923997007081248613730142069408045642476712539"
305                )
306            )]
307        );
308    }
309
310    #[test]
311    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
312    fn pack_modn_div_modn_ok() {
313        let hint_code = hint_code::PACK_MODN_DIV_MODN;
314        let mut exec_scopes = scope![("N", N.clone())];
315        let mut vm = vm!();
316
317        vm.segments = segments![
318            ((1, 0), 15),
319            ((1, 1), 3),
320            ((1, 2), 40),
321            ((1, 3), 0),
322            ((1, 4), 10),
323            ((1, 5), 1)
324        ];
325        vm.run_context.fp = 3;
326        let ids_data = non_continuous_ids_data![("x", -3), ("s", 0)];
327        assert_matches!(run_hint!(vm, ids_data, hint_code, &mut exec_scopes), Ok(()));
328        assert_matches!(div_mod_n_safe_div(&mut exec_scopes, "x", "s", 0), Ok(()));
329    }
330}