diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 1f342fca05..3752d4df51 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -642,6 +642,37 @@ jobs: key: codecov-cache-test-no_std-mod_builtin-${{ github.sha }} fail-on-cache-miss: true + - name: Fetch results for tests with stdlib (w/cairo-0-secp-hints; part. 1) + uses: actions/cache/restore@v3 + with: + path: lcov-test#1-cairo-0-secp-hints.info + key: codecov-cache-test#1-cairo-0-secp-hints-${{ github.sha }} + fail-on-cache-miss: true + - name: Fetch results for tests with stdlib (w/cairo-0-secp-hints; part. 2) + uses: actions/cache/restore@v3 + with: + path: lcov-test#2-cairo-0-secp-hints.info + key: codecov-cache-test#2-cairo-0-secp-hints-${{ github.sha }} + fail-on-cache-miss: true + - name: Fetch results for tests with stdlib (w/cairo-0-secp-hints; part. 3) + uses: actions/cache/restore@v3 + with: + path: lcov-test#3-cairo-0-secp-hints.info + key: codecov-cache-test#3-cairo-0-secp-hints-${{ github.sha }} + fail-on-cache-miss: true + - name: Fetch results for tests with stdlib (w/cairo-0-secp-hints; part. 4) + uses: actions/cache/restore@v3 + with: + path: lcov-test#4-cairo-0-secp-hints.info + key: codecov-cache-test#4-cairo-0-secp-hints-${{ github.sha }} + fail-on-cache-miss: true + - name: Fetch results for tests without stdlib (w/cairo-0-secp-hints) + uses: actions/cache/restore@v3 + with: + path: lcov-no_std-cairo-0-secp-hints.info + key: codecov-cache-test-no_std-cairo-0-secp-hints-${{ github.sha }} + fail-on-cache-miss: true + - name: Upload coverage to codecov.io uses: codecov/codecov-action@v3 with: diff --git a/cairo_programs/cairo-0-secp-hints-feature/negative_points.cairo b/cairo_programs/cairo-0-secp-hints-feature/negative_points.cairo new file mode 100644 index 0000000000..3fdaae6245 --- /dev/null +++ b/cairo_programs/cairo-0-secp-hints-feature/negative_points.cairo @@ -0,0 +1,167 @@ +%builtins range_check + +from starkware.cairo.common.secp256r1.ec import ( + EcPoint, +) +from starkware.cairo.common.secp256r1.bigint import nondet_bigint3 +from starkware.cairo.common.cairo_secp.bigint3 import BigInt3 + +func main{range_check_ptr: felt}() { + let point = EcPoint( + BigInt3(1, 2, 3), + BigInt3(-1, -2, -3), + ); + + let slope = compute_doubling_slope_prime(point); + assert slope = BigInt3( + 15487438216801236710343013, 27596288489803578791625491, 8178446608657045587339469 + ); + + let slope = compute_doubling_slope_secp256r1(point); + assert slope = BigInt3( + 56511396263956479754791421, 38561311687768998103117219, 2015104701319196654781984 + ); + + let x = BigInt3(-1,-2,-3); + let y = try_get_point_from_x_prime(x, 0); + assert y = BigInt3( + 39197606747300743094893670, 38008389934708701866119639, 2071781356858789560884686 + ); + + let x = BigInt3(-1,-2,-3); + let y = try_get_point_from_x_secp256r1(x, 0); + assert y = BigInt3( + 56004882917990234964232380, 17943756516348761157632108, 3811440313376405071875160 + ); + + let slope = BigInt3(-1,-2,-3); + let x = ec_double_x_prime(point, slope); + assert x = BigInt3( + 648518346341351470, 77370588372549613637288996, 18662792551970020321619971 + ); + + let slope = BigInt3(-1,-2,-3); + let x = ec_double_x_secp256r1(point, slope); + assert x = BigInt3( + 21299552074028835321108137, 50187220174510023990904347, 2291813387120727975022296 + ); + + return (); +} + +func compute_doubling_slope_prime{range_check_ptr}(point: EcPoint) -> BigInt3 { + %{ + from starkware.cairo.common.cairo_secp.secp256r1_utils import SECP256R1_ALPHA, SECP256R1_P + from starkware.cairo.common.cairo_secp.secp_utils import pack + from starkware.python.math_utils import ec_double_slope + + # Compute the slope. + x = pack(ids.point.x, PRIME) + y = pack(ids.point.y, PRIME) + value = slope = ec_double_slope(point=(x, y), alpha=SECP256R1_ALPHA, p=SECP256R1_P) + %} + let (slope: BigInt3) = nondet_bigint3(); + return slope; +} + +func compute_doubling_slope_secp256r1{range_check_ptr}(point: EcPoint) -> BigInt3 { + %{ + from starkware.cairo.common.cairo_secp.secp256r1_utils import SECP256R1_ALPHA, SECP256R1_P + from starkware.cairo.common.cairo_secp.secp_utils import pack + from starkware.python.math_utils import ec_double_slope + + # Compute the slope. + x = pack(ids.point.x, SECP256R1_P) + y = pack(ids.point.y, SECP256R1_P) + value = slope = ec_double_slope(point=(x, y), alpha=SECP256R1_ALPHA, p=SECP256R1_P) + %} + let (slope: BigInt3) = nondet_bigint3(); + return slope; +} + +func try_get_point_from_x_prime{range_check_ptr}(x: BigInt3, v: felt) -> BigInt3 { + %{ + 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, 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 + %} + let (y: BigInt3) = nondet_bigint3(); + return y; +} + +func try_get_point_from_x_secp256r1{range_check_ptr}(x: BigInt3, v: felt) -> BigInt3 { + %{ + 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 + %} + let (y: BigInt3) = nondet_bigint3(); + return y; +} + + +func ec_double_x_prime{range_check_ptr}(point: EcPoint, slope: BigInt3) -> BigInt3 { + %{ + 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, PRIME) + x = pack(ids.point.x, PRIME) + y = pack(ids.point.y, PRIME) + + value = new_x = (pow(slope, 2, SECP256R1_P) - 2 * x) % SECP256R1_P + %} + let (new_x: BigInt3) = nondet_bigint3(); + return new_x; +} + +func ec_double_x_secp256r1{range_check_ptr}(point: EcPoint, slope: BigInt3) -> BigInt3 { + %{ + 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 + %} + let (new_x: BigInt3) = nondet_bigint3(); + return new_x; +} diff --git a/requirements.txt b/requirements.txt index 6480d4d6e9..86b2eba9db 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,4 +3,4 @@ bitarray==2.7.3 fastecdsa==2.3.2 sympy==1.11.1 typeguard==2.13.3 -cairo-lang==0.13.3 +cairo-lang==0.13.5 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 020014615e..4ba74eccb4 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,5 +1,4 @@ -#[cfg(feature = "cairo-0-secp-hints")] -use super::secp::cairo0_hints; +use super::blake2s_utils::example_blake2s_compress; use super::{ blake2s_utils::{blake2s_unpack_felts, finalize_blake2s_v3, is_less_than_63_bits_and_not_end}, ec_recover::{ @@ -25,6 +24,10 @@ use super::{ }, }; use crate::Felt252; +use crate::{ + hint_processor::builtin_hint_processor::secp::secp_utils::{SECP256R1_ALPHA, SECP256R1_P}, + utils::CAIRO_PRIME, +}; use crate::{ hint_processor::{ builtin_hint_processor::secp::ec_utils::{ @@ -117,17 +120,14 @@ use crate::{ vm::{errors::hint_errors::HintError, vm_core::VirtualMachine}, }; +#[cfg(feature = "cairo-0-secp-hints")] +use crate::hint_processor::builtin_hint_processor::secp::cairo0_hints; #[cfg(feature = "test_utils")] -use crate::hint_processor::builtin_hint_processor::skip_next_instruction::skip_next_instruction; - -#[cfg(feature = "test_utils")] -use crate::hint_processor::builtin_hint_processor::print::{print_array, print_dict, print_felt}; -use crate::hint_processor::builtin_hint_processor::secp::secp_utils::{ - SECP256R1_ALPHA, SECP256R1_P, +use crate::hint_processor::builtin_hint_processor::{ + print::{print_array, print_dict, print_felt}, + skip_next_instruction::skip_next_instruction, }; -use super::blake2s_utils::example_blake2s_compress; - pub struct HintProcessorData { pub code: String, pub ap_tracking: ApTracking, @@ -523,6 +523,7 @@ impl HintProcessorLogic for BuiltinHintProcessor { &hint_data.ids_data, &hint_data.ap_tracking, "point", + &CAIRO_PRIME, &SECP_P, &ALPHA, ), @@ -532,6 +533,7 @@ impl HintProcessorLogic for BuiltinHintProcessor { &hint_data.ids_data, &hint_data.ap_tracking, "point", + &CAIRO_PRIME, &SECP_P_V2, &ALPHA_V2, ), @@ -541,6 +543,7 @@ impl HintProcessorLogic for BuiltinHintProcessor { &hint_data.ids_data, &hint_data.ap_tracking, "pt", + &CAIRO_PRIME, &SECP_P, &ALPHA, ), @@ -550,6 +553,17 @@ impl HintProcessorLogic for BuiltinHintProcessor { &hint_data.ids_data, &hint_data.ap_tracking, "point", + SECP256R1_P.magnitude(), + &SECP256R1_P, + &SECP256R1_ALPHA, + ), + hint_code::EC_DOUBLE_SLOPE_V5 => compute_doubling_slope( + vm, + exec_scopes, + &hint_data.ids_data, + &hint_data.ap_tracking, + "point", + &CAIRO_PRIME, &SECP256R1_P, &SECP256R1_ALPHA, ), @@ -905,6 +919,16 @@ impl HintProcessorLogic for BuiltinHintProcessor { &hint_data.ids_data, &hint_data.ap_tracking, constants, + SECP256R1_P.magnitude(), + ), + #[cfg(feature = "cairo-0-secp-hints")] + cairo0_hints::SECP_DOUBLE_ASSIGN_NEW_X_V2 => cairo0_hints::secp_double_assign_new_x( + vm, + exec_scopes, + &hint_data.ids_data, + &hint_data.ap_tracking, + constants, + &CAIRO_PRIME, ), #[cfg(feature = "cairo-0-secp-hints")] cairo0_hints::FAST_SECP_ADD_ASSIGN_NEW_Y => cairo0_hints::fast_secp_add_assign_new_y( @@ -954,6 +978,17 @@ impl HintProcessorLogic for BuiltinHintProcessor { &hint_data.ids_data, &hint_data.ap_tracking, constants, + SECP256R1_P.magnitude(), + ), + + #[cfg(feature = "cairo-0-secp-hints")] + cairo0_hints::SECP_R1_GET_POINT_FROM_X_V2 => cairo0_hints::r1_get_point_from_x( + vm, + exec_scopes, + &hint_data.ids_data, + &hint_data.ap_tracking, + constants, + &CAIRO_PRIME, ), #[cfg(feature = "cairo-0-secp-hints")] diff --git a/vm/src/hint_processor/builtin_hint_processor/hint_code.rs b/vm/src/hint_processor/builtin_hint_processor/hint_code.rs index 0985d9b410..5d0919e0c8 100644 --- a/vm/src/hint_processor/builtin_hint_processor/hint_code.rs +++ b/vm/src/hint_processor/builtin_hint_processor/hint_code.rs @@ -620,6 +620,14 @@ from starkware.python.math_utils import ec_double_slope x = pack(ids.point.x, SECP256R1_P) y = pack(ids.point.y, SECP256R1_P) value = slope = ec_double_slope(point=(x, y), alpha=SECP256R1_ALPHA, p=SECP256R1_P)"#}), +(EC_DOUBLE_SLOPE_V5, indoc! {r#"from starkware.cairo.common.cairo_secp.secp256r1_utils import SECP256R1_ALPHA, SECP256R1_P +from starkware.cairo.common.cairo_secp.secp_utils import pack +from starkware.python.math_utils import ec_double_slope + +# Compute the slope. +x = pack(ids.point.x, PRIME) +y = pack(ids.point.y, PRIME) +value = slope = ec_double_slope(point=(x, y), alpha=SECP256R1_ALPHA, p=SECP256R1_P)"#}), (EC_DOUBLE_SLOPE_EXTERNAL_CONSTS, indoc! {r#"from starkware.cairo.common.cairo_secp.secp_utils import pack from starkware.python.math_utils import ec_double_slope diff --git a/vm/src/hint_processor/builtin_hint_processor/secp/bigint_utils.rs b/vm/src/hint_processor/builtin_hint_processor/secp/bigint_utils.rs index 5b32f4e691..d355ac67c0 100644 --- a/vm/src/hint_processor/builtin_hint_processor/secp/bigint_utils.rs +++ b/vm/src/hint_processor/builtin_hint_processor/secp/bigint_utils.rs @@ -95,6 +95,17 @@ impl BigIntN<'_, NUM_LIMBS> { .sum() } + pub(crate) fn pack86_for_prime(self, prime: &BigUint) -> BigInt { + self.limbs + .into_iter() + .take(3) + .enumerate() + .map(|(idx, value)| { + crate::math_utils::signed_felt_for_prime(*value, prime).shl(idx * 86) + }) + .sum() + } + pub(crate) fn split(num: &BigUint) -> Self { let limbs = split(num, 128); Self::from_values(limbs) 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 index f3ffeac78f..78ffd3b42c 100644 --- a/vm/src/hint_processor/builtin_hint_processor/secp/cairo0_hints.rs +++ b/vm/src/hint_processor/builtin_hint_processor/secp/cairo0_hints.rs @@ -67,6 +67,27 @@ y_square_int = y_squared_from_x( # = 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"#}), +(SECP_R1_GET_POINT_FROM_X_V2, indoc! {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, 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 @@ -80,6 +101,14 @@ 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"#}), +(SECP_DOUBLE_ASSIGN_NEW_X_V2, indoc! {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, PRIME) +x = pack(ids.point.x, PRIME) +y = pack(ids.point.y, PRIME) + value = new_x = (pow(slope, 2, SECP256R1_P) - 2 * x) % SECP256R1_P"#}), (GENERATE_NIBBLES, indoc! {r#"num = (ids.scalar.high << 128) + ids.scalar.low nibbles = [(num >> i) & 0xf for i in range(0, 256, 4)] @@ -172,6 +201,7 @@ pub fn r1_get_point_from_x( ids_data: &HashMap, ap_tracking: &ApTracking, _constants: &HashMap, + pack_prime: &BigUint, ) -> Result<(), HintError> { exec_scopes.insert_value::("SECP256R1_P", SECP256R1_P.clone()); @@ -204,9 +234,8 @@ pub fn r1_get_point_from_x( // 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 x = Uint384::from_var_name("x", vm, ids_data, ap_tracking)?; + let x = x.pack86_for_prime(pack_prime); 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()); @@ -255,6 +284,7 @@ pub fn secp_double_assign_new_x( ids_data: &HashMap, ap_tracking: &ApTracking, _constants: &HashMap, + pack_prime: &BigUint, ) -> Result<(), HintError> { exec_scopes.insert_value::("SECP256R1_P", SECP256R1_P.clone()); //ids.slope @@ -262,9 +292,9 @@ pub fn secp_double_assign_new_x( //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 slope = slope.pack86_for_prime(pack_prime); + let x = point.x.pack86_for_prime(pack_prime); + let y = point.y.pack86_for_prime(pack_prime); let value = (slope.modpow(&(2usize.into()), &SECP256R1_P) - (&x << 1u32)).mod_floor(&SECP256R1_P); @@ -531,6 +561,7 @@ mod tests { &ids_data, &ap_tracking, &constants, + SECP256R1_P.magnitude(), ) .expect("calculate_value() failed"); 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 4eb99ac6d4..548d6bd8f3 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 @@ -119,12 +119,14 @@ Implements hint: value = slope = ec_double_slope(point=(x, y), alpha=0, p=SECP_P) %} */ +#[allow(clippy::too_many_arguments)] pub fn compute_doubling_slope( vm: &mut VirtualMachine, exec_scopes: &mut ExecutionScopes, ids_data: &HashMap, ap_tracking: &ApTracking, point_alias: &str, + pack_prime: &BigUint, secp_p: &BigInt, alpha: &BigInt, ) -> Result<(), HintError> { @@ -132,7 +134,14 @@ pub fn compute_doubling_slope( //ids.point let point = EcPoint::from_var_name(point_alias, vm, ids_data, ap_tracking)?; - let value = ec_double_slope(&(point.x.pack86(), point.y.pack86()), alpha, secp_p)?; + let value = ec_double_slope( + &( + point.x.pack86_for_prime(pack_prime), + point.y.pack86_for_prime(pack_prime), + ), + alpha, + secp_p, + )?; exec_scopes.insert_value("value", value.clone()); exec_scopes.insert_value("slope", value); Ok(()) diff --git a/vm/src/math_utils/mod.rs b/vm/src/math_utils/mod.rs index 75ad9888e9..fd1f2759e8 100644 --- a/vm/src/math_utils/mod.rs +++ b/vm/src/math_utils/mod.rs @@ -70,6 +70,16 @@ pub fn signed_felt(felt: Felt252) -> BigInt { } } +pub fn signed_felt_for_prime(value: Felt252, prime: &BigUint) -> BigInt { + let value = value.to_biguint(); + let half_prime = prime / 2u32; + if value > half_prime { + BigInt::from_biguint(num_bigint::Sign::Minus, prime - &value) + } else { + BigInt::from_biguint(num_bigint::Sign::Plus, value) + } +} + /// QM31 utility function, used specifically for Stwo. /// QM31 operations are to be relocated into https://github.com/lambdaclass/lambdaworks. /// Reads four u64 coordinates from a single Felt252. diff --git a/vm/src/tests/cairo_run_test.rs b/vm/src/tests/cairo_run_test.rs index 6ef4faa976..b33a08535f 100644 --- a/vm/src/tests/cairo_run_test.rs +++ b/vm/src/tests/cairo_run_test.rs @@ -1345,6 +1345,15 @@ fn cairo_run_secp_cairo0_ec_mul_by_uint256() { 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_negative_points() { + let program_data = + include_bytes!("../../../cairo_programs/cairo-0-secp-hints-feature/negative_points.json"); + run_program_simple(program_data.as_slice()); +} + #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] #[cfg(feature = "cairo-0-data-availability-hints")]