Skip to content

Commit

Permalink
feat(hints): add NewHint#58 (#1054)
Browse files Browse the repository at this point in the history
* Add NewHint#58

* Update changelog

* Remove `dbg!`

Co-authored-by: Mario Rugiero <mario.rugiero@lambdaclass.com>

---------

Co-authored-by: Mario Rugiero <mario.rugiero@lambdaclass.com>
Co-authored-by: Juan Bono <juanbono94@gmail.com>
  • Loading branch information
3 people committed Apr 26, 2023
1 parent f92e167 commit 5ff1a1d
Show file tree
Hide file tree
Showing 6 changed files with 231 additions and 18 deletions.
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,19 @@

```

* Add missing hint on vrf.json lib [#1054](https://github.com/lambdaclass/cairo-rs/pull/1054):

`BuiltinHintProcessor` now supports the following hint:

```python
from starkware.cairo.common.cairo_secp.secp_utils import pack
SECP_P = 2**255-19

y = pack(ids.point.y, PRIME) % SECP_P
# The modulo operation in python always returns a nonnegative number.
value = (-y) % SECP_P
```

* Implement hint on ec_recover.json whitelist [#1032](https://github.com/lambdaclass/cairo-rs/pull/1032):

`BuiltinHintProcessor` now supports the following hint:
Expand Down
115 changes: 115 additions & 0 deletions cairo_programs/ec_negate.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
%builtins range_check

// Source: https://github.com/Astraly-Labs/Starknet-VRF/blob/33175b179627fdf1f12e32b197a368c1fefcd34c/lib/ed25519.cairo
from starkware.cairo.common.cairo_secp.bigint import BigInt3, UnreducedBigInt3, nondet_bigint3
from starkware.cairo.common.cairo_secp.ec import EcPoint

const BASE = 2 ** 86;
const SECP_REM = 19;

func verify_zero{range_check_ptr}(val: UnreducedBigInt3) {
let q = [ap];
%{
from starkware.cairo.common.cairo_secp.secp_utils import pack
SECP_P = 2**255-19
to_assert = pack(ids.val, PRIME)
q, r = divmod(pack(ids.val, PRIME), SECP_P)
assert r == 0, f"verify_zero: Invalid input {ids.val.d0, ids.val.d1, ids.val.d2}."
ids.q = q % PRIME
%}
let q_biased = [ap + 1];
q_biased = q + 2 ** 127, ap++;
[range_check_ptr] = q_biased, ap++;
// This implies that q is in the range [-2**127, 2**127).

tempvar r1 = (val.d0 + q * SECP_REM) / BASE;
assert [range_check_ptr + 1] = r1 + 2 ** 127;
// This implies that r1 is in the range [-2**127, 2**127).
// Therefore, r1 * BASE is in the range [-2**213, 2**213).
// By the soundness assumption, val.d0 is in the range (-2**250, 2**250).
// This implies that r1 * BASE = val.d0 + q * SECP_REM (as integers).

tempvar r2 = (val.d1 + r1) / BASE;
assert [range_check_ptr + 2] = r2 + 2 ** 127;
// Similarly, this implies that r2 * BASE = val.d1 + r1 (as integers).
// Therefore, r2 * BASE**2 = val.d1 * BASE + r1 * BASE.

assert val.d2 = q * (BASE / 8) - r2;
// Similarly, this implies that q * BASE / 4 = val.d2 + r2 (as integers).
// Therefore,
// q * BASE**3 / 4 = val.d2 * BASE**2 + r2 * BASE ** 2 =
// val.d2 * BASE**2 + val.d1 * BASE + r1 * BASE =
// val.d2 * BASE**2 + val.d1 * BASE + val.d0 + q * SECP_REM =
// val + q * SECP_REM.
// Hence, val = q * (BASE**3 / 4 - SECP_REM) = q * (2**256 - SECP_REM) = q * secp256k1_prime.

let range_check_ptr = range_check_ptr + 3;
return ();
}

// Computes the negation of a point on the elliptic curve, which is a point with the same x value
// and the negation of the y value. If the point is the zero point, returns the zero point.
//
// Arguments:
// point - The point to operate on.
//
// Returns:
// point - The negation of the given point.
func ec_negate{range_check_ptr}(point: EcPoint) -> (point: EcPoint) {
alloc_locals;
%{
from starkware.cairo.common.cairo_secp.secp_utils import pack
SECP_P = 2**255-19

y = pack(ids.point.y, PRIME) % SECP_P
# The modulo operation in python always returns a nonnegative number.
value = (-y) % SECP_P
%}
let (minus_y) = nondet_bigint3();

// This check fails. cairo-lang's uses a different modulus, and the one used
// by this library uses a hint that's not implemented
// verify_zero(
// UnreducedBigInt3(
// d0=minus_y.d0 + point.y.d0, d1=minus_y.d1 + point.y.d1, d2=minus_y.d2 + point.y.d2
// ),
// );

return (point=EcPoint(x=point.x, y=minus_y));
}

func test_ec_negate{range_check_ptr}() {
let p = EcPoint(BigInt3(1, 2, 3), BigInt3(1, 2, 3));

let (minus_p) = ec_negate(p);

assert minus_p.x.d0 = 1;
assert minus_p.x.d1 = 2;
assert minus_p.x.d2 = 3;

assert minus_p.y.d0 = 77371252455336267181195244;
assert minus_p.y.d1 = 77371252455336267181195261;
assert minus_p.y.d2 = 9671406556917033397649404;

let p = EcPoint(
BigInt3(12424, 53151, 363737),
BigInt3(77371252455336267181195244, 77371252455336267181195261, 9671406556917033397649404),
);

let (minus_p) = ec_negate(p);

assert minus_p.x.d0 = 12424;
assert minus_p.x.d1 = 53151;
assert minus_p.x.d2 = 363737;

assert minus_p.y.d0 = 1;
assert minus_p.y.d1 = 2;
assert minus_p.y.d2 = 3;

return ();
}

func main{range_check_ptr}() {
test_ec_negate();
return ();
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,15 @@ use super::{
ec_recover_sub_a_b,
},
field_arithmetic::uint384_div,
secp::secp_utils::{SECP_P, SECP_P_V2},
vrf::{
fq::{inv_mod_p_uint256, uint512_unsigned_div_rem},
inv_mod_p_uint512::inv_mod_p_uint512,
secp::ec_utils::{ec_negate_embedded_secp_p, ec_negate_import_secp_p},
secp::{
ec_utils::{compute_slope_and_assing_secp_p, ec_double_assign_new_y},
secp_utils::{SECP_P, SECP_P_V2},
},
vrf::fq::inv_mod_p_uint256,
vrf::{fq::uint512_unsigned_div_rem, inv_mod_p_uint512::inv_mod_p_uint512},
};
use crate::hint_processor::builtin_hint_processor::secp::ec_utils::ec_double_assign_new_x;
use crate::{
hint_processor::{
builtin_hint_processor::{
Expand Down Expand Up @@ -45,8 +48,7 @@ use crate::{
secp::{
bigint_utils::{bigint_to_uint256, hi_max_bitlen, nondet_bigint3},
ec_utils::{
compute_doubling_slope, compute_slope, compute_slope_and_assing_secp_p, di_bit,
ec_double_assign_new_x, ec_double_assign_new_y, ec_mul_inner, ec_negate,
compute_doubling_slope, compute_slope, di_bit, ec_mul_inner,
fast_ec_add_assign_new_x, fast_ec_add_assign_new_y, import_secp256r1_p,
quad_bit,
},
Expand Down Expand Up @@ -438,9 +440,18 @@ impl HintProcessor for BuiltinHintProcessor {
&hint_data.ap_tracking,
constants,
),
hint_code::EC_NEGATE => {
ec_negate(vm, exec_scopes, &hint_data.ids_data, &hint_data.ap_tracking)
}
hint_code::EC_NEGATE => ec_negate_import_secp_p(
vm,
exec_scopes,
&hint_data.ids_data,
&hint_data.ap_tracking,
),
hint_code::EC_NEGATE_EMBEDDED_SECP => ec_negate_embedded_secp_p(
vm,
exec_scopes,
&hint_data.ids_data,
&hint_data.ap_tracking,
),
hint_code::EC_DOUBLE_SCOPE => compute_doubling_slope(
vm,
exec_scopes,
Expand Down
7 changes: 7 additions & 0 deletions src/hint_processor/builtin_hint_processor/hint_code.rs
Original file line number Diff line number Diff line change
Expand Up @@ -605,6 +605,13 @@ y = pack(ids.point.y, PRIME) % SECP_P
# The modulo operation in python always returns a nonnegative number.
value = (-y) % SECP_P"#;

pub const EC_NEGATE_EMBEDDED_SECP: &str = r#"from starkware.cairo.common.cairo_secp.secp_utils import pack
SECP_P = 2**255-19
y = pack(ids.point.y, PRIME) % SECP_P
# The modulo operation in python always returns a nonnegative number.
value = (-y) % SECP_P"#;

pub const EC_DOUBLE_SCOPE: &str = r#"from starkware.cairo.common.cairo_secp.secp_utils import SECP_P, pack
from starkware.python.math_utils import ec_double_slope
Expand Down
78 changes: 69 additions & 9 deletions src/hint_processor/builtin_hint_processor/secp/ec_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,26 @@ impl EcPoint<'_> {
}
}

/*
Implements main logic for `EC_NEGATE` and `EC_NEGATE_EMBEDDED_SECP` hints
*/
pub fn ec_negate(
vm: &mut VirtualMachine,
exec_scopes: &mut ExecutionScopes,
ids_data: &HashMap<String, HintReference>,
ap_tracking: &ApTracking,
secp_p: BigInt,
) -> Result<(), HintError> {
//ids.point
let point_y = (get_relocatable_from_var_name("point", vm, ids_data, ap_tracking)? + 3i32)?;
let y_bigint3 = BigInt3::from_base_addr(point_y, "point.y", vm)?;
let y = y_bigint3.pack86();
let value = (-y).mod_floor(&secp_p);
exec_scopes.insert_value("value", value);
exec_scopes.insert_value("SECP_P", secp_p);
Ok(())
}

/*
Implements hint:
%{
Expand All @@ -53,20 +73,34 @@ Implements hint:
value = (-y) % SECP_P
%}
*/
pub fn ec_negate(
pub fn ec_negate_import_secp_p(
vm: &mut VirtualMachine,
exec_scopes: &mut ExecutionScopes,
ids_data: &HashMap<String, HintReference>,
ap_tracking: &ApTracking,
) -> Result<(), HintError> {
exec_scopes.insert_value("SECP_P", SECP_P.clone());
//ids.point
let point_y = (get_relocatable_from_var_name("point", vm, ids_data, ap_tracking)? + 3i32)?;
let y_bigint3 = BigInt3::from_base_addr(point_y, "point.y", vm)?;
let y = y_bigint3.pack86();
let value = (-y).mod_floor(&SECP_P);
exec_scopes.insert_value("value", value);
Ok(())
ec_negate(vm, exec_scopes, ids_data, ap_tracking, SECP_P.clone())
}

/*
Implements hint:
%{
from starkware.cairo.common.cairo_secp.secp_utils import pack
SECP_P = 2**255-19
y = pack(ids.point.y, PRIME) % SECP_P
# The modulo operation in python always returns a nonnegative number.
value = (-y) % SECP_P
%}
*/
pub fn ec_negate_embedded_secp_p(
vm: &mut VirtualMachine,
exec_scopes: &mut ExecutionScopes,
ids_data: &HashMap<String, HintReference>,
ap_tracking: &ApTracking,
) -> Result<(), HintError> {
let secp_p = (BigInt::one() << 255) - 19;
ec_negate(vm, exec_scopes, ids_data, ap_tracking, secp_p)
}

/*
Expand Down Expand Up @@ -424,6 +458,32 @@ mod tests {
);
}

#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn run_ec_negate_embedded_secp_p_ok() {
let hint_code = hint_code::EC_NEGATE_EMBEDDED_SECP;
let mut vm = vm_with_range_check!();

let (y0, y1, y2) = (2645i32, 454i32, 206i32);

let y = (BigInt::from(y2) << (86 * 2)) + (BigInt::from(y1) << 86) + y0;
let minus_y = (BigInt::one() << 255) - 19 - y;

vm.segments = segments![((1, 3), y0), ((1, 4), y1), ((1, 5), y2)];
//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 'value' is defined in the vm scope
assert_matches!(
exec_scopes.get::<BigInt>("value"),
Ok(x) if x == minus_y
);
}

#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn run_compute_doubling_slope_ok() {
Expand Down
7 changes: 7 additions & 0 deletions src/tests/cairo_run_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -862,3 +862,10 @@ fn cairo_run_compute_slope_v2_test() {
let program_data = include_bytes!("../../cairo_programs/compute_slope_v2.json");
run_program_simple(program_data.as_slice());
}

#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn cairo_run_ec_negate() {
let program_data = include_bytes!("../../cairo_programs/ec_negate.json");
run_program_simple_with_memory_holes(program_data.as_slice(), 0);
}

0 comments on commit 5ff1a1d

Please sign in to comment.