diff --git a/CHANGELOG.md b/CHANGELOG.md index 3e0f90c404..dae41525e4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -354,6 +354,19 @@ Add missing hint on vrf.json lib [#1053](https://github.com/lambdaclass/cairo-rs * Remove duplicated tests in cairo_run_test * BREAKING CHANGE: `MemorySegmentManager.get_memory_holes` now also receives the amount of builtins in the vm. Signature is now `pub fn get_memory_holes(&self, builtin_count: usize) -> Result` +* Add missing hints on cairo_secp lib [#1026](https://github.com/lambdaclass/cairo-rs/pull/1026): + + `BuiltinHintProcessor` now supports the following hints: + + ```python + from starkware.cairo.common.cairo_secp.secp256r1_utils import SECP256R1_ALPHA as ALPHA + ``` + and: + + ```python + from starkware.cairo.common.cairo_secp.secp256r1_utils import SECP256R1_N as N + ``` + * Add missing hint on vrf.json lib [#1043](https://github.com/lambdaclass/cairo-rs/pull/1043): `BuiltinHintProcessor` now supports the following hint: diff --git a/cairo_programs/secp256r1_div_mod_n.cairo b/cairo_programs/secp256r1_div_mod_n.cairo new file mode 100644 index 0000000000..ab77e03570 --- /dev/null +++ b/cairo_programs/secp256r1_div_mod_n.cairo @@ -0,0 +1,75 @@ +%builtins range_check + +// Sources: https://github.com/myBraavos/efficient-secp256r1/blob/main/src/secp256r1/signature.cairo#L48 +// Sources: https://github.com/myBraavos/efficient-secp256r1/blob/main/src/secp256r1/ec.cairo#L32 + +from starkware.cairo.common.cairo_secp.bigint import BigInt3, nondet_bigint3, BASE, bigint_mul + +// N = 0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551 +const N0 = 0x179e84f3b9cac2fc632551; +const N1 = 0x3ffffffffffef39beab69c; +const N2 = 0xffffffff00000000fffff; + +func div_mod_n{range_check_ptr}(a: BigInt3, b: BigInt3) -> (res: BigInt3) { + %{ from starkware.cairo.common.cairo_secp.secp256r1_utils import SECP256R1_N as N %} + // Hint 24 + %{ + from starkware.cairo.common.cairo_secp.secp_utils import pack + from starkware.python.math_utils import div_mod, safe_div + + a = pack(ids.a, PRIME) + b = pack(ids.b, PRIME) + value = res = div_mod(a, b, N) + %} + let (res) = nondet_bigint3(); + // Hint 25 + %{ + value = k_plus_one = safe_div(res * b - a, N) + 1 + %} + let (k_plus_one) = nondet_bigint3(); + let k = BigInt3(d0=k_plus_one.d0 - 1, d1=k_plus_one.d1, d2=k_plus_one.d2); + + let (res_b) = bigint_mul(res, b); + let n = BigInt3(N0, N1, N2); + let (k_n) = bigint_mul(k, n); + + // We should now have res_b = k_n + a. Since the numbers are in unreduced form, + // we should handle the carry. + + tempvar carry1 = (res_b.d0 - k_n.d0 - a.d0) / BASE; + assert [range_check_ptr + 0] = carry1 + 2 ** 127; + + tempvar carry2 = (res_b.d1 - k_n.d1 - a.d1 + carry1) / BASE; + assert [range_check_ptr + 1] = carry2 + 2 ** 127; + + tempvar carry3 = (res_b.d2 - k_n.d2 - a.d2 + carry2) / BASE; + assert [range_check_ptr + 2] = carry3 + 2 ** 127; + + tempvar carry4 = (res_b.d3 - k_n.d3 + carry3) / BASE; + assert [range_check_ptr + 3] = carry4 + 2 ** 127; + + assert res_b.d4 - k_n.d4 + carry4 = 0; + + let range_check_ptr = range_check_ptr + 4; + + return (res=res); +} + +func test_div_mod_n{range_check_ptr: felt}() { + let a: BigInt3 = BigInt3(100, 99, 98); + let b: BigInt3 = BigInt3(10, 9, 8); + + let (res) = div_mod_n(a, b); + + assert res = BigInt3( + 17710125265123803206911742, 47938808641831879622633720, 16714845192957993827873659 + ); + + return (); +} + +func main{range_check_ptr: felt}() { + test_div_mod_n(); + + return (); +} diff --git a/src/hint_processor/builtin_hint_processor/builtin_hint_processor_definition.rs b/src/hint_processor/builtin_hint_processor/builtin_hint_processor_definition.rs index c3c9a2bcea..40e537bd27 100644 --- a/src/hint_processor/builtin_hint_processor/builtin_hint_processor_definition.rs +++ b/src/hint_processor/builtin_hint_processor/builtin_hint_processor_definition.rs @@ -52,7 +52,8 @@ use crate::{ bigint_utils::{bigint_to_uint256, hi_max_bitlen, nondet_bigint3}, ec_utils::{ compute_doubling_slope, compute_slope, di_bit, fast_ec_add_assign_new_x, - fast_ec_add_assign_new_y, import_secp256r1_p, quad_bit, + fast_ec_add_assign_new_y, import_secp256r1_alpha, import_secp256r1_n, + import_secp256r1_p, quad_bit, }, field_utils::{ is_zero_assign_scope_variables, is_zero_assign_scope_variables_external_const, @@ -673,6 +674,8 @@ impl HintProcessor for BuiltinHintProcessor { hint_code::UINT256_MUL_DIV_MOD => { uint256_mul_div_mod(vm, &hint_data.ids_data, &hint_data.ap_tracking) } + hint_code::IMPORT_SECP256R1_ALPHA => import_secp256r1_alpha(exec_scopes), + hint_code::IMPORT_SECP256R1_N => import_secp256r1_n(exec_scopes), hint_code::UINT512_UNSIGNED_DIV_REM => { uint512_unsigned_div_rem(vm, &hint_data.ids_data, &hint_data.ap_tracking) } diff --git a/src/hint_processor/builtin_hint_processor/hint_code.rs b/src/hint_processor/builtin_hint_processor/hint_code.rs index 612a88b690..229323f78e 100644 --- a/src/hint_processor/builtin_hint_processor/hint_code.rs +++ b/src/hint_processor/builtin_hint_processor/hint_code.rs @@ -1049,6 +1049,12 @@ ids.remainder.d2 = remainder_split[2]"#; pub const UINT384_SIGNED_NN: &str = "memory[ap] = 1 if 0 <= (ids.a.d2 % PRIME) < 2 ** 127 else 0"; +pub const IMPORT_SECP256R1_ALPHA: &str = + "from starkware.cairo.common.cairo_secp.secp256r1_utils import SECP256R1_ALPHA as ALPHA"; + +pub const IMPORT_SECP256R1_N: &str = + "from starkware.cairo.common.cairo_secp.secp256r1_utils import SECP256R1_N as N"; + pub const UINT384_GET_SQUARE_ROOT: &str = "from starkware.python.math_utils import is_quad_residue, sqrt diff --git a/src/hint_processor/builtin_hint_processor/secp/ec_utils.rs b/src/hint_processor/builtin_hint_processor/secp/ec_utils.rs index 51d977e156..f80fe6caf1 100644 --- a/src/hint_processor/builtin_hint_processor/secp/ec_utils.rs +++ b/src/hint_processor/builtin_hint_processor/secp/ec_utils.rs @@ -5,7 +5,10 @@ use crate::{ get_integer_from_var_name, get_relocatable_from_var_name, insert_value_from_var_name, insert_value_into_ap, }, - secp::{bigint_utils::BigInt3, secp_utils::SECP_P}, + secp::{ + bigint_utils::BigInt3, + secp_utils::{SECP256R1_ALPHA, SECP256R1_N, SECP_P}, + }, }, hint_processor_definition::HintReference, }, @@ -18,6 +21,7 @@ use crate::{ use felt::Felt252; use num_bigint::BigInt; use num_integer::Integer; + use num_traits::{One, ToPrimitive, Zero}; use super::secp_utils::SECP256R1_P; @@ -333,6 +337,24 @@ pub fn ec_mul_inner( insert_value_into_ap(vm, scalar) } +/* +Implements hint: +%{ from starkware.cairo.common.cairo_secp.secp256r1_utils import SECP256R1_ALPHA as ALPHA %} +*/ +pub fn import_secp256r1_alpha(exec_scopes: &mut ExecutionScopes) -> Result<(), HintError> { + exec_scopes.insert_value("ALPHA", SECP256R1_ALPHA.clone()); + Ok(()) +} + +/* +Implements hint: +%{ from starkware.cairo.common.cairo_secp.secp256r1_utils import SECP256R1_N as N %} +*/ +pub fn import_secp256r1_n(exec_scopes: &mut ExecutionScopes) -> Result<(), HintError> { + exec_scopes.insert_value("N", SECP256R1_N.clone()); + Ok(()) +} + /* Implements hint: %{ @@ -435,6 +457,7 @@ mod tests { }; use assert_matches::assert_matches; + use num_bigint::BigUint; #[cfg(target_arch = "wasm32")] use wasm_bindgen_test::*; @@ -1142,4 +1165,26 @@ mod tests { // Check hint memory inserts check_memory![vm.segments.memory, ((1, 3), 2)]; } + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + fn run_import_secp256r1_alpha() { + let hint_code = "from starkware.cairo.common.cairo_secp.secp256r1_utils import SECP256R1_ALPHA as ALPHA"; + let mut vm = vm_with_range_check!(); + + //Initialize fp + vm.run_context.fp = 1; + //Create hint_data + let ids_data = ids_data!["point"]; + let mut exec_scopes = ExecutionScopes::new(); + //Execute the hint + assert_matches!(run_hint!(vm, ids_data, hint_code, &mut exec_scopes), Ok(())); + //Check 'ALPHA' is defined in the vm scope + assert_matches!( + exec_scopes.get::("ALPHA"), + Ok(x) if x == biguint_str!( + "115792089210356248762697446949407573530086143415290314195533631308867097853948" + ) + ); + } } diff --git a/src/hint_processor/builtin_hint_processor/secp/secp_utils.rs b/src/hint_processor/builtin_hint_processor/secp/secp_utils.rs index 155b9f4976..967e6da3e8 100644 --- a/src/hint_processor/builtin_hint_processor/secp/secp_utils.rs +++ b/src/hint_processor/builtin_hint_processor/secp/secp_utils.rs @@ -61,7 +61,7 @@ lazy_static! { "115792089210356248762697446949407573530086143415290314195533631308867097853951" ).unwrap(); //SECP256R1_N = 0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551 - pub(crate) static ref SECP256R1_N: BigUint = BigUint::from_str( + pub(crate) static ref SECP256R1_N: BigInt = BigInt::from_str( "115792089210356248762697446949407573529996955224135760342422259061068512044369" ).unwrap(); //SECP256R1_ALPHA = 0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC diff --git a/src/hint_processor/builtin_hint_processor/secp/signature.rs b/src/hint_processor/builtin_hint_processor/secp/signature.rs index b8f369ca1c..d4a2d3b0a4 100644 --- a/src/hint_processor/builtin_hint_processor/secp/signature.rs +++ b/src/hint_processor/builtin_hint_processor/secp/signature.rs @@ -81,7 +81,9 @@ pub fn div_mod_n_safe_div( let b = exec_scopes.get_ref::(b_alias)?; let res = exec_scopes.get_ref::("res")?; - let value = safe_div_bigint(&(res * b - a), &N)?.add(to_add); + let n = exec_scopes.get("N")?; + + let value = safe_div_bigint(&(res * b - a), &n)?.add(to_add); exec_scopes.insert_value("value", value); Ok(()) @@ -151,6 +153,7 @@ pub fn pack_modn_div_modn( let value = div_mod(&x, &s, &N); exec_scopes.insert_value("x", x); exec_scopes.insert_value("s", s); + exec_scopes.insert_value("N", N.clone()); exec_scopes.insert_value("value", value.clone()); exec_scopes.insert_value("res", value); Ok(()) @@ -218,7 +221,8 @@ mod tests { let mut exec_scopes = scope![ ("a", BigInt::zero()), ("b", BigInt::one()), - ("res", BigInt::one()) + ("res", BigInt::one()), + ("N", N.clone()) ]; assert_matches!( div_mod_n_safe_div( @@ -308,6 +312,7 @@ mod tests { #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn pack_modn_div_modn_ok() { let hint_code = hint_code::PACK_MODN_DIV_MODN; + let mut exec_scopes = scope![("N", N.clone())]; let mut vm = vm!(); vm.segments = segments![ @@ -320,7 +325,6 @@ mod tests { ]; vm.run_context.fp = 3; let ids_data = non_continuous_ids_data![("x", -3), ("s", 0)]; - let mut exec_scopes = ExecutionScopes::new(); assert_matches!(run_hint!(vm, ids_data, hint_code, &mut exec_scopes), Ok(())); assert_matches!(div_mod_n_safe_div(&mut exec_scopes, "x", "s", 0), Ok(())); } diff --git a/src/tests/cairo_run_test.rs b/src/tests/cairo_run_test.rs index 9d8c6d4e41..e237566c28 100644 --- a/src/tests/cairo_run_test.rs +++ b/src/tests/cairo_run_test.rs @@ -793,6 +793,13 @@ fn is_zero() { run_program_simple(program_data.as_slice()); } +#[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +fn cairo_run_secp256r1_div_mod_n() { + let program_data = include_bytes!("../../cairo_programs/secp256r1_div_mod_n.json"); + run_program_simple(program_data.as_slice()); +} + #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn is_zero_pack() {