Skip to content

Commit

Permalink
feat(hints): add NewHint#26 (lambdaclass#992)
Browse files Browse the repository at this point in the history
* Add NewHint#26

* Update changelog

* Change assert_matches for assert + is_ok

* Get and insert SECP_P from scope where applicable

* Appease clippy
  • Loading branch information
MegaRedHand authored and kariy committed Jun 23, 2023
1 parent 341e2bb commit 8ce29cc
Show file tree
Hide file tree
Showing 7 changed files with 156 additions and 31 deletions.
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,18 @@
* Added dynamic layout [#879](https://github.com/lambdaclass/cairo-rs/pull/879)
* `get_segment_size` was exposed [#934](https://github.com/lambdaclass/cairo-rs/pull/934)

* Add missing hint on cairo_secp lib [#992](https://github.com/lambdaclass/cairo-rs/pull/992):

`BuiltinHintProcessor` now supports the following hint:

```python
from starkware.cairo.common.cairo_secp.secp_utils import pack

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 cairo_secp lib [#990](https://github.com/lambdaclass/cairo-rs/pull/990):

`BuiltinHintProcessor` now supports the following hint:
Expand Down
54 changes: 54 additions & 0 deletions cairo_programs/ed25519_field.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,51 @@ func verify_zero{range_check_ptr}(val: UnreducedBigInt3) {
return ();
}

// Source: https://github.com/myBraavos/efficient-secp256r1/blob/73cca4d53730cb8b2dcf34e36c7b8f34b96b3230/src/secp256r1/ec.cairo#L106
func verify_zero_alt{range_check_ptr}(val: UnreducedBigInt3) {
let x = val;
// Used just to import SECP_P in scope
%{
from starkware.cairo.common.cairo_secp.secp_utils import SECP_P, pack
value = pack(ids.x, PRIME) % SECP_P
%}
nondet_bigint3();

let q = [ap];
%{
from starkware.cairo.common.cairo_secp.secp_utils import pack
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++;

tempvar r1 = (val.d0 + q * SECP_REM) / BASE;
assert [range_check_ptr + 1] = r1 + 2 ** 127;
// This implies r1 * BASE = val.d0 + q * SECP_REM (as integers).

tempvar r2 = (val.d1 + r1) / BASE;
assert [range_check_ptr + 2] = r2 + 2 ** 127;
// This implies r2 * BASE = val.d1 + r1 (as integers).
// Therefore, r2 * BASE**2 = val.d1 * BASE + r1 * BASE.

assert val.d2 = q * (BASE / 4) - r2;
// This implies 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).

let range_check_ptr = range_check_ptr + 3;
return ();
}

func test_verify_zero{range_check_ptr: felt}() {
let val = UnreducedBigInt3(0, 0, 0);

Expand All @@ -59,8 +104,17 @@ func test_verify_zero{range_check_ptr: felt}() {
return ();
}

func test_verify_zero_alt{range_check_ptr: felt}() {
let val = UnreducedBigInt3(0, 0, 0);

verify_zero_alt(val);

return ();
}

func main{range_check_ptr: felt}() {
test_verify_zero();
test_verify_zero_alt();

return ();
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
use crate::stdlib::{any::Any, collections::HashMap, prelude::*, rc::Rc};

use crate::{
hint_processor::{
builtin_hint_processor::{
Expand All @@ -14,6 +12,7 @@ use crate::{
default_dict_new, dict_new, dict_read, dict_squash_copy_dict,
dict_squash_update_ptr, dict_update, dict_write,
},
ec_utils::{chained_ec_op_random_ec_point_hint, random_ec_point_hint, recover_y_hint},
find_element_hint::{find_element, search_sorted_lower},
hint_code,
keccak_utils::{
Expand All @@ -36,7 +35,7 @@ use crate::{
},
field_utils::{
is_zero_assign_scope_variables, is_zero_nondet, is_zero_pack, reduce,
verify_zero,
verify_zero, verify_zero_with_external_const,
},
signature::{
div_mod_n_packed_divmod, div_mod_n_safe_div, get_point_from_x,
Expand All @@ -58,6 +57,10 @@ use crate::{
split_64, uint256_add, uint256_mul_div_mod, uint256_signed_nn, uint256_sqrt,
uint256_unsigned_div_rem,
},
uint384::{
add_no_uint384_check, uint384_signed_nn, uint384_split_128, uint384_sqrt,
uint384_unsigned_div_rem, uint384_unsigned_div_rem_expanded,
},
usort::{
usort_body, usort_enter_scope, verify_multiplicity_assert,
verify_multiplicity_body, verify_usort,
Expand All @@ -66,6 +69,7 @@ use crate::{
hint_processor_definition::{HintProcessor, HintReference},
},
serde::deserialize_program::ApTracking,
stdlib::{any::Any, collections::HashMap, prelude::*, rc::Rc},
types::exec_scope::ExecutionScopes,
vm::{errors::hint_errors::HintError, vm_core::VirtualMachine},
};
Expand All @@ -74,12 +78,6 @@ use felt::Felt252;
#[cfg(feature = "skip_next_instruction_hint")]
use crate::hint_processor::builtin_hint_processor::skip_next_instruction::skip_next_instruction;

use super::ec_utils::{chained_ec_op_random_ec_point_hint, random_ec_point_hint, recover_y_hint};
use super::uint384::{
add_no_uint384_check, uint384_signed_nn, uint384_split_128, uint384_sqrt,
uint384_unsigned_div_rem, uint384_unsigned_div_rem_expanded,
};

pub struct HintProcessorData {
pub code: String,
pub ap_tracking: ApTracking,
Expand Down Expand Up @@ -253,8 +251,14 @@ impl HintProcessor for BuiltinHintProcessor {
compute_blake2s(vm, &hint_data.ids_data, &hint_data.ap_tracking)
}
hint_code::VERIFY_ZERO_V1 | hint_code::VERIFY_ZERO_V2 => {
verify_zero(vm, &hint_data.ids_data, &hint_data.ap_tracking)
verify_zero(vm, exec_scopes, &hint_data.ids_data, &hint_data.ap_tracking)
}
hint_code::VERIFY_ZERO_EXTERNAL_SECP => verify_zero_with_external_const(
vm,
exec_scopes,
&hint_data.ids_data,
&hint_data.ap_tracking,
),
hint_code::NONDET_BIGINT3 => {
nondet_bigint3(vm, exec_scopes, &hint_data.ids_data, &hint_data.ap_tracking)
}
Expand Down
6 changes: 6 additions & 0 deletions src/hint_processor/builtin_hint_processor/hint_code.rs
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,12 @@ 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)
assert r == 0, f"verify_zero: Invalid input {ids.val.d0, ids.val.d1, ids.val.d2}."
ids.q = q % PRIME"#;

pub const REDUCE: &str = r#"from starkware.cairo.common.cairo_secp.secp_utils import SECP_P, pack
value = pack(ids.x, PRIME) % SECP_P"#;
Expand Down
8 changes: 7 additions & 1 deletion src/hint_processor/builtin_hint_processor/secp/ec_utils.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::stdlib::{collections::HashMap, ops::BitAnd, prelude::*};
use crate::{
any_box,
hint_processor::{
builtin_hint_processor::{
hint_utils::{
Expand All @@ -11,6 +11,7 @@ use crate::{
},
math_utils::{ec_double_slope, line_slope},
serde::deserialize_program::ApTracking,
stdlib::{collections::HashMap, ops::BitAnd, prelude::*},
types::exec_scope::ExecutionScopes,
vm::{errors::hint_errors::HintError, vm_core::VirtualMachine},
};
Expand Down Expand Up @@ -58,6 +59,7 @@ pub fn ec_negate(
ids_data: &HashMap<String, HintReference>,
ap_tracking: &ApTracking,
) -> Result<(), HintError> {
exec_scopes.assign_or_update_variable("SECP_P", any_box!(SECP_P.clone()));
//ids.point
let point_y = (get_relocatable_from_var_name("point", vm, ids_data, ap_tracking)? + 3i32)?;
let y_bigint3 = BigInt3::from_base_addr(point_y, "point.y", vm)?;
Expand Down Expand Up @@ -86,6 +88,7 @@ pub fn compute_doubling_slope(
ap_tracking: &ApTracking,
point_alias: &str,
) -> Result<(), HintError> {
exec_scopes.assign_or_update_variable("SECP_P", any_box!(SECP_P.clone()));
//ids.point
let point = EcPoint::from_var_name(point_alias, vm, ids_data, ap_tracking)?;

Expand Down Expand Up @@ -117,6 +120,7 @@ pub fn compute_slope(
point0_alias: &str,
point1_alias: &str,
) -> Result<(), HintError> {
exec_scopes.assign_or_update_variable("SECP_P", any_box!(SECP_P.clone()));
//ids.point0
let point0 = EcPoint::from_var_name(point0_alias, vm, ids_data, ap_tracking)?;
//ids.point1
Expand Down Expand Up @@ -150,6 +154,7 @@ pub fn ec_double_assign_new_x(
ids_data: &HashMap<String, HintReference>,
ap_tracking: &ApTracking,
) -> Result<(), HintError> {
exec_scopes.assign_or_update_variable("SECP_P", any_box!(SECP_P.clone()));
//ids.slope
let slope = BigInt3::from_var_name("slope", vm, ids_data, ap_tracking)?;
//ids.point
Expand Down Expand Up @@ -208,6 +213,7 @@ pub fn fast_ec_add_assign_new_x(
ids_data: &HashMap<String, HintReference>,
ap_tracking: &ApTracking,
) -> Result<(), HintError> {
exec_scopes.assign_or_update_variable("SECP_P", any_box!(SECP_P.clone()));
//ids.slope
let slope = BigInt3::from_var_name("slope", vm, ids_data, ap_tracking)?;
//ids.point0
Expand Down
81 changes: 61 additions & 20 deletions src/hint_processor/builtin_hint_processor/secp/field_utils.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::any_box;
use crate::stdlib::{collections::HashMap, prelude::*};

use crate::{
Expand Down Expand Up @@ -30,9 +31,11 @@ Implements hint:
*/
pub fn verify_zero(
vm: &mut VirtualMachine,
exec_scopes: &mut ExecutionScopes,
ids_data: &HashMap<String, HintReference>,
ap_tracking: &ApTracking,
) -> Result<(), HintError> {
exec_scopes.assign_or_update_variable("SECP_P", any_box!(SECP_P.clone()));
let val = pack(BigInt3::from_var_name("val", vm, ids_data, ap_tracking)?);
let (q, r) = val.div_rem(&SECP_P);
if !r.is_zero() {
Expand All @@ -42,6 +45,32 @@ pub fn verify_zero(
insert_value_from_var_name("q", Felt252::new(q), vm, ids_data, ap_tracking)
}

/*
Implements hint:
%{
from starkware.cairo.common.cairo_secp.secp_utils import pack
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 fn verify_zero_with_external_const(
vm: &mut VirtualMachine,
exec_scopes: &mut ExecutionScopes,
ids_data: &HashMap<String, HintReference>,
ap_tracking: &ApTracking,
) -> Result<(), HintError> {
let secp_p = exec_scopes.get_ref("SECP_P")?;
let val = pack(BigInt3::from_var_name("val", vm, ids_data, ap_tracking)?);
let (q, r) = val.div_rem(secp_p);
if !r.is_zero() {
return Err(HintError::SecpVerifyZero(val));
}

insert_value_from_var_name("q", Felt252::new(q), vm, ids_data, ap_tracking)
}

/*
Implements hint:
%{
Expand All @@ -56,6 +85,7 @@ pub fn reduce(
ids_data: &HashMap<String, HintReference>,
ap_tracking: &ApTracking,
) -> Result<(), HintError> {
exec_scopes.assign_or_update_variable("SECP_P", any_box!(SECP_P.clone()));
let value = pack(BigInt3::from_var_name("x", vm, ids_data, ap_tracking)?);
exec_scopes.insert_value("value", value.mod_floor(&SECP_P));
Ok(())
Expand All @@ -75,6 +105,8 @@ pub fn is_zero_pack(
ids_data: &HashMap<String, HintReference>,
ap_tracking: &ApTracking,
) -> Result<(), HintError> {
exec_scopes.assign_or_update_variable("SECP_P", any_box!(SECP_P.clone()));

let x_packed = pack(BigInt3::from_var_name("x", vm, ids_data, ap_tracking)?);
let x = x_packed.mod_floor(&SECP_P);
exec_scopes.insert_value("x", x);
Expand Down Expand Up @@ -114,6 +146,7 @@ Implements hint:
%}
*/
pub fn is_zero_assign_scope_variables(exec_scopes: &mut ExecutionScopes) -> Result<(), HintError> {
exec_scopes.assign_or_update_variable("SECP_P", any_box!(SECP_P.clone()));
//Get `x` variable from vm scope
let x = exec_scopes.get::<BigInt>("x")?;

Expand Down Expand Up @@ -154,38 +187,46 @@ mod tests {
#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn run_verify_zero_ok() {
let hint_code = "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";
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_matches!(
run_hint!(vm, ids_data, hint_code, exec_scopes_ref!()),
Ok(())
);
//Check hint memory inserts
//ids.q
check_memory![vm.segments.memory, ((1, 9), 0)];
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",
];
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_without_pack_ok() {
let hint_code = "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";
fn run_verify_zero_with_external_const_ok() {
let hint_code = "from starkware.cairo.common.cairo_secp.secp_utils import 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";
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)];
vm.segments = segments![((1, 4), 55), ((1, 5), 0), ((1, 6), 0)];

let new_secp_p = 55;

let mut exec_scopes = ExecutionScopes::new();
exec_scopes.assign_or_update_variable("SECP_P", any_box!(bigint!(new_secp_p)));

//Execute the hint
assert!(run_hint!(vm, ids_data, hint_code, exec_scopes_ref!()).is_ok());
assert!(run_hint!(vm, ids_data, hint_code, &mut exec_scopes).is_ok());
//Check hint memory inserts
//ids.q
check_memory![vm.segments.memory, ((1, 9), 0)];
check_memory![vm.segments.memory, ((1, 9), 1)];
}

#[test]
Expand Down
2 changes: 2 additions & 0 deletions src/hint_processor/builtin_hint_processor/secp/signature.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::any_box;
use crate::stdlib::{collections::HashMap, ops::Shr, prelude::*};

use crate::{
Expand Down Expand Up @@ -87,6 +88,7 @@ pub fn get_point_from_x(
ap_tracking: &ApTracking,
constants: &HashMap<String, Felt252>,
) -> Result<(), HintError> {
exec_scopes.assign_or_update_variable("SECP_P", any_box!(SECP_P.clone()));
#[allow(deprecated)]
let beta = constants
.get(BETA)
Expand Down

0 comments on commit 8ce29cc

Please sign in to comment.