From 61324829fce901d5d4753ccd80b6d85d33315d5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s?= Date: Thu, 13 Apr 2023 17:08:41 -0300 Subject: [PATCH 1/6] Add `COMPUTE_SLOPE_WHITELIST` hint --- cairo_programs/ed25519_ec.cairo | 75 +++++++++++++++++++ .../builtin_hint_processor_definition.rs | 19 ++++- .../builtin_hint_processor/hint_code.rs | 10 +++ .../builtin_hint_processor/secp/ec_utils.rs | 57 +++++++++++++- src/tests/cairo_run_test.rs | 7 ++ 5 files changed, 163 insertions(+), 5 deletions(-) create mode 100644 cairo_programs/ed25519_ec.cairo diff --git a/cairo_programs/ed25519_ec.cairo b/cairo_programs/ed25519_ec.cairo new file mode 100644 index 0000000000..4f6e6b5527 --- /dev/null +++ b/cairo_programs/ed25519_ec.cairo @@ -0,0 +1,75 @@ +%builtins range_check + +// Source: https://github.com/NilFoundation/cairo-ed25519/blob/fee64a1a60b2e07b3b5c20df57f31d7ffcb29ac9/ed25519_ec.cairo + +from starkware.cairo.common.serialize import serialize_word +from starkware.cairo.common.cairo_secp.bigint import BigInt3, UnreducedBigInt3, nondet_bigint3 +from starkware.cairo.common.cairo_secp.field import ( + is_zero, + unreduced_mul, + unreduced_sqr, + verify_zero, +) + +// Represents a point on the elliptic curve. +// The zero point is represented using pt.x=0, as there is no point on the curve with this x value. +struct EcPoint { + x: BigInt3, + y: BigInt3, +} + +// Returns the slope of the line connecting the two given points. +// The slope is used to compute pt0 + pt1. +// Assumption: pt0.x != pt1.x (mod secp256k1_prime). +func compute_slope{range_check_ptr: felt}(pt0: EcPoint, pt1: EcPoint) -> (slope: BigInt3) { + %{ + from starkware.cairo.common.cairo_secp.secp_utils import SECP_P, pack + from starkware.python.math_utils import div_mod + + # Compute the slope. + x0 = pack(ids.pt0.x, PRIME) + y0 = pack(ids.pt0.y, PRIME) + x1 = pack(ids.pt1.x, PRIME) + y1 = pack(ids.pt1.y, PRIME) + value = slope = div_mod(y0 - y1, x0 - x1, SECP_P) + %} + let (slope) = nondet_bigint3(); + + let x_diff = BigInt3(d0=pt0.x.d0 - pt1.x.d0, d1=pt0.x.d1 - pt1.x.d1, d2=pt0.x.d2 - pt1.x.d2); + let (x_diff_slope: UnreducedBigInt3) = unreduced_mul(x_diff, slope); + + verify_zero( + UnreducedBigInt3( + d0=x_diff_slope.d0 - pt0.y.d0 + pt1.y.d0, + d1=x_diff_slope.d1 - pt0.y.d1 + pt1.y.d1, + d2=x_diff_slope.d2 - pt0.y.d2 + pt1.y.d2, + ), + ); + + return (slope=slope); +} + +func test_compute_slope{range_check_ptr: felt}() { + let x0 = BigInt3(d0=1, d1=5, d2=10); + let y0 = BigInt3(d0=2, d1=4, d2=20); + + let pt0 = EcPoint(x=x0, y=y0); + + let x1 = BigInt3(d0=3, d1=3, d2=3); + let y1 = BigInt3(d0=3, d1=5, d2=22); + + let pt1 = EcPoint(x=x1, y=y1); + + // Compute slope + let (slope) = compute_slope(pt0, pt1); + + assert slope = BigInt3( + d0=39919528597790922692721903, d1=31451568879578276714332055, d2=6756007504256943629292535 + ); +} + +func main{range_check_ptr: felt}() { + test_compute_slope(); + + 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 535db3b7ec..89770142c3 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 @@ -356,9 +356,22 @@ impl HintProcessor for BuiltinHintProcessor { hint_code::EC_DOUBLE_SCOPE => { compute_doubling_slope(vm, exec_scopes, &hint_data.ids_data, &hint_data.ap_tracking) } - hint_code::COMPUTE_SLOPE => { - compute_slope(vm, exec_scopes, &hint_data.ids_data, &hint_data.ap_tracking) - } + hint_code::COMPUTE_SLOPE => compute_slope( + vm, + exec_scopes, + &hint_data.ids_data, + &hint_data.ap_tracking, + "point0", + "point1", + ), + hint_code::COMPUTE_SLOPE_WHITELIST => compute_slope( + vm, + exec_scopes, + &hint_data.ids_data, + &hint_data.ap_tracking, + "pt0", + "pt1", + ), hint_code::EC_DOUBLE_ASSIGN_NEW_X => { ec_double_assign_new_x(vm, exec_scopes, &hint_data.ids_data, &hint_data.ap_tracking) } diff --git a/src/hint_processor/builtin_hint_processor/hint_code.rs b/src/hint_processor/builtin_hint_processor/hint_code.rs index aa0472104f..88fb0f7125 100644 --- a/src/hint_processor/builtin_hint_processor/hint_code.rs +++ b/src/hint_processor/builtin_hint_processor/hint_code.rs @@ -465,6 +465,16 @@ x1 = pack(ids.point1.x, PRIME) y1 = pack(ids.point1.y, PRIME) value = slope = line_slope(point1=(x0, y0), point2=(x1, y1), p=SECP_P)"#; +pub(crate) const COMPUTE_SLOPE_WHITELIST: &str = r#"from starkware.cairo.common.cairo_secp.secp_utils import SECP_P, pack +from starkware.python.math_utils import div_mod + +# Compute the slope. +x0 = pack(ids.pt0.x, PRIME) +y0 = pack(ids.pt0.y, PRIME) +x1 = pack(ids.pt1.x, PRIME) +y1 = pack(ids.pt1.y, PRIME) +value = slope = div_mod(y0 - y1, x0 - x1, SECP_P)"#; + pub(crate) const EC_DOUBLE_ASSIGN_NEW_X: &str = r#"from starkware.cairo.common.cairo_secp.secp_utils import SECP_P, pack slope = pack(ids.slope, PRIME) 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 cd41607005..d59da2c96d 100644 --- a/src/hint_processor/builtin_hint_processor/secp/ec_utils.rs +++ b/src/hint_processor/builtin_hint_processor/secp/ec_utils.rs @@ -113,11 +113,13 @@ pub fn compute_slope( exec_scopes: &mut ExecutionScopes, ids_data: &HashMap, ap_tracking: &ApTracking, + point0_alias: &str, + point1_alias: &str, ) -> Result<(), HintError> { //ids.point0 - let point0 = EcPoint::from_var_name("point0", vm, ids_data, ap_tracking)?; + let point0 = EcPoint::from_var_name(point0_alias, vm, ids_data, ap_tracking)?; //ids.point1 - let point1 = EcPoint::from_var_name("point1", vm, ids_data, ap_tracking)?; + let point1 = EcPoint::from_var_name(point1_alias, vm, ids_data, ap_tracking)?; let value = line_slope( &(pack(point0.x), pack(point0.y)), @@ -406,6 +408,57 @@ mod tests { ); } + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + fn run_compute_slope_wdivmod_ok() { + let hint_code = "from starkware.cairo.common.cairo_secp.secp_utils import SECP_P, pack\nfrom starkware.python.math_utils import div_mod\n\n# Compute the slope.\nx0 = pack(ids.pt0.x, PRIME)\ny0 = pack(ids.pt0.y, PRIME)\nx1 = pack(ids.pt1.x, PRIME)\ny1 = pack(ids.pt1.y, PRIME)\nvalue = slope = div_mod(y0 - y1, x0 - x1, SECP_P)"; + let mut vm = vm_with_range_check!(); + + // Insert ids.pt0 and ids.pt1 into memory + vm.segments = segments![ + ((1, 0), 134), + ((1, 1), 5123), + ((1, 2), 140), + ((1, 3), 1232), + ((1, 4), 4652), + ((1, 5), 720), + ((1, 6), 156), + ((1, 7), 6545), + ((1, 8), 100010), + ((1, 9), 1123), + ((1, 10), 1325), + ((1, 11), 910) + ]; + + // Initialize fp + vm.run_context.fp = 14; + let ids_data = HashMap::from([ + ("pt0".to_string(), HintReference::new_simple(-14)), + ("pt1".to_string(), HintReference::new_simple(-8)), + ]); + let mut exec_scopes = ExecutionScopes::new(); + + // Execute the hint + assert_matches!(run_hint!(vm, ids_data, hint_code, &mut exec_scopes), Ok(())); + check_scope!( + &exec_scopes, + [ + ( + "value", + bigint_str!( + "41419765295989780131385135514529906223027172305400087935755859001910844026631" + ) + ), + ( + "slope", + bigint_str!( + "41419765295989780131385135514529906223027172305400087935755859001910844026631" + ) + ) + ] + ); + } + #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn run_ec_double_assign_new_x_ok() { diff --git a/src/tests/cairo_run_test.rs b/src/tests/cairo_run_test.rs index 9c12f8b0e3..22eb20e22c 100644 --- a/src/tests/cairo_run_test.rs +++ b/src/tests/cairo_run_test.rs @@ -1280,3 +1280,10 @@ fn cairo_run_is_quad_residue_test() { let program_data = include_bytes!("../../cairo_programs/is_quad_residue_test.json"); run_program_simple(program_data.as_slice()); } + +#[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +fn cairo_run_ed25519_ec() { + let program_data = include_bytes!("../../cairo_programs/ed25519_ec.json"); + run_program_simple(program_data.as_slice()); +} From aaa202b0713d62cd36484479b058e41a46cf234a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s?= Date: Thu, 13 Apr 2023 17:54:33 -0300 Subject: [PATCH 2/6] Update changelog --- CHANGELOG.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a1c8956cac..e7d339ccc9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,20 @@ #### Upcoming Changes +* Add missing hint on cairo_secp lib [#984]: + `BuiltinHintProcessor` now supports the following hint: + ```python + from starkware.cairo.common.cairo_secp.secp_utils import SECP_P, pack + from starkware.python.math_utils import div_mod + + # Compute the slope. + x0 = pack(ids.pt0.x, PRIME) + y0 = pack(ids.pt0.y, PRIME) + x1 = pack(ids.pt1.x, PRIME) + y1 = pack(ids.pt1.y, PRIME) + value = slope = div_mod(y0 - y1, x0 - x1, SECP_P) + ``` + * Move `Memory` into `MemorySegmentManager` [#830](https://github.com/lambdaclass/cairo-rs/pull/830) * Structural changes: * Remove `memory: Memory` field from `VirtualMachine` From 6e54f1da8362aed6b9c119880ef1d33222dcdedc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s?= Date: Thu, 13 Apr 2023 18:09:30 -0300 Subject: [PATCH 3/6] Fix: add missing return to cairo program --- cairo_programs/ed25519_ec.cairo | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cairo_programs/ed25519_ec.cairo b/cairo_programs/ed25519_ec.cairo index 4f6e6b5527..61425dbb22 100644 --- a/cairo_programs/ed25519_ec.cairo +++ b/cairo_programs/ed25519_ec.cairo @@ -66,6 +66,8 @@ func test_compute_slope{range_check_ptr: felt}() { assert slope = BigInt3( d0=39919528597790922692721903, d1=31451568879578276714332055, d2=6756007504256943629292535 ); + + return (); } func main{range_check_ptr: felt}() { From f35c393ee6107ea795f4a9883a84a7716a366bb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s?= Date: Thu, 13 Apr 2023 18:41:31 -0300 Subject: [PATCH 4/6] Add `EC_DOUBLE_SCOPE_WHITELIST` hint --- cairo_programs/ed25519_ec.cairo | 48 +++++++++++++++++++ .../builtin_hint_processor_definition.rs | 17 +++++-- .../builtin_hint_processor/hint_code.rs | 8 ++++ .../builtin_hint_processor/secp/ec_utils.rs | 44 ++++++++++++++++- 4 files changed, 113 insertions(+), 4 deletions(-) diff --git a/cairo_programs/ed25519_ec.cairo b/cairo_programs/ed25519_ec.cairo index 61425dbb22..71c841cbd4 100644 --- a/cairo_programs/ed25519_ec.cairo +++ b/cairo_programs/ed25519_ec.cairo @@ -18,6 +18,37 @@ struct EcPoint { y: BigInt3, } +// Returns the slope of the elliptic curve at the given point. +// The slope is used to compute pt + pt. +// Assumption: pt != 0. +func compute_doubling_slope{range_check_ptr}(pt: EcPoint) -> (slope: BigInt3) { + // Note that y cannot be zero: assume that it is, then pt = -pt, so 2 * pt = 0, which + // contradicts the fact that the size of the curve is odd. + %{ + from starkware.cairo.common.cairo_secp.secp_utils import SECP_P, pack + from starkware.python.math_utils import div_mod + + # Compute the slope. + x = pack(ids.pt.x, PRIME) + y = pack(ids.pt.y, PRIME) + value = slope = div_mod(3 * x ** 2, 2 * y, SECP_P) + %} + let (slope: BigInt3) = nondet_bigint3(); + + let (x_sqr: UnreducedBigInt3) = unreduced_sqr(pt.x); + let (slope_y: UnreducedBigInt3) = unreduced_mul(slope, pt.y); + + verify_zero( + UnreducedBigInt3( + d0=3 * x_sqr.d0 - 2 * slope_y.d0, + d1=3 * x_sqr.d1 - 2 * slope_y.d1, + d2=3 * x_sqr.d2 - 2 * slope_y.d2, + ), + ); + + return (slope=slope); +} + // Returns the slope of the line connecting the two given points. // The slope is used to compute pt0 + pt1. // Assumption: pt0.x != pt1.x (mod secp256k1_prime). @@ -49,6 +80,22 @@ func compute_slope{range_check_ptr: felt}(pt0: EcPoint, pt1: EcPoint) -> (slope: return (slope=slope); } +func test_compute_double_slope{range_check_ptr: felt}() { + let x = BigInt3(d0=33, d1=24, d2=12412); + let y = BigInt3(d0=3232, d1=122, d2=31415); + + let pt = EcPoint(x=x, y=y); + + // Compute slope + let (slope) = compute_doubling_slope(pt); + + assert slope = BigInt3( + d0=56007611085086895200895667, d1=15076814030975805918069142, d2=6556143173243739984479201 + ); + + return (); +} + func test_compute_slope{range_check_ptr: felt}() { let x0 = BigInt3(d0=1, d1=5, d2=10); let y0 = BigInt3(d0=2, d1=4, d2=20); @@ -71,6 +118,7 @@ func test_compute_slope{range_check_ptr: felt}() { } func main{range_check_ptr: felt}() { + test_compute_double_slope(); test_compute_slope(); 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 89770142c3..bad11ca60e 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 @@ -353,9 +353,20 @@ impl HintProcessor for BuiltinHintProcessor { hint_code::EC_NEGATE => { ec_negate(vm, exec_scopes, &hint_data.ids_data, &hint_data.ap_tracking) } - hint_code::EC_DOUBLE_SCOPE => { - compute_doubling_slope(vm, exec_scopes, &hint_data.ids_data, &hint_data.ap_tracking) - } + hint_code::EC_DOUBLE_SCOPE => compute_doubling_slope( + vm, + exec_scopes, + &hint_data.ids_data, + &hint_data.ap_tracking, + "point", + ), + hint_code::EC_DOUBLE_SCOPE_WHITELIST => compute_doubling_slope( + vm, + exec_scopes, + &hint_data.ids_data, + &hint_data.ap_tracking, + "pt", + ), hint_code::COMPUTE_SLOPE => compute_slope( vm, exec_scopes, diff --git a/src/hint_processor/builtin_hint_processor/hint_code.rs b/src/hint_processor/builtin_hint_processor/hint_code.rs index 88fb0f7125..b2b13c6093 100644 --- a/src/hint_processor/builtin_hint_processor/hint_code.rs +++ b/src/hint_processor/builtin_hint_processor/hint_code.rs @@ -455,6 +455,14 @@ x = pack(ids.point.x, PRIME) y = pack(ids.point.y, PRIME) value = slope = ec_double_slope(point=(x, y), alpha=0, p=SECP_P)"#; +pub(crate) const EC_DOUBLE_SCOPE_WHITELIST: &str = r#"from starkware.cairo.common.cairo_secp.secp_utils import SECP_P, pack +from starkware.python.math_utils import div_mod + +# Compute the slope. +x = pack(ids.pt.x, PRIME) +y = pack(ids.pt.y, PRIME) +value = slope = div_mod(3 * x ** 2, 2 * y, SECP_P)"#; + pub(crate) const COMPUTE_SLOPE: &str = r#"from starkware.cairo.common.cairo_secp.secp_utils import SECP_P, pack from starkware.python.math_utils import line_slope 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 d59da2c96d..9c7dfe74f8 100644 --- a/src/hint_processor/builtin_hint_processor/secp/ec_utils.rs +++ b/src/hint_processor/builtin_hint_processor/secp/ec_utils.rs @@ -84,9 +84,10 @@ pub fn compute_doubling_slope( exec_scopes: &mut ExecutionScopes, ids_data: &HashMap, ap_tracking: &ApTracking, + point_alias: &str, ) -> Result<(), HintError> { //ids.point - let point = EcPoint::from_var_name("point", vm, ids_data, ap_tracking)?; + let point = EcPoint::from_var_name(point_alias, vm, ids_data, ap_tracking)?; let value = ec_double_slope(&(pack(point.x), pack(point.y)), &BigInt::zero(), &SECP_P); exec_scopes.insert_value("value", value.clone()); @@ -357,6 +358,47 @@ mod tests { ); } + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + fn run_compute_doubling_slope_wdivmod_ok() { + let hint_code = "from starkware.cairo.common.cairo_secp.secp_utils import SECP_P, pack\nfrom starkware.python.math_utils import div_mod\n\n# Compute the slope.\nx = pack(ids.pt.x, PRIME)\ny = pack(ids.pt.y, PRIME)\nvalue = slope = div_mod(3 * x ** 2, 2 * y, SECP_P)"; + let mut vm = vm_with_range_check!(); + vm.segments = segments![ + ((1, 0), 614323u64), + ((1, 1), 5456867u64), + ((1, 2), 101208u64), + ((1, 3), 773712524u64), + ((1, 4), 77371252u64), + ((1, 5), 5298795u64) + ]; + + //Initialize fp + vm.run_context.fp = 1; + + let ids_data = ids_data!["pt"]; + let mut exec_scopes = ExecutionScopes::new(); + + //Execute the hint + assert_matches!(run_hint!(vm, ids_data, hint_code, &mut exec_scopes), Ok(())); + check_scope!( + &exec_scopes, + [ + ( + "value", + bigint_str!( + "40442433062102151071094722250325492738932110061897694430475034100717288403728" + ) + ), + ( + "slope", + bigint_str!( + "40442433062102151071094722250325492738932110061897694430475034100717288403728" + ) + ) + ] + ); + } + #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn run_compute_slope_ok() { From 9f68e732649da2e1478d033010c36e74c49a0105 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s?= Date: Thu, 13 Apr 2023 18:50:58 -0300 Subject: [PATCH 5/6] Update changelog --- CHANGELOG.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e7d339ccc9..274e925923 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,18 @@ #### Upcoming Changes +* Add missing hint on cairo_secp lib [#986]: + `BuiltinHintProcessor` now supports the following hint: + ```python + from starkware.cairo.common.cairo_secp.secp_utils import SECP_P, pack + from starkware.python.math_utils import div_mod + + # Compute the slope. + x = pack(ids.pt.x, PRIME) + y = pack(ids.pt.y, PRIME) + value = slope = div_mod(3 * x ** 2, 2 * y, SECP_P) + ``` + * Add missing hint on cairo_secp lib [#984]: `BuiltinHintProcessor` now supports the following hint: ```python From 1fa59382613db588760a10a53f64715d0e97fcd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s?= Date: Fri, 14 Apr 2023 11:51:21 -0300 Subject: [PATCH 6/6] Add missing newline --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 274e925923..3d82ad1d20 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ #### Upcoming Changes * Add missing hint on cairo_secp lib [#986]: + `BuiltinHintProcessor` now supports the following hint: ```python from starkware.cairo.common.cairo_secp.secp_utils import SECP_P, pack