Skip to content
36 changes: 36 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,42 @@

#### Upcoming Changes

* Add missing hint on cairo_secp lib [#989]:

`BuiltinHintProcessor` now supports the following hint:
```python
from starkware.cairo.common.cairo_secp.secp_utils import SECP_P
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
```

* Add missing hint on cairo_secp lib [#986]:
`BuiltinHintProcessor` now supports the following hint:
```python
from starkware.cairo.common.cairo_secp.secp_utils import SECP_P, pack
from starkware.python.math_utils import div_mod

# Compute the slope.
x = pack(ids.pt.x, PRIME)
y = pack(ids.pt.y, PRIME)
value = slope = div_mod(3 * x ** 2, 2 * y, SECP_P)
```

* Add missing hint on cairo_secp lib [#984]:
`BuiltinHintProcessor` now supports the following hint:
```python
from starkware.cairo.common.cairo_secp.secp_utils import SECP_P, pack
from starkware.python.math_utils import div_mod

# Compute the slope.
x0 = pack(ids.pt0.x, PRIME)
y0 = pack(ids.pt0.y, PRIME)
x1 = pack(ids.pt1.x, PRIME)
y1 = pack(ids.pt1.y, PRIME)
value = slope = div_mod(y0 - y1, x0 - x1, SECP_P)
```

* Move `Memory` into `MemorySegmentManager` [#830](https://github.com/lambdaclass/cairo-rs/pull/830)
* Structural changes:
* Remove `memory: Memory` field from `VirtualMachine`
Expand Down
125 changes: 125 additions & 0 deletions cairo_programs/ed25519_ec.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
%builtins range_check

// Source: https://github.com/NilFoundation/cairo-ed25519/blob/fee64a1a60b2e07b3b5c20df57f31d7ffcb29ac9/ed25519_ec.cairo

from starkware.cairo.common.serialize import serialize_word
from starkware.cairo.common.cairo_secp.bigint import BigInt3, UnreducedBigInt3, nondet_bigint3
from starkware.cairo.common.cairo_secp.field import (
is_zero,
unreduced_mul,
unreduced_sqr,
verify_zero,
)

// Represents a point on the elliptic curve.
// The zero point is represented using pt.x=0, as there is no point on the curve with this x value.
struct EcPoint {
x: BigInt3,
y: BigInt3,
}

// Returns the slope of the elliptic curve at the given point.
// The slope is used to compute pt + pt.
// Assumption: pt != 0.
func compute_doubling_slope{range_check_ptr}(pt: EcPoint) -> (slope: BigInt3) {
// Note that y cannot be zero: assume that it is, then pt = -pt, so 2 * pt = 0, which
// contradicts the fact that the size of the curve is odd.
%{
from starkware.cairo.common.cairo_secp.secp_utils import SECP_P, pack
from starkware.python.math_utils import div_mod

# Compute the slope.
x = pack(ids.pt.x, PRIME)
y = pack(ids.pt.y, PRIME)
value = slope = div_mod(3 * x ** 2, 2 * y, SECP_P)
%}
let (slope: BigInt3) = nondet_bigint3();

let (x_sqr: UnreducedBigInt3) = unreduced_sqr(pt.x);
let (slope_y: UnreducedBigInt3) = unreduced_mul(slope, pt.y);

verify_zero(
UnreducedBigInt3(
d0=3 * x_sqr.d0 - 2 * slope_y.d0,
d1=3 * x_sqr.d1 - 2 * slope_y.d1,
d2=3 * x_sqr.d2 - 2 * slope_y.d2,
),
);

return (slope=slope);
}

// Returns the slope of the line connecting the two given points.
// The slope is used to compute pt0 + pt1.
// Assumption: pt0.x != pt1.x (mod secp256k1_prime).
func compute_slope{range_check_ptr: felt}(pt0: EcPoint, pt1: EcPoint) -> (slope: BigInt3) {
%{
from starkware.cairo.common.cairo_secp.secp_utils import SECP_P, pack
from starkware.python.math_utils import div_mod

# Compute the slope.
x0 = pack(ids.pt0.x, PRIME)
y0 = pack(ids.pt0.y, PRIME)
x1 = pack(ids.pt1.x, PRIME)
y1 = pack(ids.pt1.y, PRIME)
value = slope = div_mod(y0 - y1, x0 - x1, SECP_P)
%}
let (slope) = nondet_bigint3();

let x_diff = BigInt3(d0=pt0.x.d0 - pt1.x.d0, d1=pt0.x.d1 - pt1.x.d1, d2=pt0.x.d2 - pt1.x.d2);
let (x_diff_slope: UnreducedBigInt3) = unreduced_mul(x_diff, slope);

verify_zero(
UnreducedBigInt3(
d0=x_diff_slope.d0 - pt0.y.d0 + pt1.y.d0,
d1=x_diff_slope.d1 - pt0.y.d1 + pt1.y.d1,
d2=x_diff_slope.d2 - pt0.y.d2 + pt1.y.d2,
),
);

return (slope=slope);
}

func test_compute_double_slope{range_check_ptr: felt}() {
let x = BigInt3(d0=33, d1=24, d2=12412);
let y = BigInt3(d0=3232, d1=122, d2=31415);

let pt = EcPoint(x=x, y=y);

// Compute slope
let (slope) = compute_doubling_slope(pt);

assert slope = BigInt3(
d0=56007611085086895200895667, d1=15076814030975805918069142, d2=6556143173243739984479201
);

return ();
}

func test_compute_slope{range_check_ptr: felt}() {
let x0 = BigInt3(d0=1, d1=5, d2=10);
let y0 = BigInt3(d0=2, d1=4, d2=20);

let pt0 = EcPoint(x=x0, y=y0);

let x1 = BigInt3(d0=3, d1=3, d2=3);
let y1 = BigInt3(d0=3, d1=5, d2=22);

let pt1 = EcPoint(x=x1, y=y1);

// Compute slope
let (slope) = compute_slope(pt0, pt1);

assert slope = BigInt3(
d0=39919528597790922692721903, d1=31451568879578276714332055, d2=6756007504256943629292535
);

return ();
}

func main{range_check_ptr: felt}() {
test_compute_double_slope();
test_compute_slope();

return ();
}
66 changes: 66 additions & 0 deletions cairo_programs/ed25519_field.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
%builtins range_check

// Source: https://github.com/NilFoundation/cairo-ed25519/blob/fee64a1a60b2e07b3b5c20df57f31d7ffcb29ac9/ed25519_field.cairo

from starkware.cairo.common.cairo_secp.bigint import BASE, BigInt3, UnreducedBigInt3, nondet_bigint3
from starkware.cairo.common.cairo_secp.constants import SECP_REM

// Verifies that the given unreduced value is equal to zero modulo the secp256k1 prime.
// Completeness assumption: val's limbs are in the range (-2**210.99, 2**210.99).
// Soundness assumption: val's limbs are in the range (-2**250, 2**250).
func verify_zero{range_check_ptr}(val: UnreducedBigInt3) {
let x = val;
// Used just to import pack in scope
%{
from starkware.cairo.common.cairo_secp.secp_utils import SECP_P, pack

value = pack(ids.x, PRIME) % SECP_P
%}
nondet_bigint3();

let q = [ap];
%{
from starkware.cairo.common.cairo_secp.secp_utils import SECP_P
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++;

tempvar r1 = (val.d0 + q * SECP_REM) / BASE;
assert [range_check_ptr + 1] = r1 + 2 ** 127;
// This implies r1 * BASE = val.d0 + q * SECP_REM (as integers).

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

assert val.d2 = q * (BASE / 4) - r2;
// This implies 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).

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

func test_verify_zero{range_check_ptr: felt}() {
let val = UnreducedBigInt3(0, 0, 0);

verify_zero(val);

return ();
}

func main{range_check_ptr: felt}() {
test_verify_zero();

return ();
}
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,9 @@ impl HintProcessor for BuiltinHintProcessor {
hint_code::BLAKE2S_COMPUTE => {
compute_blake2s(vm, &hint_data.ids_data, &hint_data.ap_tracking)
}
hint_code::VERIFY_ZERO => verify_zero(vm, &hint_data.ids_data, &hint_data.ap_tracking),
hint_code::VERIFY_ZERO_V1 | hint_code::VERIFY_ZERO_V2 => {
verify_zero(vm, &hint_data.ids_data, &hint_data.ap_tracking)
}
hint_code::NONDET_BIGINT3 => {
nondet_bigint3(vm, exec_scopes, &hint_data.ids_data, &hint_data.ap_tracking)
}
Expand Down Expand Up @@ -353,12 +355,36 @@ impl HintProcessor for BuiltinHintProcessor {
hint_code::EC_NEGATE => {
ec_negate(vm, exec_scopes, &hint_data.ids_data, &hint_data.ap_tracking)
}
hint_code::EC_DOUBLE_SCOPE => {
compute_doubling_slope(vm, exec_scopes, &hint_data.ids_data, &hint_data.ap_tracking)
}
hint_code::COMPUTE_SLOPE => {
compute_slope(vm, exec_scopes, &hint_data.ids_data, &hint_data.ap_tracking)
}
hint_code::EC_DOUBLE_SCOPE => compute_doubling_slope(
vm,
exec_scopes,
&hint_data.ids_data,
&hint_data.ap_tracking,
"point",
),
hint_code::EC_DOUBLE_SCOPE_WHITELIST => compute_doubling_slope(
vm,
exec_scopes,
&hint_data.ids_data,
&hint_data.ap_tracking,
"pt",
),
hint_code::COMPUTE_SLOPE => compute_slope(
vm,
exec_scopes,
&hint_data.ids_data,
&hint_data.ap_tracking,
"point0",
"point1",
),
hint_code::COMPUTE_SLOPE_WHITELIST => compute_slope(
vm,
exec_scopes,
&hint_data.ids_data,
&hint_data.ap_tracking,
"pt0",
"pt1",
),
hint_code::EC_DOUBLE_ASSIGN_NEW_X => {
ec_double_assign_new_x(vm, exec_scopes, &hint_data.ids_data, &hint_data.ap_tracking)
}
Expand Down
25 changes: 24 additions & 1 deletion src/hint_processor/builtin_hint_processor/hint_code.rs
Original file line number Diff line number Diff line change
Expand Up @@ -372,12 +372,17 @@ pub(crate) const NONDET_BIGINT3: &str = r#"from starkware.cairo.common.cairo_sec

segments.write_arg(ids.res.address_, split(value))"#;

pub(crate) const VERIFY_ZERO: &str = r#"from starkware.cairo.common.cairo_secp.secp_utils import SECP_P, pack
pub(crate) const VERIFY_ZERO_V1: &str = r#"from starkware.cairo.common.cairo_secp.secp_utils import SECP_P, pack

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"#;

pub(crate) const VERIFY_ZERO_V2: &str = r#"from starkware.cairo.common.cairo_secp.secp_utils import SECP_P
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"#;

pub(crate) const REDUCE: &str = r#"from starkware.cairo.common.cairo_secp.secp_utils import SECP_P, pack

value = pack(ids.x, PRIME) % SECP_P"#;
Expand Down Expand Up @@ -455,6 +460,14 @@ x = pack(ids.point.x, PRIME)
y = pack(ids.point.y, PRIME)
value = slope = ec_double_slope(point=(x, y), alpha=0, p=SECP_P)"#;

pub(crate) const EC_DOUBLE_SCOPE_WHITELIST: &str = r#"from starkware.cairo.common.cairo_secp.secp_utils import SECP_P, pack
from starkware.python.math_utils import div_mod

# Compute the slope.
x = pack(ids.pt.x, PRIME)
y = pack(ids.pt.y, PRIME)
value = slope = div_mod(3 * x ** 2, 2 * y, SECP_P)"#;

pub(crate) const COMPUTE_SLOPE: &str = r#"from starkware.cairo.common.cairo_secp.secp_utils import SECP_P, pack
from starkware.python.math_utils import line_slope

Expand All @@ -465,6 +478,16 @@ x1 = pack(ids.point1.x, PRIME)
y1 = pack(ids.point1.y, PRIME)
value = slope = line_slope(point1=(x0, y0), point2=(x1, y1), p=SECP_P)"#;

pub(crate) const COMPUTE_SLOPE_WHITELIST: &str = r#"from starkware.cairo.common.cairo_secp.secp_utils import SECP_P, pack
from starkware.python.math_utils import div_mod

# Compute the slope.
x0 = pack(ids.pt0.x, PRIME)
y0 = pack(ids.pt0.y, PRIME)
x1 = pack(ids.pt1.x, PRIME)
y1 = pack(ids.pt1.y, PRIME)
value = slope = div_mod(y0 - y1, x0 - x1, SECP_P)"#;

pub(crate) const EC_DOUBLE_ASSIGN_NEW_X: &str = r#"from starkware.cairo.common.cairo_secp.secp_utils import SECP_P, pack

slope = pack(ids.slope, PRIME)
Expand Down
Loading