From 83108ec0936cd1dd7a4651f11de048af375309e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s?= <47506558+MegaRedHand@users.noreply.github.com> Date: Mon, 17 Apr 2023 17:36:56 -0300 Subject: [PATCH] feat(hints): add NewHint#25 and NewHint#24 (#994) * Add NewHint#26 * Update changelog * Change assert_matches for assert + is_ok * Get and insert SECP_P from scope where applicable * Add NewHint#25 * Add NewHint#24 * Update changelog * Appease clippy * Rename hint codes * Get or insert N from scope * Change test contract * Fix test * Fix test (for real this time) --- CHANGELOG.md | 16 +++ cairo_programs/div_mod_n.cairo | 129 ++++++++++++++++++ .../builtin_hint_processor_definition.rs | 17 ++- .../builtin_hint_processor/hint_code.rs | 12 +- .../builtin_hint_processor/secp/signature.rs | 81 +++++++---- src/tests/cairo_run_test.rs | 7 + 6 files changed, 232 insertions(+), 30 deletions(-) create mode 100644 cairo_programs/div_mod_n.cairo diff --git a/CHANGELOG.md b/CHANGELOG.md index a09ff06ad4..e6e11d7e2d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,6 +31,22 @@ * Added dynamic layout [#879](https://github.com/lambdaclass/cairo-rs/pull/879) * `get_segment_size` was exposed [#934](https://github.com/lambdaclass/cairo-rs/pull/934) +* Add missing hints on cairo_secp lib [#994](https://github.com/lambdaclass/cairo-rs/pull/994):: + + `BuiltinHintProcessor` now supports the following hints: + ```python + 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) + ``` + + ```python + value = k_plus_one = safe_div(res * b - a, N) + 1 + ``` + * Add missing hint on cairo_secp lib [#992](https://github.com/lambdaclass/cairo-rs/pull/992): `BuiltinHintProcessor` now supports the following hint: diff --git a/cairo_programs/div_mod_n.cairo b/cairo_programs/div_mod_n.cairo new file mode 100644 index 0000000000..4dbe3c8290 --- /dev/null +++ b/cairo_programs/div_mod_n.cairo @@ -0,0 +1,129 @@ +%builtins range_check + +from starkware.cairo.common.cairo_secp.bigint import BigInt3, nondet_bigint3, BASE, bigint_mul +from starkware.cairo.common.cairo_secp.constants import BETA, N0, N1, N2 + +// Source: https://github.com/myBraavos/efficient-secp256r1/blob/73cca4d53730cb8b2dcf34e36c7b8f34b96b3230/src/secp256r1/signature.cairo + +// Computes a * b^(-1) modulo the size of the elliptic curve (N). +// +// Prover assumptions: +// * All the limbs of a are in the range (-2 ** 210.99, 2 ** 210.99). +// * All the limbs of b are in the range (-2 ** 124.99, 2 ** 124.99). +// * b is in the range [0, 2 ** 256). +// +// Soundness assumptions: +// * The limbs of a are in the range (-2 ** 249, 2 ** 249). +// * The limbs of b are in the range (-2 ** 159.83, 2 ** 159.83). +func div_mod_n{range_check_ptr}(a: BigInt3, b: BigInt3) -> (res: BigInt3) { + %{ + from starkware.cairo.common.cairo_secp.secp_utils import N, 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(); + + %{ 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 div_mod_n_alt{range_check_ptr}(a: BigInt3, b: BigInt3) -> (res: BigInt3) { + // just used to import N + %{ + from starkware.cairo.common.cairo_secp.secp_utils import N, 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) + %} + + %{ + 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(); + + %{ 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); + + 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( + 3413472211745629263979533, 17305268010345238170172332, 11991751872105858217578135 + ); + + // test alternative hint + let (res_alt) = div_mod_n_alt(a, b); + + assert res_alt = res; + + 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 7383370e99..5dc58ab18c 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 @@ -38,8 +38,8 @@ use crate::{ verify_zero, verify_zero_with_external_const, }, signature::{ - div_mod_n_packed_divmod, div_mod_n_safe_div, get_point_from_x, - pack_modn_div_modn, + div_mod_n_packed_divmod, div_mod_n_packed_external_n, div_mod_n_safe_div, + get_point_from_x, pack_modn_div_modn, }, }, segments::{relocate_segment, temporary_array}, @@ -350,13 +350,20 @@ impl HintProcessor for BuiltinHintProcessor { } hint_code::IS_ZERO_NONDET => is_zero_nondet(vm, exec_scopes), hint_code::IS_ZERO_ASSIGN_SCOPE_VARS => is_zero_assign_scope_variables(exec_scopes), - hint_code::DIV_MOD_N_PACKED_DIVMOD => div_mod_n_packed_divmod( + hint_code::DIV_MOD_N_PACKED_DIVMOD_V1 => div_mod_n_packed_divmod( vm, exec_scopes, &hint_data.ids_data, &hint_data.ap_tracking, ), - hint_code::DIV_MOD_N_SAFE_DIV => div_mod_n_safe_div(exec_scopes, "a", "b"), + hint_code::DIV_MOD_N_PACKED_DIVMOD_EXTERNAL_N => div_mod_n_packed_external_n( + vm, + exec_scopes, + &hint_data.ids_data, + &hint_data.ap_tracking, + ), + hint_code::DIV_MOD_N_SAFE_DIV => div_mod_n_safe_div(exec_scopes, "a", "b", 0), + hint_code::DIV_MOD_N_SAFE_DIV_PLUS_ONE => div_mod_n_safe_div(exec_scopes, "a", "b", 1), hint_code::GET_POINT_FROM_X => get_point_from_x( vm, exec_scopes, @@ -493,7 +500,7 @@ impl HintProcessor for BuiltinHintProcessor { hint_code::PACK_MODN_DIV_MODN => { pack_modn_div_modn(vm, exec_scopes, &hint_data.ids_data, &hint_data.ap_tracking) } - hint_code::XS_SAFE_DIV => div_mod_n_safe_div(exec_scopes, "x", "s"), + hint_code::XS_SAFE_DIV => div_mod_n_safe_div(exec_scopes, "x", "s", 0), hint_code::UINT384_UNSIGNED_DIV_REM => { uint384_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 6131ead533..872a6ec5cf 100644 --- a/src/hint_processor/builtin_hint_processor/hint_code.rs +++ b/src/hint_processor/builtin_hint_processor/hint_code.rs @@ -440,7 +440,14 @@ from starkware.python.math_utils import div_mod value = x_inv = div_mod(1, x, SECP_P)"#; -pub const DIV_MOD_N_PACKED_DIVMOD: &str = r#"from starkware.cairo.common.cairo_secp.secp_utils import N, pack +pub const DIV_MOD_N_PACKED_DIVMOD_V1: &str = r#"from starkware.cairo.common.cairo_secp.secp_utils import N, 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)"#; + +pub const DIV_MOD_N_PACKED_DIVMOD_EXTERNAL_N: &str = r#"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) @@ -449,6 +456,9 @@ value = res = div_mod(a, b, N)"#; pub const DIV_MOD_N_SAFE_DIV: &str = r#"value = k = safe_div(res * b - a, N)"#; +pub const DIV_MOD_N_SAFE_DIV_PLUS_ONE: &str = + r#"value = k_plus_one = safe_div(res * b - a, N) + 1"#; + pub const GET_POINT_FROM_X: &str = r#"from starkware.cairo.common.cairo_secp.secp_utils import SECP_P, pack x_cube_int = pack(ids.x_cube, PRIME) % SECP_P diff --git a/src/hint_processor/builtin_hint_processor/secp/signature.rs b/src/hint_processor/builtin_hint_processor/secp/signature.rs index 84dd9e5979..54b13b0bf4 100644 --- a/src/hint_processor/builtin_hint_processor/secp/signature.rs +++ b/src/hint_processor/builtin_hint_processor/secp/signature.rs @@ -1,7 +1,5 @@ -use crate::any_box; -use crate::stdlib::{collections::HashMap, ops::Shr, prelude::*}; - use crate::{ + any_box, hint_processor::{ builtin_hint_processor::{ hint_utils::get_integer_from_var_name, @@ -11,10 +9,11 @@ use crate::{ }, math_utils::{div_mod, safe_div_bigint}, serde::deserialize_program::ApTracking, + stdlib::{collections::HashMap, ops::Shr, prelude::*}, types::exec_scope::ExecutionScopes, - vm::errors::hint_errors::HintError, - vm::vm_core::VirtualMachine, + vm::{errors::hint_errors::HintError, vm_core::VirtualMachine}, }; +use core::ops::Add; use felt::Felt252; use num_bigint::BigInt; use num_integer::Integer; @@ -32,16 +31,17 @@ a = pack(ids.a, PRIME) b = pack(ids.b, PRIME) value = res = div_mod(a, b, N) */ -pub fn div_mod_n_packed_divmod( +pub fn div_mod_n_packed( vm: &mut VirtualMachine, exec_scopes: &mut ExecutionScopes, ids_data: &HashMap, ap_tracking: &ApTracking, + n: &BigInt, ) -> Result<(), HintError> { let a = pack(BigInt3::from_var_name("a", vm, ids_data, ap_tracking)?); let b = pack(BigInt3::from_var_name("b", vm, ids_data, ap_tracking)?); - let value = div_mod(&a, &b, &N); + let value = div_mod(&a, &b, n); exec_scopes.insert_value("a", a); exec_scopes.insert_value("b", b); exec_scopes.insert_value("value", value.clone()); @@ -49,18 +49,39 @@ pub fn div_mod_n_packed_divmod( Ok(()) } +pub fn div_mod_n_packed_divmod( + vm: &mut VirtualMachine, + exec_scopes: &mut ExecutionScopes, + ids_data: &HashMap, + ap_tracking: &ApTracking, +) -> Result<(), HintError> { + exec_scopes.assign_or_update_variable("N", any_box!(N.clone())); + div_mod_n_packed(vm, exec_scopes, ids_data, ap_tracking, &N) +} + +pub fn div_mod_n_packed_external_n( + vm: &mut VirtualMachine, + exec_scopes: &mut ExecutionScopes, + ids_data: &HashMap, + ap_tracking: &ApTracking, +) -> Result<(), HintError> { + let n = exec_scopes.get::("N")?; + div_mod_n_packed(vm, exec_scopes, ids_data, ap_tracking, &n) +} + // Implements hint: // value = k = safe_div(res * b - a, N) pub fn div_mod_n_safe_div( exec_scopes: &mut ExecutionScopes, a_alias: &str, b_alias: &str, + to_add: u64, ) -> Result<(), HintError> { let a = exec_scopes.get_ref::(a_alias)?; let b = exec_scopes.get_ref::(b_alias)?; let res = exec_scopes.get_ref::("res")?; - let value = safe_div_bigint(&(res * b - a), &N)?; + let value = safe_div_bigint(&(res * b - a), &N)?.add(to_add); exec_scopes.insert_value("value", value); Ok(()) @@ -163,22 +184,33 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn safe_div_ok() { - let hint_code = hint_code::DIV_MOD_N_PACKED_DIVMOD; - let mut vm = vm!(); + // "import N" + let mut exec_scopes = ExecutionScopes::new(); + exec_scopes.assign_or_update_variable("N", any_box!(N.clone())); - vm.segments = segments![ - ((1, 0), 15), - ((1, 1), 3), - ((1, 2), 40), - ((1, 3), 0), - ((1, 4), 10), - ((1, 5), 1) + let hint_codes = vec![ + hint_code::DIV_MOD_N_PACKED_DIVMOD_V1, + hint_code::DIV_MOD_N_PACKED_DIVMOD_EXTERNAL_N, ]; - vm.run_context.fp = 3; - let ids_data = non_continuous_ids_data![("a", -3), ("b", 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, "a", "b"), Ok(())); + for hint_code in hint_codes { + let mut vm = vm!(); + + vm.segments = segments![ + ((1, 0), 15), + ((1, 1), 3), + ((1, 2), 40), + ((1, 3), 0), + ((1, 4), 10), + ((1, 5), 1) + ]; + vm.run_context.fp = 3; + let ids_data = non_continuous_ids_data![("a", -3), ("b", 0)]; + + assert_matches!(run_hint!(vm, ids_data, hint_code, &mut exec_scopes), Ok(())); + + assert_matches!(div_mod_n_safe_div(&mut exec_scopes, "a", "b", 0), Ok(())); + assert_matches!(div_mod_n_safe_div(&mut exec_scopes, "a", "b", 1), Ok(())); + } } #[test] @@ -193,7 +225,8 @@ mod tests { div_mod_n_safe_div( &mut exec_scopes, "a", - "b" + "b", + 0, ), Err( HintError::Math(MathError::SafeDivFailBigInt( @@ -290,6 +323,6 @@ mod tests { 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"), 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 9429dd94ae..465d51e8f0 100644 --- a/src/tests/cairo_run_test.rs +++ b/src/tests/cairo_run_test.rs @@ -1322,3 +1322,10 @@ fn cairo_run_efficient_secp256r1_ec() { let program_data = include_bytes!("../../cairo_programs/efficient_secp256r1_ec.json"); run_program_simple(program_data.as_slice()); } + +#[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +fn cairo_run_div_mod_n() { + let program_data = include_bytes!("../../cairo_programs/div_mod_n.json"); + run_program_simple(program_data.as_slice()); +}