diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 13ecb59765..b58b6d84d8 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -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 @@ -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 \ @@ -844,4 +844,3 @@ jobs: - name: Run comparison run: ./vm/src/tests/compare_all_pie_outputs.sh - diff --git a/CHANGELOG.md b/CHANGELOG.md index fee65de5ed..75027597b0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/Makefile b/Makefile index 49183fd314..355f87ac07 100644 --- a/Makefile +++ b/Makefile @@ -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)) @@ -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) @@ -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 @@ -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 diff --git a/cairo_programs/cairo-0-secp-hints-feature/secp_cairo0_compute_q_mod_prime.cairo b/cairo_programs/cairo-0-secp-hints-feature/secp_cairo0_compute_q_mod_prime.cairo new file mode 100644 index 0000000000..5cee8bbf04 --- /dev/null +++ b/cairo_programs/cairo-0-secp-hints-feature/secp_cairo0_compute_q_mod_prime.cairo @@ -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 (); +} diff --git a/cairo_programs/cairo-0-secp-hints-feature/secp_cairo0_ec.cairo b/cairo_programs/cairo-0-secp-hints-feature/secp_cairo0_ec.cairo new file mode 100644 index 0000000000..98bc7f1cfe --- /dev/null +++ b/cairo_programs/cairo-0-secp-hints-feature/secp_cairo0_ec.cairo @@ -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 (); +} diff --git a/cairo_programs/cairo-0-secp-hints-feature/secp_cairo0_ec_double_assign_new_x.cairo b/cairo_programs/cairo-0-secp-hints-feature/secp_cairo0_ec_double_assign_new_x.cairo new file mode 100644 index 0000000000..15deaca336 --- /dev/null +++ b/cairo_programs/cairo-0-secp-hints-feature/secp_cairo0_ec_double_assign_new_x.cairo @@ -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 (); +} diff --git a/cairo_programs/cairo-0-secp-hints-feature/secp_cairo0_ec_mul_by_uint256.cairo b/cairo_programs/cairo-0-secp-hints-feature/secp_cairo0_ec_mul_by_uint256.cairo new file mode 100644 index 0000000000..4e6a939e0b --- /dev/null +++ b/cairo_programs/cairo-0-secp-hints-feature/secp_cairo0_ec_mul_by_uint256.cairo @@ -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 (); +} diff --git a/cairo_programs/cairo-0-secp-hints-feature/secp_cairo0_get_point_from_x.cairo b/cairo_programs/cairo-0-secp-hints-feature/secp_cairo0_get_point_from_x.cairo new file mode 100644 index 0000000000..8ac143a75b --- /dev/null +++ b/cairo_programs/cairo-0-secp-hints-feature/secp_cairo0_get_point_from_x.cairo @@ -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 (); +} diff --git a/cairo_programs/cairo-0-secp-hints-feature/secp_cairo0_reduce_value.cairo b/cairo_programs/cairo-0-secp-hints-feature/secp_cairo0_reduce_value.cairo new file mode 100644 index 0000000000..15f8771f64 --- /dev/null +++ b/cairo_programs/cairo-0-secp-hints-feature/secp_cairo0_reduce_value.cairo @@ -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 (); +} diff --git a/cairo_programs/cairo-0-secp-hints-feature/secp_cairo0_reduce_x.cairo b/cairo_programs/cairo-0-secp-hints-feature/secp_cairo0_reduce_x.cairo new file mode 100644 index 0000000000..fa9865c520 --- /dev/null +++ b/cairo_programs/cairo-0-secp-hints-feature/secp_cairo0_reduce_x.cairo @@ -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 (); +} diff --git a/vm/Cargo.toml b/vm/Cargo.toml index 07bcde08cd..a900717c35 100644 --- a/vm/Cargo.toml +++ b/vm/Cargo.toml @@ -29,6 +29,7 @@ cairo-1-hints = [ ] tracer = [] mod_builtin = [] +cairo-0-secp-hints = [] # Note that these features are not retro-compatible with the cairo Python VM. test_utils = ["std", "dep:arbitrary", "starknet-types-core/arbitrary", "starknet-types-core/std"] # This feature will reference every test-oriented feature diff --git a/vm/src/hint_processor/builtin_hint_processor/builtin_hint_processor_definition.rs b/vm/src/hint_processor/builtin_hint_processor/builtin_hint_processor_definition.rs index fcc6ad0dcc..15c8318b15 100644 --- a/vm/src/hint_processor/builtin_hint_processor/builtin_hint_processor_definition.rs +++ b/vm/src/hint_processor/builtin_hint_processor/builtin_hint_processor_definition.rs @@ -1,3 +1,5 @@ +#[cfg(feature = "cairo-0-secp-hints")] +use super::secp::cairo0_hints; use super::{ blake2s_utils::finalize_blake2s_v3, ec_recover::{ @@ -874,6 +876,97 @@ impl HintProcessorLogic for BuiltinHintProcessor { constants, exec_scopes, ), + #[cfg(feature = "cairo-0-secp-hints")] + cairo0_hints::COMPUTE_Q_MOD_PRIME => cairo0_hints::compute_q_mod_prime( + vm, + exec_scopes, + &hint_data.ids_data, + &hint_data.ap_tracking, + constants, + ), + #[cfg(feature = "cairo-0-secp-hints")] + cairo0_hints::COMPUTE_IDS_HIGH_LOW => cairo0_hints::compute_ids_high_low( + vm, + exec_scopes, + &hint_data.ids_data, + &hint_data.ap_tracking, + constants, + ), + #[cfg(feature = "cairo-0-secp-hints")] + cairo0_hints::SECP_DOUBLE_ASSIGN_NEW_X => cairo0_hints::secp_double_assign_new_x( + vm, + exec_scopes, + &hint_data.ids_data, + &hint_data.ap_tracking, + constants, + ), + #[cfg(feature = "cairo-0-secp-hints")] + cairo0_hints::FAST_SECP_ADD_ASSIGN_NEW_Y => cairo0_hints::fast_secp_add_assign_new_y( + vm, + exec_scopes, + &hint_data.ids_data, + &hint_data.ap_tracking, + constants, + ), + #[cfg(feature = "cairo-0-secp-hints")] + cairo0_hints::COMPUTE_VALUE_DIV_MOD => cairo0_hints::compute_value_div_mod( + vm, + exec_scopes, + &hint_data.ids_data, + &hint_data.ap_tracking, + constants, + ), + #[cfg(feature = "cairo-0-secp-hints")] + cairo0_hints::GENERATE_NIBBLES => cairo0_hints::generate_nibbles( + vm, + exec_scopes, + &hint_data.ids_data, + &hint_data.ap_tracking, + constants, + ), + + #[cfg(feature = "cairo-0-secp-hints")] + cairo0_hints::WRITE_NIBBLES_TO_MEM => cairo0_hints::write_nibbles_to_mem( + vm, + exec_scopes, + &hint_data.ids_data, + &hint_data.ap_tracking, + constants, + ), + #[cfg(feature = "cairo-0-secp-hints")] + cairo0_hints::IS_ON_CURVE_2 => cairo0_hints::is_on_curve_2( + vm, + exec_scopes, + &hint_data.ids_data, + &hint_data.ap_tracking, + constants, + ), + #[cfg(feature = "cairo-0-secp-hints")] + cairo0_hints::SECP_R1_GET_POINT_FROM_X => cairo0_hints::r1_get_point_from_x( + vm, + exec_scopes, + &hint_data.ids_data, + &hint_data.ap_tracking, + constants, + ), + + #[cfg(feature = "cairo-0-secp-hints")] + cairo0_hints::SECP_REDUCE => cairo0_hints::reduce_value( + vm, + exec_scopes, + &hint_data.ids_data, + &hint_data.ap_tracking, + constants, + ), + #[cfg(feature = "cairo-0-secp-hints")] + cairo0_hints::SECP_REDUCE_X => cairo0_hints::reduce_x( + vm, + exec_scopes, + &hint_data.ids_data, + &hint_data.ap_tracking, + constants, + ), + code => Err(HintError::UnknownHint(code.to_string().into_boxed_str())), } } diff --git a/vm/src/hint_processor/builtin_hint_processor/secp/cairo0_hints.rs b/vm/src/hint_processor/builtin_hint_processor/secp/cairo0_hints.rs new file mode 100644 index 0000000000..6fec9cb10f --- /dev/null +++ b/vm/src/hint_processor/builtin_hint_processor/secp/cairo0_hints.rs @@ -0,0 +1,671 @@ +use crate::stdlib::{ + collections::HashMap, + ops::Deref, + ops::{Add, Mul, Rem}, + prelude::*, +}; + +use crate::hint_processor::builtin_hint_processor::hint_utils::{ + get_constant_from_var_name, get_integer_from_var_name, insert_value_from_var_name, +}; +use crate::hint_processor::builtin_hint_processor::uint256_utils::Uint256; +use crate::hint_processor::hint_processor_definition::HintReference; +use crate::math_utils::{div_mod, signed_felt}; +use crate::serde::deserialize_program::ApTracking; +use crate::types::errors::math_errors::MathError; +use crate::types::exec_scope::ExecutionScopes; +use crate::vm::errors::hint_errors::HintError; +use crate::vm::vm_core::VirtualMachine; +use crate::Felt252; +use num_bigint::{BigInt, BigUint}; +use num_integer::Integer; +use num_traits::One; +use num_traits::Zero; + +use super::bigint_utils::{BigInt3, Uint384}; +use super::ec_utils::EcPoint; +use super::secp_utils::{SECP256R1_ALPHA, SECP256R1_B, SECP256R1_P}; + +pub const SECP_REDUCE: &str = r#"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"#; +pub fn reduce_value( + vm: &mut VirtualMachine, + exec_scopes: &mut ExecutionScopes, + ids_data: &HashMap, + ap_tracking: &ApTracking, + _constants: &HashMap, +) -> Result<(), HintError> { + let x = Uint384::from_var_name("x", vm, ids_data, ap_tracking)?.pack86(); + exec_scopes.insert_value("value", x.mod_floor(&SECP256R1_P)); + Ok(()) +} + +pub const SECP_REDUCE_X: &str = r#"from starkware.cairo.common.cairo_secp.secp256r1_utils import SECP256R1_P +from starkware.cairo.common.cairo_secp.secp_utils import pack + +x = pack(ids.x, PRIME) % SECP256R1_P"#; +pub fn reduce_x( + vm: &mut VirtualMachine, + exec_scopes: &mut ExecutionScopes, + ids_data: &HashMap, + ap_tracking: &ApTracking, + _constants: &HashMap, +) -> Result<(), HintError> { + let x = Uint384::from_var_name("x", vm, ids_data, ap_tracking)?.pack86(); + exec_scopes.insert_value("x", x.mod_floor(&SECP256R1_P)); + Ok(()) +} + +pub const COMPUTE_Q_MOD_PRIME: &str = r#"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"#; +pub fn compute_q_mod_prime( + vm: &mut VirtualMachine, + _exec_scopes: &mut ExecutionScopes, + ids_data: &HashMap, + ap_tracking: &ApTracking, + _constants: &HashMap, +) -> Result<(), HintError> { + let val = Uint384::from_var_name("val", vm, ids_data, ap_tracking)?.pack86(); + let (q, r) = val.div_mod_floor(&SECP256R1_P); + if !r.is_zero() { + return Err(HintError::SecpVerifyZero(Box::new(val))); + } + insert_value_from_var_name("q", Felt252::from(&q), vm, ids_data, ap_tracking)?; + Ok(()) +} + +pub const COMPUTE_IDS_HIGH_LOW: &str = r#"from starkware.cairo.common.math_utils import as_int + +# Correctness check. +value = as_int(ids.value, PRIME) % PRIME +assert value < ids.UPPER_BOUND, f'{value} is outside of the range [0, 2**165).' + +# Calculation for the assertion. +ids.high, ids.low = divmod(ids.value, ids.SHIFT)"#; +pub fn compute_ids_high_low( + vm: &mut VirtualMachine, + exec_scopes: &mut ExecutionScopes, + ids_data: &HashMap, + ap_tracking: &ApTracking, + constants: &HashMap, +) -> Result<(), HintError> { + exec_scopes.insert_value::("SECP256R1_P", SECP256R1_P.clone()); + + const UPPER_BOUND: &str = "starkware.cairo.common.math.assert_250_bit.UPPER_BOUND"; + const SHIFT: &str = "starkware.cairo.common.math.assert_250_bit.SHIFT"; + + let upper_bound = constants + .get(UPPER_BOUND) + .map_or_else(|| get_constant_from_var_name("UPPER_BOUND", constants), Ok)?; + let shift = constants + .get(SHIFT) + .map_or_else(|| get_constant_from_var_name("SHIFT", constants), Ok)?; + let value = Felt252::from(&signed_felt(get_integer_from_var_name( + "value", + vm, + ids_data, + ap_tracking, + )?)); + if &value > upper_bound { + return Err(HintError::ValueOutside250BitRange(Box::new(value))); + } + + let (high, low) = value.div_rem(&shift.try_into().map_err(|_| MathError::DividedByZero)?); + insert_value_from_var_name("high", high, vm, ids_data, ap_tracking)?; + insert_value_from_var_name("low", low, vm, ids_data, ap_tracking)?; + Ok(()) +} + +pub const SECP_R1_GET_POINT_FROM_X: &str = r#"from starkware.cairo.common.cairo_secp.secp_utils import SECP256R1, pack +from starkware.python.math_utils import y_squared_from_x + +y_square_int = y_squared_from_x( + x=pack(ids.x, SECP256R1.prime), + alpha=SECP256R1.alpha, + beta=SECP256R1.beta, + field_prime=SECP256R1.prime, +) + +# Note that (y_square_int ** ((SECP256R1.prime + 1) / 4)) ** 2 = +# = y_square_int ** ((SECP256R1.prime + 1) / 2) = +# = y_square_int ** ((SECP256R1.prime - 1) / 2 + 1) = +# = y_square_int * y_square_int ** ((SECP256R1.prime - 1) / 2) = y_square_int * {+/-}1. +y = pow(y_square_int, (SECP256R1.prime + 1) // 4, SECP256R1.prime) + +# We need to decide whether to take y or prime - y. +if ids.v % 2 == y % 2: + value = y +else: + value = (-y) % SECP256R1.prime"#; + +pub fn r1_get_point_from_x( + vm: &mut VirtualMachine, + exec_scopes: &mut ExecutionScopes, + ids_data: &HashMap, + ap_tracking: &ApTracking, + _constants: &HashMap, +) -> Result<(), HintError> { + exec_scopes.insert_value::("SECP256R1_P", SECP256R1_P.clone()); + + // def y_squared_from_x(x: int, alpha: int, beta: int, field_prime: int) -> int: + // """ + // Computes y^2 using the curve equation: + // y^2 = x^3 + alpha * x + beta (mod field_prime) + // """ + // return (pow(x, 3, field_prime) + alpha * x + beta) % field_prime + fn y_squared_from_x(x: &BigInt, alpha: &BigInt, beta: &BigInt, field_prime: &BigInt) -> BigInt { + // Compute x^3 (mod field_prime) + let x_cubed = x.modpow(&BigInt::from(3), field_prime); + + // Compute alpha * x + let alpha_x = alpha.mul(x); + + // Compute y^2 = (x^3 + alpha * x + beta) % field_prime + x_cubed.add(&alpha_x).add(beta).rem(field_prime) + } + + // prime = curve.prime + // y_squared = y_squared_from_x( + // x=x, + // alpha=curve.alpha, + // beta=curve.beta, + // field_prime=prime, + // ) + + // y = pow(y_squared, (prime + 1) // 4, prime) + // if (y & 1) != request.y_parity: + // y = (-y) % prime + + let x = Uint384::from_var_name("x", vm, ids_data, ap_tracking)? + .pack86() + .mod_floor(&SECP256R1_P); + + let y_square_int = y_squared_from_x(&x, &SECP256R1_ALPHA, &SECP256R1_B, &SECP256R1_P); + exec_scopes.insert_value::("y_square_int", y_square_int.clone()); + + // Calculate (prime + 1) // 4 + let exp = (SECP256R1_P.to_owned() + BigInt::one()).div_floor(&BigInt::from(4)); + // Calculate pow(y_square_int, exp, prime) + let y = y_square_int.modpow(&exp, &SECP256R1_P); + exec_scopes.insert_value::("y", y.clone()); + + let v = get_integer_from_var_name("v", vm, ids_data, ap_tracking)?.to_biguint(); + if v.is_even() == y.is_even() { + exec_scopes.insert_value("value", y); + } else { + let value = (-y).mod_floor(&SECP256R1_P); + exec_scopes.insert_value("value", value); + } + Ok(()) +} + +pub const IS_ON_CURVE_2: &str = r#"ids.is_on_curve = (y * y) % SECP256R1.prime == y_square_int"#; + +pub fn is_on_curve_2( + vm: &mut VirtualMachine, + exec_scopes: &mut ExecutionScopes, + ids_data: &HashMap, + ap_tracking: &ApTracking, + _constants: &HashMap, +) -> Result<(), HintError> { + let y: BigInt = exec_scopes.get("y")?; + let y_square_int: BigInt = exec_scopes.get("y_square_int")?; + + let is_on_curve = ((y.pow(2)) % SECP256R1_P.to_owned()) == y_square_int; + insert_value_from_var_name( + "is_on_curve", + Felt252::from(is_on_curve), + vm, + ids_data, + ap_tracking, + )?; + + Ok(()) +} + +pub const SECP_DOUBLE_ASSIGN_NEW_X: &str = r#"from starkware.cairo.common.cairo_secp.secp256r1_utils import SECP256R1_P +from starkware.cairo.common.cairo_secp.secp_utils import pack + +slope = pack(ids.slope, SECP256R1_P) +x = pack(ids.point.x, SECP256R1_P) +y = pack(ids.point.y, SECP256R1_P) + +value = new_x = (pow(slope, 2, SECP256R1_P) - 2 * x) % SECP256R1_P"#; + +pub fn secp_double_assign_new_x( + vm: &mut VirtualMachine, + exec_scopes: &mut ExecutionScopes, + ids_data: &HashMap, + ap_tracking: &ApTracking, + _constants: &HashMap, +) -> Result<(), HintError> { + exec_scopes.insert_value::("SECP256R1_P", SECP256R1_P.clone()); + //ids.slope + let slope = BigInt3::from_var_name("slope", vm, ids_data, ap_tracking)?; + //ids.point + let point = EcPoint::from_var_name("point", vm, ids_data, ap_tracking)?; + + let slope = slope.pack86().mod_floor(&SECP256R1_P); + let x = point.x.pack86().mod_floor(&SECP256R1_P); + let y = point.y.pack86().mod_floor(&SECP256R1_P); + + let value = + (slope.modpow(&(2usize.into()), &SECP256R1_P) - (&x << 1u32)).mod_floor(&SECP256R1_P); + + //Assign variables to vm scope + exec_scopes.insert_value("slope", slope); + exec_scopes.insert_value("x", x); + exec_scopes.insert_value("y", y); + exec_scopes.insert_value("value", value.clone()); + exec_scopes.insert_value("new_x", value); + Ok(()) +} + +pub const GENERATE_NIBBLES: &str = r#"num = (ids.scalar.high << 128) + ids.scalar.low +nibbles = [(num >> i) & 0xf for i in range(0, 256, 4)] +ids.first_nibble = nibbles.pop() +ids.last_nibble = nibbles[0]"#; +pub fn generate_nibbles( + vm: &mut VirtualMachine, + exec_scopes: &mut ExecutionScopes, + ids_data: &HashMap, + ap_tracking: &ApTracking, + _constants: &HashMap, +) -> Result<(), HintError> { + let num = Uint256::from_var_name("scalar", vm, ids_data, ap_tracking)?.pack(); + + // Generate nibbles + let mut nibbles: Vec = (0..256) + .step_by(4) + .map(|i| ((&num >> i) & BigUint::from(0xfu8))) + .map(|s: BigUint| s.into()) + .collect(); + + // ids.first_nibble = nibbles.pop() + let first_nibble = nibbles.pop().ok_or(HintError::EmptyNibbles)?; + + insert_value_from_var_name("first_nibble", first_nibble, vm, ids_data, ap_tracking)?; + + // ids.last_nibble = nibbles[0] + let last_nibble = *nibbles.first().ok_or(HintError::EmptyNibbles)?; + insert_value_from_var_name("last_nibble", last_nibble, vm, ids_data, ap_tracking)?; + exec_scopes.insert_value("nibbles", nibbles); + Ok(()) +} + +pub const FAST_SECP_ADD_ASSIGN_NEW_Y: &str = + r#"value = new_y = (slope * (x - new_x) - y) % SECP256R1_P"#; +pub fn fast_secp_add_assign_new_y( + _vm: &mut VirtualMachine, + exec_scopes: &mut ExecutionScopes, + _ids_data: &HashMap, + _ap_tracking: &ApTracking, + _constants: &HashMap, +) -> Result<(), HintError> { + //Get variables from vm scope + let (slope, x, new_x, y, secp_p) = ( + exec_scopes.get::("slope")?, + exec_scopes.get::("x")?, + exec_scopes.get::("new_x")?, + exec_scopes.get::("y")?, + SECP256R1_P.deref(), + ); + let value = (slope * (x - new_x) - y).mod_floor(secp_p); + exec_scopes.insert_value("value", value.clone()); + exec_scopes.insert_value("new_y", value); + + Ok(()) +} + +pub const WRITE_NIBBLES_TO_MEM: &str = r#"memory[fp + 0] = to_felt_or_relocatable(nibbles.pop())"#; + +pub fn write_nibbles_to_mem( + vm: &mut VirtualMachine, + exec_scopes: &mut ExecutionScopes, + _ids_data: &HashMap, + _ap_tracking: &ApTracking, + _constants: &HashMap, +) -> Result<(), HintError> { + let nibbles: &mut Vec = exec_scopes.get_mut_list_ref("nibbles")?; + let nibble = nibbles.pop().ok_or(HintError::EmptyNibbles)?; + vm.insert_value((vm.get_fp() + 0)?, nibble)?; + + Ok(()) +} + +pub const COMPUTE_VALUE_DIV_MOD: &str = r#"from starkware.python.math_utils import div_mod + +value = div_mod(1, x, SECP256R1_P)"#; +pub fn compute_value_div_mod( + _vm: &mut VirtualMachine, + exec_scopes: &mut ExecutionScopes, + _ids_data: &HashMap, + _ap_tracking: &ApTracking, + _constants: &HashMap, +) -> Result<(), HintError> { + //Get variables from vm scope + let x = exec_scopes.get_ref::("x")?; + + let value = div_mod(&BigInt::one(), x, &SECP256R1_P)?; + exec_scopes.insert_value("value", value); + + Ok(()) +} + +#[cfg(test)] +mod tests { + + use assert_matches::assert_matches; + + use crate::utils::test_utils::*; + + use super::*; + + #[cfg(target_arch = "wasm32")] + use wasm_bindgen_test::*; + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + fn test_is_on_curve_2() { + let mut vm = VirtualMachine::new(false); + vm.set_fp(1); + let ids_data = non_continuous_ids_data![("is_on_curve", -1)]; + vm.segments = segments![((1, 0), 1)]; + let ap_tracking = ApTracking::default(); + + let mut exec_scopes = ExecutionScopes::new(); + + let y = BigInt::from(1234); + let y_square_int = y.clone() * y.clone(); + + exec_scopes.insert_value("y", y); + exec_scopes.insert_value("y_square_int", y_square_int); + + is_on_curve_2( + &mut vm, + &mut exec_scopes, + &ids_data, + &ap_tracking, + &Default::default(), + ) + .expect("is_on_curve2() failed"); + + let is_on_curve: Felt252 = + get_integer_from_var_name("is_on_curve", &vm, &ids_data, &ap_tracking) + .expect("is_on_curve2 should be put in ids_data"); + assert_eq!(is_on_curve, 1.into()); + } + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + fn test_compute_q_mod_prime() { + let mut vm = VirtualMachine::new(false); + + let ap_tracking = ApTracking::default(); + + let mut exec_scopes = ExecutionScopes::new(); + + vm.run_context.fp = 9; + //Create hint data + let ids_data = non_continuous_ids_data![("val", -5), ("q", 0)]; + vm.segments = segments![((1, 4), 0), ((1, 5), 0), ((1, 6), 0)]; + compute_q_mod_prime( + &mut vm, + &mut exec_scopes, + &ids_data, + &ap_tracking, + &Default::default(), + ) + .expect("compute_q_mod_prime() failed"); + + let q: Felt252 = get_integer_from_var_name("q", &vm, &ids_data, &ap_tracking) + .expect("compute_q_mod_prime should have put 'q' in ids_data"); + assert_eq!(q, Felt252::from(0)); + } + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + fn test_compute_ids_high_low() { + let mut vm = VirtualMachine::new(false); + + let value = BigInt::from(25); + let shift = BigInt::from(12); + + vm.set_fp(14); + let ids_data = non_continuous_ids_data![ + ("UPPER_BOUND", -14), + ("value", -11), + ("high", -8), + ("low", -5), + ("SHIFT", -2) + ]; + + vm.segments = segments!( + //UPPER_BOUND + ((1, 0), 18446744069414584321), + ((1, 1), 0), + ((1, 2), 0), + //value + ((1, 3), 25), + ((1, 4), 0), + ((1, 5), 0), + //high + ((1, 6), 2), + ((1, 7), 0), + ((1, 8), 0), + //low + ((1, 9), 1), + ((1, 10), 0), + ((1, 11), 0), + //SHIFT + ((1, 12), 12), + ((1, 13), 0), + ((1, 14), 0) + ); + + let ap_tracking = ApTracking::default(); + + let mut exec_scopes = ExecutionScopes::new(); + + let constants = HashMap::from([ + ( + "UPPER_BOUND".to_string(), + Felt252::from(18446744069414584321_u128), + ), + ("SHIFT".to_string(), Felt252::from(12)), + ]); + compute_ids_high_low( + &mut vm, + &mut exec_scopes, + &ids_data, + &ap_tracking, + &constants, + ) + .expect("compute_ids_high_low() failed"); + + let high: Felt252 = get_integer_from_var_name("high", &vm, &ids_data, &ap_tracking) + .expect("compute_ids_high_low should have put 'high' in ids_data"); + let low: Felt252 = get_integer_from_var_name("low", &vm, &ids_data, &ap_tracking) + .expect("compute_ids_high_low should have put 'low' in ids_data"); + + let (expected_high, expected_low) = value.div_rem(&shift); + assert_eq!(high, Felt252::from(expected_high)); + assert_eq!(low, Felt252::from(expected_low)); + } + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + fn test_r1_get_point_from_x() { + let mut vm = VirtualMachine::new(false); + vm.set_fp(10); + + let ids_data = non_continuous_ids_data![("x", -10), ("v", -7)]; + vm.segments = segments!( + // X + ((1, 0), 18446744069414584321), + ((1, 1), 0), + ((1, 2), 0), + // v + ((1, 3), 1), + ((1, 4), 0), + ((1, 5), 0), + ); + let ap_tracking = ApTracking::default(); + + let mut exec_scopes = ExecutionScopes::new(); + + let x = BigInt::from(18446744069414584321u128); // Example x value + let v = BigInt::from(1); // Example v value (must be 0 or 1 for even/odd check) + + let constants = HashMap::new(); + + r1_get_point_from_x( + &mut vm, + &mut exec_scopes, + &ids_data, + &ap_tracking, + &constants, + ) + .expect("calculate_value() failed"); + + let value: BigInt = exec_scopes + .get("value") + .expect("value should be calculated and stored in exec_scopes"); + + // Compute y_squared_from_x(x) + let y_square_int = (x.modpow(&BigInt::from(3), &SECP256R1_P) + + SECP256R1_ALPHA.deref() * &x + + SECP256R1_B.deref()) + .mod_floor(&SECP256R1_P); + + // Calculate y = pow(y_square_int, (SECP256R1_P + 1) // 4, SECP256R1_P) + let exp = (SECP256R1_P.deref() + BigInt::one()).div_floor(&BigInt::from(4)); + let y = y_square_int.modpow(&exp, &SECP256R1_P); + + // Determine the expected value based on the parity of v and y + let expected_value = if v.is_even() == y.is_even() { + y + } else { + (-y).mod_floor(&SECP256R1_P) + }; + + assert_eq!(value, expected_value); + } + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + fn test_reduce_value() { + let mut vm = VirtualMachine::new(false); + + //Initialize fp + vm.run_context.fp = 10; + + //Create hint data + let ids_data = non_continuous_ids_data![("x", -5)]; + + vm.segments = segments![ + ( + (1, 5), + ( + "1113660525233188137217661511617697775365785011829423399545361443", + 10 + ) + ), + ( + (1, 6), + ( + "1243997169368861650657124871657865626433458458266748922940703512", + 10 + ) + ), + ( + (1, 7), + ( + "1484456708474143440067316914074363277495967516029110959982060577", + 10 + ) + ) + ]; + + let ap_tracking = ApTracking::default(); + + let mut exec_scopes = ExecutionScopes::new(); + + reduce_value( + &mut vm, + &mut exec_scopes, + &ids_data, + &ap_tracking, + &Default::default(), + ) + .expect("reduce_value() failed"); + + assert_matches!( + exec_scopes.get::("value"), + Ok(x) if x == bigint_str!( + "78544963828434122936060793808853327022047551513756524908970552805092599079793" + ) + ); + } + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + fn test_reduce_x() { + let mut vm = VirtualMachine::new(false); + + //Initialize fp + vm.run_context.fp = 10; + + //Create hint data + let ids_data = non_continuous_ids_data![("x", -5)]; + + vm.segments = segments![ + ( + (1, 5), + ( + "1113660525233188137217661511617697775365785011829423399545361443", + 10 + ) + ), + ( + (1, 6), + ( + "1243997169368861650657124871657865626433458458266748922940703512", + 10 + ) + ), + ( + (1, 7), + ( + "1484456708474143440067316914074363277495967516029110959982060577", + 10 + ) + ) + ]; + + let ap_tracking = ApTracking::default(); + + let mut exec_scopes = ExecutionScopes::new(); + + reduce_x( + &mut vm, + &mut exec_scopes, + &ids_data, + &ap_tracking, + &Default::default(), + ) + .expect("x() failed"); + + assert_matches!( + exec_scopes.get::("x"), + Ok(x) if x == bigint_str!( + "78544963828434122936060793808853327022047551513756524908970552805092599079793" + ) + ); + } +} diff --git a/vm/src/hint_processor/builtin_hint_processor/secp/ec_utils.rs b/vm/src/hint_processor/builtin_hint_processor/secp/ec_utils.rs index fdafd49e07..4eb99ac6d4 100644 --- a/vm/src/hint_processor/builtin_hint_processor/secp/ec_utils.rs +++ b/vm/src/hint_processor/builtin_hint_processor/secp/ec_utils.rs @@ -27,12 +27,12 @@ use num_traits::{One, ToPrimitive, Zero}; use super::secp_utils::SECP256R1_P; #[derive(Debug, PartialEq)] -struct EcPoint<'a> { - x: BigInt3<'a>, - y: BigInt3<'a>, +pub(crate) struct EcPoint<'a> { + pub(crate) x: BigInt3<'a>, + pub(crate) y: BigInt3<'a>, } impl EcPoint<'_> { - fn from_var_name<'a>( + pub(crate) fn from_var_name<'a>( name: &'a str, vm: &'a VirtualMachine, ids_data: &'a HashMap, diff --git a/vm/src/hint_processor/builtin_hint_processor/secp/mod.rs b/vm/src/hint_processor/builtin_hint_processor/secp/mod.rs index bb98b7868a..c47d6e5628 100644 --- a/vm/src/hint_processor/builtin_hint_processor/secp/mod.rs +++ b/vm/src/hint_processor/builtin_hint_processor/secp/mod.rs @@ -1,4 +1,6 @@ pub mod bigint_utils; +#[cfg(feature = "cairo-0-secp-hints")] +pub mod cairo0_hints; pub mod ec_utils; pub mod field_utils; pub mod secp_utils; diff --git a/vm/src/hint_processor/builtin_hint_processor/secp/secp_utils.rs b/vm/src/hint_processor/builtin_hint_processor/secp/secp_utils.rs index 4457c975b3..4d5ffaf8b3 100644 --- a/vm/src/hint_processor/builtin_hint_processor/secp/secp_utils.rs +++ b/vm/src/hint_processor/builtin_hint_processor/secp/secp_utils.rs @@ -6,7 +6,7 @@ use crate::vm::errors::hint_errors::HintError; use lazy_static::lazy_static; use num_bigint::{BigInt, BigUint}; -use num_traits::Zero; +use num_traits::{Num, Zero}; // Constants in package "starkware.cairo.common.cairo_secp.constants". pub const BASE_86: &str = "starkware.cairo.common.cairo_secp.constants.BASE"; @@ -66,6 +66,11 @@ lazy_static! { pub(crate) static ref SECP256R1_ALPHA: BigInt = BigInt::from_str( "115792089210356248762697446949407573530086143415290314195533631308867097853948" ).unwrap(); + pub(crate) static ref SECP256R1_B: BigInt = BigInt::from_str_radix( + "5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B", + 16, + ) + .unwrap(); } /* diff --git a/vm/src/hint_processor/builtin_hint_processor/secp/signature.rs b/vm/src/hint_processor/builtin_hint_processor/secp/signature.rs index 57d72b5bba..b495ab073f 100644 --- a/vm/src/hint_processor/builtin_hint_processor/secp/signature.rs +++ b/vm/src/hint_processor/builtin_hint_processor/secp/signature.rs @@ -118,8 +118,10 @@ pub fn get_point_from_x( .pack86() .mod_floor(&SECP_P); let y_cube_int = (x_cube_int + beta).mod_floor(&SECP_P); + exec_scopes.insert_value("y_square_int", y_cube_int.clone()); // Divide by 4 let mut y = y_cube_int.modpow(&(&*SECP_P + 1_u32).shr(2_u32), &SECP_P); + exec_scopes.insert_value::("y", y.clone()); let v = get_integer_from_var_name("v", vm, ids_data, ap_tracking)?.to_bigint(); if v.is_even() != y.is_even() { diff --git a/vm/src/lib.rs b/vm/src/lib.rs index 5d78058f13..90545d0f47 100644 --- a/vm/src/lib.rs +++ b/vm/src/lib.rs @@ -8,6 +8,7 @@ //! - the `skip_next_instruction()` hints; //! - implementations of [`arbitrary::Arbitrary`](https://docs.rs/arbitrary/latest/arbitrary/) for some structs. //! - `cairo-1-hints`: Enable hints that were introduced in Cairo 1. Not enabled by default. +//! - `cairo-0-secp-hints`: Enable secp hints that were introduced in Cairo 0. Not enabled by default. #![cfg_attr(docsrs, feature(doc_cfg))] #![deny(warnings)] diff --git a/vm/src/tests/cairo_run_test.rs b/vm/src/tests/cairo_run_test.rs index 55126c98df..f33ed945de 100644 --- a/vm/src/tests/cairo_run_test.rs +++ b/vm/src/tests/cairo_run_test.rs @@ -1240,3 +1240,71 @@ fn cairo_run_apply_poly_proof() { include_bytes!("../../../cairo_programs/mod_builtin_feature/proof/apply_poly.json"); run_program(program_data, true, None, None, None); } + +#[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +#[cfg(feature = "cairo-0-secp-hints")] +fn cairo_run_secp_cairo0_reduce_value() { + let program_data = include_bytes!( + "../../../cairo_programs/cairo-0-secp-hints-feature/secp_cairo0_reduce_value.json" + ); + run_program_simple(program_data.as_slice()); +} + +#[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +#[cfg(feature = "cairo-0-secp-hints")] +fn cairo_run_secp_cairo0_ec() { + let program_data = + include_bytes!("../../../cairo_programs/cairo-0-secp-hints-feature/secp_cairo0_ec.json"); + run_program_simple(program_data.as_slice()); +} + +#[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +#[cfg(feature = "cairo-0-secp-hints")] +fn cairo_run_secp_cairo0_reduce_x() { + let program_data = include_bytes!( + "../../../cairo_programs/cairo-0-secp-hints-feature/secp_cairo0_reduce_x.json" + ); + run_program_simple(program_data.as_slice()); +} + +#[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +#[cfg(feature = "cairo-0-secp-hints")] +fn cairo_run_secp_cairo0_get_point_from_x() { + let program_data = include_bytes!( + "../../../cairo_programs/cairo-0-secp-hints-feature/secp_cairo0_get_point_from_x.json" + ); + run_program_simple(program_data.as_slice()); +} + +#[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +#[cfg(feature = "cairo-0-secp-hints")] +fn cairo_run_secp_cairo0_compute_q_mod_prime() { + let program_data = include_bytes!( + "../../../cairo_programs/cairo-0-secp-hints-feature/secp_cairo0_compute_q_mod_prime.json" + ); + run_program_simple(program_data.as_slice()); +} + +#[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +#[cfg(feature = "cairo-0-secp-hints")] +fn cairo_run_secp_cairo0_ec_double_assign_new_x() { + let program_data = + include_bytes!("../../../cairo_programs/cairo-0-secp-hints-feature/secp_cairo0_ec_double_assign_new_x.json"); + run_program_simple(program_data.as_slice()); +} + +#[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +#[cfg(feature = "cairo-0-secp-hints")] +fn cairo_run_secp_cairo0_ec_mul_by_uint256() { + let program_data = include_bytes!( + "../../../cairo_programs/cairo-0-secp-hints-feature/secp_cairo0_ec_mul_by_uint256.json" + ); + run_program_simple(program_data.as_slice()); +} diff --git a/vm/src/vm/errors/hint_errors.rs b/vm/src/vm/errors/hint_errors.rs index 849d38288d..4851980837 100644 --- a/vm/src/vm/errors/hint_errors.rs +++ b/vm/src/vm/errors/hint_errors.rs @@ -192,6 +192,8 @@ pub enum HintError { ExcessBalanceKeyError(Box), #[error("excess_balance_func: Failed to calculate {0}")] ExcessBalanceCalculationFailed(Box), + #[error("generate_nibbles fail: tried to read from an empty list of nibbles")] + EmptyNibbles, #[error("circuit evalution: {0}")] CircuitEvaluationFailed(Box), }