diff --git a/CHANGELOG.md b/CHANGELOG.md index b5fb2d5d58..0b4e60ad72 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -153,6 +153,39 @@ * Optimizations for hash builtin [#1029](https://github.com/lambdaclass/cairo-rs/pull/1029): * Track the verified addresses by offset in a `Vec` rather than storing the address in a `Vec` +* Add missing hint on vrf.json lib [#1035](https://github.com/lambdaclass/cairo-rs/pull/1035): + + `BuiltinHintProcessor` now supports the following hint: + + ```python + %{ + from starkware.python.math_utils import line_slope + from starkware.cairo.common.cairo_secp.secp_utils import pack + SECP_P = 2**255-19 + # Compute the slope. + x0 = pack(ids.point0.x, PRIME) + y0 = pack(ids.point0.y, PRIME) + 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) + %} + ``` + +* Add missing hint on vrf.json lib [#1035](https://github.com/lambdaclass/cairo-rs/pull/1035): + + `BuiltinHintProcessor` now supports the following hint: + + ```python + %{ + 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 + %} + ``` + * Add missing hint on vrf.json lib [#1000](https://github.com/lambdaclass/cairo-rs/pull/1000): `BuiltinHintProcessor` now supports the following hint: diff --git a/cairo_programs/compute_slope_v2.cairo b/cairo_programs/compute_slope_v2.cairo new file mode 100644 index 0000000000..a853e236d9 --- /dev/null +++ b/cairo_programs/compute_slope_v2.cairo @@ -0,0 +1,112 @@ +%builtins range_check + +from starkware.cairo.common.cairo_secp.bigint import ( + BigInt3, + UnreducedBigInt3, + nondet_bigint3, + bigint_to_uint256, + uint256_to_bigint, +) + +struct EcPoint { + x: BigInt3, + y: BigInt3, +} + +const BASE = 2 ** 86; +const SECP_REM = 19; + +func compute_slope{range_check_ptr}(point0: EcPoint, point1: EcPoint) -> (slope: BigInt3) { + alloc_locals; + %{ + from starkware.python.math_utils import line_slope + from starkware.cairo.common.cairo_secp.secp_utils import pack + SECP_P = 2**255-19 + # Compute the slope. + x0 = pack(ids.point0.x, PRIME) + y0 = pack(ids.point0.y, PRIME) + 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) + %} + let (slope) = nondet_bigint3(); + + let x_diff = BigInt3( + d0=point0.x.d0 - point1.x.d0, d1=point0.x.d1 - point1.x.d1, d2=point0.x.d2 - point1.x.d2 + ); + let (x_diff_slope: UnreducedBigInt3) = unreduced_mul(x_diff, slope); + + verify_zero( + UnreducedBigInt3( + d0=x_diff_slope.d0 - point0.y.d0 + point1.y.d0, + d1=x_diff_slope.d1 - point0.y.d1 + point1.y.d1, + d2=x_diff_slope.d2 - point0.y.d2 + point1.y.d2), + ); + + return (slope=slope); +} + +func unreduced_mul(a: BigInt3, b: BigInt3) -> (res_low: UnreducedBigInt3) { + // The result of the product is: + // sum_{i, j} a.d_i * b.d_j * BASE**(i + j) + // Since we are computing it mod secp256k1_prime, we replace the term + // a.d_i * b.d_j * BASE**(i + j) + // where i + j >= 3 with + // a.d_i * b.d_j * BASE**(i + j - 3) * 4 * SECP_REM + // since BASE ** 3 = 4 * SECP_REM (mod secp256k1_prime). + return ( + UnreducedBigInt3( + d0=a.d0 * b.d0 + (a.d1 * b.d2 + a.d2 * b.d1) * (8 * SECP_REM), + d1=a.d0 * b.d1 + a.d1 * b.d0 + (a.d2 * b.d2) * (8 * SECP_REM), + d2=a.d0 * b.d2 + a.d1 * b.d1 + a.d2 * b.d0), + ); +} + +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 (); +} + +func main{range_check_ptr}() { + let point_1 = EcPoint(BigInt3(512,2412,133), BigInt3(64,0,6546)); + let point_2 = EcPoint(BigInt3(7,8,123), BigInt3(1,7,465)); + + let (slope) = compute_slope(point_1, point_2); + assert slope = BigInt3(32565103718045841981942279,60662980405630750722698303,6577829329490861459174478); + return (); +} diff --git a/src/hint_processor/builtin_hint_processor/builtin_hint_processor_definition.rs b/src/hint_processor/builtin_hint_processor/builtin_hint_processor_definition.rs index 47fb656e90..ae7cf70e65 100644 --- a/src/hint_processor/builtin_hint_processor/builtin_hint_processor_definition.rs +++ b/src/hint_processor/builtin_hint_processor/builtin_hint_processor_definition.rs @@ -4,6 +4,7 @@ use super::{ ec_recover_sub_a_b, }, field_arithmetic::uint384_div, + secp::secp_utils::{SECP_P, SECP_P_V2}, vrf::{fq::uint512_unsigned_div_rem, inv_mod_p_uint512::inv_mod_p_uint512}, }; use crate::{ @@ -41,7 +42,7 @@ use crate::{ secp::{ bigint_utils::{bigint_to_uint256, hi_max_bitlen, nondet_bigint3}, ec_utils::{ - compute_doubling_slope, compute_slope, compute_slope_secp_p, di_bit, + 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, fast_ec_add_assign_new_x, fast_ec_add_assign_new_y, import_secp256r1_p, quad_bit, @@ -266,9 +267,20 @@ impl HintProcessor for BuiltinHintProcessor { hint_code::BLAKE2S_COMPUTE => { compute_blake2s(vm, &hint_data.ids_data, &hint_data.ap_tracking) } - hint_code::VERIFY_ZERO_V1 | hint_code::VERIFY_ZERO_V2 => { - verify_zero(vm, exec_scopes, &hint_data.ids_data, &hint_data.ap_tracking) - } + hint_code::VERIFY_ZERO_V1 | hint_code::VERIFY_ZERO_V2 => verify_zero( + vm, + exec_scopes, + &hint_data.ids_data, + &hint_data.ap_tracking, + &SECP_P, + ), + hint_code::VERIFY_ZERO_V3 => verify_zero( + vm, + exec_scopes, + &hint_data.ids_data, + &hint_data.ap_tracking, + &SECP_P_V2, + ), hint_code::VERIFY_ZERO_EXTERNAL_SECP => verify_zero_with_external_const( vm, exec_scopes, @@ -433,13 +445,23 @@ impl HintProcessor for BuiltinHintProcessor { &hint_data.ap_tracking, "pt", ), - hint_code::COMPUTE_SLOPE => compute_slope_secp_p( + hint_code::COMPUTE_SLOPE_V1 => compute_slope_and_assing_secp_p( + vm, + exec_scopes, + &hint_data.ids_data, + &hint_data.ap_tracking, + "point0", + "point1", + &SECP_P, + ), + hint_code::COMPUTE_SLOPE_V2 => compute_slope_and_assing_secp_p( vm, exec_scopes, &hint_data.ids_data, &hint_data.ap_tracking, "point0", "point1", + &SECP_P_V2, ), hint_code::COMPUTE_SLOPE_SECP256R1 => compute_slope( vm, @@ -450,13 +472,14 @@ impl HintProcessor for BuiltinHintProcessor { "point1", ), hint_code::IMPORT_SECP256R1_P => import_secp256r1_p(exec_scopes), - hint_code::COMPUTE_SLOPE_WHITELIST => compute_slope_secp_p( + hint_code::COMPUTE_SLOPE_WHITELIST => compute_slope_and_assing_secp_p( vm, exec_scopes, &hint_data.ids_data, &hint_data.ap_tracking, "pt0", "pt1", + &SECP_P, ), hint_code::EC_DOUBLE_ASSIGN_NEW_X_V1 | hint_code::EC_DOUBLE_ASSIGN_NEW_X_V2 => { ec_double_assign_new_x(vm, exec_scopes, &hint_data.ids_data, &hint_data.ap_tracking) diff --git a/src/hint_processor/builtin_hint_processor/hint_code.rs b/src/hint_processor/builtin_hint_processor/hint_code.rs index 769b374a76..38e6104d7e 100644 --- a/src/hint_processor/builtin_hint_processor/hint_code.rs +++ b/src/hint_processor/builtin_hint_processor/hint_code.rs @@ -446,6 +446,13 @@ 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 const VERIFY_ZERO_V3: &str = r#"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"#; + pub const VERIFY_ZERO_EXTERNAL_SECP: &str = r#"from starkware.cairo.common.cairo_secp.secp_utils import pack q, r = divmod(pack(ids.val, PRIME), SECP_P) @@ -574,7 +581,7 @@ x = pack(ids.pt.x, PRIME) y = pack(ids.pt.y, PRIME) value = slope = div_mod(3 * x ** 2, 2 * y, SECP_P)"#; -pub const COMPUTE_SLOPE: &str = r#"from starkware.cairo.common.cairo_secp.secp_utils import SECP_P, pack +pub const COMPUTE_SLOPE_V1: &str = r#"from starkware.cairo.common.cairo_secp.secp_utils import SECP_P, pack from starkware.python.math_utils import line_slope # Compute the slope. @@ -584,6 +591,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 const COMPUTE_SLOPE_V2: &str = r#"from starkware.python.math_utils import line_slope +from starkware.cairo.common.cairo_secp.secp_utils import pack +SECP_P = 2**255-19 +# Compute the slope. +x0 = pack(ids.point0.x, PRIME) +y0 = pack(ids.point0.y, PRIME) +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 const COMPUTE_SLOPE_SECP256R1: &str = r#"from starkware.cairo.common.cairo_secp.secp_utils import pack from starkware.python.math_utils import line_slope diff --git a/src/hint_processor/builtin_hint_processor/secp/ec_utils.rs b/src/hint_processor/builtin_hint_processor/secp/ec_utils.rs index e18872d65f..6f363dda22 100644 --- a/src/hint_processor/builtin_hint_processor/secp/ec_utils.rs +++ b/src/hint_processor/builtin_hint_processor/secp/ec_utils.rs @@ -116,15 +116,16 @@ Implements hint: value = slope = line_slope(point1=(x0, y0), point2=(x1, y1), p=SECP_P) %} */ -pub fn compute_slope_secp_p( +pub fn compute_slope_and_assing_secp_p( vm: &mut VirtualMachine, exec_scopes: &mut ExecutionScopes, ids_data: &HashMap, ap_tracking: &ApTracking, point0_alias: &str, point1_alias: &str, + secp_p: &BigInt, ) -> Result<(), HintError> { - exec_scopes.insert_value("SECP_P", SECP_P.clone()); + exec_scopes.insert_value("SECP_P", secp_p.clone()); compute_slope( vm, exec_scopes, @@ -556,6 +557,61 @@ mod tests { ); } + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + fn run_compute_slope_v2_ok() { + let mut vm = vm_with_range_check!(); + + //Insert ids.point0 and ids.point1 into memory + vm.segments = segments![ + ((1, 0), 512), + ((1, 1), 2412), + ((1, 2), 133), + ((1, 3), 64), + ((1, 4), 0), + ((1, 5), 6546), + ((1, 6), 7), + ((1, 7), 8), + ((1, 8), 123), + ((1, 9), 1), + ((1, 10), 7), + ((1, 11), 465) + ]; + // let point_1 = EcPoint(BigInt3(512,2412,133), BigInt3(64,0,6546)); + // let point_2 = EcPoint(BigInt3(7,8,123), BigInt3(1,7,465)); + + //Initialize fp + vm.run_context.fp = 14; + let ids_data = HashMap::from([ + ("point0".to_string(), HintReference::new_simple(-14)), + ("point1".to_string(), HintReference::new_simple(-8)), + ]); + let mut exec_scopes = ExecutionScopes::new(); + + //Execute the hint + assert_matches!( + run_hint!(vm, ids_data, hint_code::COMPUTE_SLOPE_V2, &mut exec_scopes), + Ok(()) + ); + check_scope!( + &exec_scopes, + [ + ( + "value", + bigint_str!( + "39376930140709393693483102164172662915882483986415749881375763965703119677959" + ) + ), + ( + "slope", + bigint_str!( + "39376930140709393693483102164172662915882483986415749881375763965703119677959" + ) + ) + ] + ); + } + #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn run_compute_slope_wdivmod_ok() { diff --git a/src/hint_processor/builtin_hint_processor/secp/field_utils.rs b/src/hint_processor/builtin_hint_processor/secp/field_utils.rs index 1009b96ab3..f7cc4488ca 100644 --- a/src/hint_processor/builtin_hint_processor/secp/field_utils.rs +++ b/src/hint_processor/builtin_hint_processor/secp/field_utils.rs @@ -35,10 +35,11 @@ pub fn verify_zero( exec_scopes: &mut ExecutionScopes, ids_data: &HashMap, ap_tracking: &ApTracking, + secp_p: &BigInt, ) -> Result<(), HintError> { - exec_scopes.insert_value("SECP_P", SECP_P.clone()); + exec_scopes.insert_value("SECP_P", secp_p.clone()); let val = bigint3_pack(Uint384::from_var_name("val", vm, ids_data, ap_tracking)?); - let (q, r) = val.div_rem(&SECP_P); + let (q, r) = val.div_rem(secp_p); if !r.is_zero() { return Err(HintError::SecpVerifyZero(val)); } @@ -220,6 +221,29 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn run_verify_zero_ok() { + let hint_codes = vec![ + &hint_code::VERIFY_ZERO_V1, + &hint_code::VERIFY_ZERO_V2, + &hint_code::VERIFY_ZERO_V3, + ]; + for hint_code in hint_codes { + let mut vm = vm_with_range_check!(); + //Initialize run_context + run_context!(vm, 0, 9, 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)]; + //Execute the hint + assert!(run_hint!(vm, ids_data, hint_code, exec_scopes_ref!()).is_ok()); + //Check hint memory inserts + //ids.q + check_memory![vm.segments.memory, ((1, 9), 0)]; + } + } + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + fn run_verify_zero_v3_ok() { let hint_codes = vec![ "from starkware.cairo.common.cairo_secp.secp_utils import SECP_P, pack\n\nq, r = divmod(pack(ids.val, PRIME), SECP_P)\nassert r == 0, f\"verify_zero: Invalid input {ids.val.d0, ids.val.d1, ids.val.d2}.\"\nids.q = q % PRIME", "from starkware.cairo.common.cairo_secp.secp_utils import SECP_P\nq, r = divmod(pack(ids.val, PRIME), SECP_P)\nassert r == 0, f\"verify_zero: Invalid input {ids.val.d0, ids.val.d1, ids.val.d2}.\"\nids.q = q % PRIME", diff --git a/src/hint_processor/builtin_hint_processor/secp/secp_utils.rs b/src/hint_processor/builtin_hint_processor/secp/secp_utils.rs index 7dfbd55d5f..0deae656fd 100644 --- a/src/hint_processor/builtin_hint_processor/secp/secp_utils.rs +++ b/src/hint_processor/builtin_hint_processor/secp/secp_utils.rs @@ -27,6 +27,12 @@ lazy_static! { "115792089237316195423570985008687907853269984665640564039457584007908834671663" ) .unwrap(); + + //SECP_P_V2 = 2**255-19 + pub(crate) static ref SECP_P_V2: BigInt = BigInt::from_str( + "57896044618658097711785492504343953926634992332820282019728792003956564819949" + ) + .unwrap(); // BASE = 2**86 pub(crate) static ref BASE: BigUint = BigUint::from_str( "77371252455336267181195264" diff --git a/src/tests/cairo_run_test.rs b/src/tests/cairo_run_test.rs index d5d38aa611..10dc2ebcfc 100644 --- a/src/tests/cairo_run_test.rs +++ b/src/tests/cairo_run_test.rs @@ -841,3 +841,10 @@ fn cairo_run_fq_test() { let program_data = include_bytes!("../../cairo_programs/fq_test.json"); run_program_simple_with_memory_holes(program_data.as_slice(), 42); } + +#[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +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()); +}