Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(hints): add NewHint#25 and NewHint#24 #994

Merged
merged 15 commits into from
Apr 17, 2023
16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
129 changes: 129 additions & 0 deletions cairo_programs/div_mod_n.cairo
Original file line number Diff line number Diff line change
@@ -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 ();
}
Original file line number Diff line number Diff line change
Expand Up @@ -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},
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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)
}
Expand Down
12 changes: 11 additions & 1 deletion src/hint_processor/builtin_hint_processor/hint_code.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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
Expand Down
81 changes: 57 additions & 24 deletions src/hint_processor/builtin_hint_processor/secp/signature.rs
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -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;
Expand All @@ -32,35 +31,57 @@ 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<String, HintReference>,
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());
exec_scopes.insert_value("res", value);
Ok(())
}

pub fn div_mod_n_packed_divmod(
vm: &mut VirtualMachine,
exec_scopes: &mut ExecutionScopes,
ids_data: &HashMap<String, HintReference>,
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<String, HintReference>,
ap_tracking: &ApTracking,
) -> Result<(), HintError> {
let n = exec_scopes.get::<BigInt>("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::<BigInt>(a_alias)?;
let b = exec_scopes.get_ref::<BigInt>(b_alias)?;
let res = exec_scopes.get_ref::<BigInt>("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(())
Expand Down Expand Up @@ -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]
Expand All @@ -193,7 +225,8 @@ mod tests {
div_mod_n_safe_div(
&mut exec_scopes,
"a",
"b"
"b",
0,
),
Err(
HintError::Math(MathError::SafeDivFailBigInt(
Expand Down Expand Up @@ -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(()));
}
}
7 changes: 7 additions & 0 deletions src/tests/cairo_run_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}