Skip to content

Commit

Permalink
feat: SECP related hints (lambdaclass#1829)
Browse files Browse the repository at this point in the history
* feat: secp-related hints

Problem: we need an implementation of the hints used by the Starknet OS
in the secp syscalls. These hints rely on private primitives in
`cairo-vm` and need to be implemented here.

Solution: this PR adds an implementation of all the hints that require
`cairo-vm` primitives in the `cairo-vm` repository.

* PR link in changelog

* fix: handle unwraps in secp hints with HintError variants

* add: cairo-0-secp-hints to allow enabling secp related hints

* lint: clippy replace `.get(0)` with `.first()`

* tests: add integration tests for cairo-0-secp hints

* fix: add missing feature `cairo-0-secp-hints` in tests

* fix: get the ec_cairo test running

* fix: move the test programs to own dir and update Makefile

* fix: remove some test remnants

* fix: move cairo-0-secp-hints to special_features matrix

* lint: cargo fmt

* fix: remove unneeed recipes

* fix: remove SECP_CAIRO0_HINTS_PROOF_TESTS

* fix: remove unused error variant

* fix: use div_mod_floor instead of div_rem

* revert: use div_mod_floor instead of div_rem

* changelog: add this PR to upcoming changes

* fix: remove unnecessary newline

---------

Co-authored-by: whichqua <[email protected]>
Co-authored-by: Herman Obst Demaestri <[email protected]>
  • Loading branch information
3 people authored Dec 3, 2024
1 parent 4e55e35 commit 577f744
Show file tree
Hide file tree
Showing 20 changed files with 1,179 additions and 10 deletions.
5 changes: 2 additions & 3 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -323,7 +323,7 @@ jobs:
strategy:
fail-fast: false
matrix:
special_features: ["", "extensive_hints", "mod_builtin"]
special_features: ["", "extensive_hints", "mod_builtin", "cairo-0-secp-hints"]
target: [ test#1, test#2, test#3, test#4, test-no_std#1, test-no_std#2, test-no_std#3, test-no_std#4, test-wasm ]
name: Run tests
runs-on: ubuntu-22.04
Expand Down Expand Up @@ -370,7 +370,7 @@ jobs:
'test')
cargo llvm-cov nextest --lcov --output-path lcov-${{ matrix.target }}-${{ matrix.special_features }}.info \
--partition count:${PARTITION}/4 \
--workspace --features "cairo-1-hints, test_utils, ${{ matrix.special_features }}"
--workspace --features "cairo-1-hints, test_utils, ${{ matrix.special_features }}"
;;
'test-no_std')
cargo llvm-cov nextest --lcov --output-path lcov-${{ matrix.target }}-${{ matrix.special_features }}.info \
Expand Down Expand Up @@ -844,4 +844,3 @@ jobs:

- name: Run comparison
run: ./vm/src/tests/compare_all_pie_outputs.sh

2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

#### Upcoming Changes

* feat: Implement `SECP related` hints [#1829](https://github.com/lambdaclass/cairo-vm/pull/1829)

* fix: [#1862](https://github.com/lambdaclass/cairo-vm/pull/1862):
* Use MaybeRelocatable for relocation table

Expand Down
9 changes: 7 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,10 @@ BAD_TEST_DIR=cairo_programs/bad_programs
BAD_TEST_FILES:=$(wildcard $(BAD_TEST_DIR)/*.cairo)
COMPILED_BAD_TESTS:=$(patsubst $(BAD_TEST_DIR)/%.cairo, $(BAD_TEST_DIR)/%.json, $(BAD_TEST_FILES))

SECP_CAIRO0_HINTS_DIR=cairo_programs/cairo-0-secp-hints-feature
SECP_CAIRO0_HINTS_FILES:=$(wildcard $(SECP_CAIRO0_HINTS_DIR)/*.cairo)
COMPILED_SECP_CAIRO0_HINTS:=$(patsubst $(SECP_CAIRO0_HINTS_DIR)/%.cairo, $(SECP_CAIRO0_HINTS_DIR)/%.json, $(SECP_CAIRO0_HINTS_FILES))

PRINT_TEST_DIR=cairo_programs/print_feature
PRINT_TEST_FILES:=$(wildcard $(PRINT_TEST_DIR)/*.cairo)
COMPILED_PRINT_TESTS:=$(patsubst $(PRINT_TEST_DIR)/%.cairo, $(PRINT_TEST_DIR)/%.json, $(PRINT_TEST_FILES))
Expand Down Expand Up @@ -239,7 +243,7 @@ run:
check:
cargo check

cairo_test_programs: $(COMPILED_TESTS) $(COMPILED_BAD_TESTS) $(COMPILED_NORETROCOMPAT_TESTS) $(COMPILED_PRINT_TESTS) $(COMPILED_MOD_BUILTIN_TESTS)
cairo_test_programs: $(COMPILED_TESTS) $(COMPILED_BAD_TESTS) $(COMPILED_NORETROCOMPAT_TESTS) $(COMPILED_PRINT_TESTS) $(COMPILED_MOD_BUILTIN_TESTS) $(COMPILED_SECP_CAIRO0_HINTS)
cairo_proof_programs: $(COMPILED_PROOF_TESTS) $(COMPILED_MOD_BUILTIN_PROOF_TESTS)
cairo_bench_programs: $(COMPILED_BENCHES)
cairo_1_test_contracts: $(CAIRO_1_COMPILED_CASM_CONTRACTS)
Expand All @@ -264,7 +268,7 @@ test-wasm: cairo_proof_programs cairo_test_programs
# NOTE: release mode is needed to avoid "too many locals" error
wasm-pack test --release --node vm --no-default-features
test-extensive_hints: cairo_proof_programs cairo_test_programs
$(TEST_COMMAND) --workspace --features "test_utils, cairo-1-hints, extensive_hints"
$(TEST_COMMAND) --workspace --features "test_utils, cairo-1-hints, cairo-0-secp-hints, extensive_hints"

check-fmt:
cargo fmt --all -- --check
Expand Down Expand Up @@ -344,6 +348,7 @@ clean:
rm -f $(TEST_DIR)/*.pie
rm -f $(BENCH_DIR)/*.json
rm -f $(BAD_TEST_DIR)/*.json
rm -f $(SECP_CAIRO0_HINTS_DIR)/*.json
rm -f $(PRINT_TEST_DIR)/*.json
rm -f $(CAIRO_1_CONTRACTS_TEST_DIR)/*.sierra
rm -f $(CAIRO_1_CONTRACTS_TEST_DIR)/*.casm
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
%builtins range_check

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

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

func test_q_mod_prime{range_check_ptr: felt}(val: UnreducedBigInt3) {
let q = [ap];
%{
from starkware.cairo.common.cairo_secp.secp256r1_utils import SECP256R1_P
from starkware.cairo.common.cairo_secp.secp_utils import pack
q, r = divmod(pack(ids.val, PRIME), SECP256R1_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 ();
}

func main{range_check_ptr: felt}() {
let val = UnreducedBigInt3(0, 0, 0);
test_q_mod_prime(val);
return ();
}
105 changes: 105 additions & 0 deletions cairo_programs/cairo-0-secp-hints-feature/secp_cairo0_ec.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
%builtins range_check

// Tests:
// - cairo0_hints::COMPUTE_Q_MOD_PRIME
// - cairo0_hints::COMPUTE_IDS_HIGH_LOW
// - cairo0_hints::SECP_DOUBLE_ASSIGN_NEW_X
// - cairo0_hints::FAST_SECP_ADD_ASSIGN_NEW_Y

from starkware.cairo.common.secp256r1.ec import (
EcPoint,
compute_doubling_slope,
compute_slope,
ec_double,
fast_ec_add,
ec_mul_inner,
)
from starkware.cairo.common.cairo_secp.bigint3 import BigInt3

func main{range_check_ptr: felt}() {
let x = BigInt3(1, 5, 10);
let y = BigInt3(2, 4, 20);


let point_a = EcPoint(x, y);

let point_b = EcPoint(
BigInt3(1, 5, 10),
BigInt3(77371252455336262886226989, 77371252455336267181195259, 19342813113834066795298795),
);

// let (point_c) = ec_negate(EcPoint(BigInt3(156, 6545, 100010), BigInt3(1123, -1325, 910)));
let point_c = EcPoint(
BigInt3(156, 6545, 100010),
BigInt3(77371252455336262886225868, 1324, 19342813113834066795297906),
);

// compute_doubling_slope
let (slope_a) = compute_doubling_slope(point_b);
assert slope_a = BigInt3(
64839545681970757313529612, 5953360968438044038987377, 13253714962539897079325475
);

let (slope_b) = compute_doubling_slope(
EcPoint(BigInt3(1231, 51235643, 100000), BigInt3(77371252455, 7737125245, 19342813113))
);
assert slope_b = BigInt3(
61129622008745017597879703, 29315582959606925875642332, 13600923539144215962821694
);

// compute_slope
let (slope_c) = compute_slope(point_a, point_c);
assert slope_c = BigInt3(
69736698275759322439409874, 45955733659898858347886847, 18034242868575077772302310
);

let (slope_d) = compute_slope(point_c, point_b);
assert slope_d = BigInt3(
66872739393348882319301304, 44057296979296181456999622, 6628179500048909995474229
);

// ec_double
let (point_d) = ec_double(point_a);
assert point_d = EcPoint(
BigInt3(62951442591564288805558802, 32562108923955565608466346, 18605500881547971871596634),
BigInt3(32147810383256899543807670, 5175857156528420748725791, 6618806236944685895112117),
);

let (point_e) = ec_double(
EcPoint(BigInt3(156, 6545, 100010), BigInt3(5336262886225868, 1324, 113834066795297906))
);
assert point_e = EcPoint(
BigInt3(47503316700827173496989353, 17218105161352860131668522, 527908748911931938599018),
BigInt3(50964737623371959432443726, 60451660835701602854498663, 5043009036652075489876599),
);

// fast_ec_add
let (point_f) = fast_ec_add(point_a, point_e);
assert point_f = EcPoint(
BigInt3(29666922781464823323928071, 37719311829566792810003084, 9541551049028573381125035),
BigInt3(12938160206947174373897851, 22954464827120147659997987, 2690642098017756659925259),
);

let (point_g) = fast_ec_add(
EcPoint(BigInt3(89712, 56, 7348489324), BigInt3(980126, 10, 8793)),
EcPoint(BigInt3(16451, 5967, 2171381), BigInt3(12364564, 123654, 193)),
);
assert point_g = EcPoint(
BigInt3(14771767859485410664249539, 62406103981610765545970487, 8912032684309792565082157),
BigInt3(25591125893919304137822981, 54543895003572926651874352, 18968003584818937876851951),
);

// ec_mul_inner
let (pow2, res) = ec_mul_inner(
EcPoint(
BigInt3(65162296, 359657, 04862662171381), BigInt3(5166641367474701, 63029418, 793)
),
123,
298,
);
assert pow2 = EcPoint(
BigInt3(73356165220405599685396595, 44054642183803477920871071, 5138516367480965019117743),
BigInt3(40256732918865941543909206, 68107624737772931608959283, 1842118797516663063623771),
);
return ();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
%builtins range_check

from starkware.cairo.common.secp256r1.ec import (
EcPoint,
ec_double
)
from starkware.cairo.common.cairo_secp.bigint import BigInt3

func main{range_check_ptr}() {
let x = BigInt3(235, 522, 111);
let y = BigInt3(1323, 15124, 796759);

let point = EcPoint(x, y);

let (r) = ec_double(point);

assert r.x.d0 = 64413149096815403908768532;
assert r.x.d1 = 28841630551789071202278393;
assert r.x.d2 = 11527965423300397026710769;

assert r.y.d0 = 6162628527473476058419904;
assert r.y.d1 = 69076668518034904023852368;
assert r.y.d2 = 10886445027049641070037760;

return ();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
%builtins range_check

from starkware.cairo.common.secp256r1.ec import (
EcPoint,
ec_mul_by_uint256
)
from starkware.cairo.common.uint256 import Uint256
from starkware.cairo.common.cairo_secp.bigint import BigInt3

func main{range_check_ptr: felt}() {
let x = BigInt3(235, 522, 111);
let y = BigInt3(1323, 15124, 796759);

let point = EcPoint(x, y);

let scalar = Uint256(
143186476941636880901214103594843510573, 124026708105846590725274683684370988502
);
let (res) = ec_mul_by_uint256(point, scalar);

assert res = EcPoint(
BigInt3(31454759005629465428788733, 35370111304581841775514461, 13535495107675380502530193),
BigInt3(18078210390106977421552565, 53503834862379828768870254, 3887397808398301655656699),
);
return ();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
%builtins range_check

from starkware.cairo.common.alloc import alloc
from starkware.cairo.common.cairo_secp.bigint3 import BigInt3
from starkware.cairo.common.cairo_secp.ec_point import EcPoint
from starkware.cairo.common.secp256r1.ec import (
try_get_point_from_x
)
from starkware.cairo.common.uint256 import Uint256


func main{range_check_ptr: felt}() {
let zero = BigInt3(
0, 0, 0
);
let result: EcPoint* = alloc();
let (is_on_curve) = try_get_point_from_x(zero, 0, result);
assert is_on_curve = 1;

let x = BigInt3(512,2412,133);
let result: EcPoint* = alloc();
let (is_on_curve) = try_get_point_from_x(x, 1, result);
assert is_on_curve = 1;

let x = BigInt3(64,0,6546);

let result: EcPoint* = alloc();
let (is_on_curve) = try_get_point_from_x(x, 1, result);
assert is_on_curve = 0;
return ();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
%builtins range_check

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

func reduce_value{range_check_ptr}(x: UnreducedBigInt3) -> (res: BigInt3) {
%{
from starkware.cairo.common.cairo_secp.secp256r1_utils import SECP256R1_P
from starkware.cairo.common.cairo_secp.secp_utils import pack
value = pack(ids.x, PRIME) % SECP256R1_P
%}
let (res) = nondet_bigint3();
return (res=res);
}

func test_reduce_value{range_check_ptr: felt}() {
let x = UnreducedBigInt3(0, 0, 0);
let (reduce_a) = reduce_value(x);
assert reduce_a = BigInt3(
0, 0, 0
);

let y = UnreducedBigInt3(12354, 745634534, 81298789312879123);
let (reduce_b) = reduce_value(y);
assert reduce_b = BigInt3(
12354, 745634534, 81298789312879123
);

let z = UnreducedBigInt3(12354812987893128791212331231233, 7453123123123123312634534, 8129224990312325879);
let (reduce_c) = reduce_value(z);
assert reduce_c = BigInt3(
16653320122975184709085185, 7453123123123123312794216, 8129224990312325879
);
return ();
}

func main{range_check_ptr: felt}() {
test_reduce_value();
return ();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
%builtins range_check

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

func reduce_x{range_check_ptr}(x: UnreducedBigInt3) -> (res: BigInt3) {
%{
from starkware.cairo.common.cairo_secp.secp256r1_utils import SECP256R1_P
from starkware.cairo.common.cairo_secp.secp_utils import pack
value = pack(ids.x, PRIME) % SECP256R1_P
%}
let (res) = nondet_bigint3();
return (res=res);
}

func test_reduce_x{range_check_ptr: felt}() {
let x = UnreducedBigInt3(0, 0, 0);
let (reduce_a) = reduce_x(x);
assert reduce_a = BigInt3(
0, 0, 0
);

let y = UnreducedBigInt3(12354, 745634534, 81298789312879123);
let (reduce_b) = reduce_x(y);
assert reduce_b = BigInt3(
12354, 745634534, 81298789312879123
);

let z = UnreducedBigInt3(12354812987893128791212331231233, 7453123123123123312634534, 8129224990312325879);
let (reduce_c) = reduce_x(z);
assert reduce_c = BigInt3(
16653320122975184709085185, 7453123123123123312794216, 8129224990312325879
);
return ();
}

func main{range_check_ptr: felt}() {
test_reduce_x();
return ();
}
Loading

0 comments on commit 577f744

Please sign in to comment.