Skip to content
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,16 @@
* 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 [#996](https://github.com/lambdaclass/cairo-rs/pull/996):

`BuiltinHintProcessor` now supports the following hint:

```python
from starkware.python.math_utils import div_mod

value = x_inv = div_mod(1, x, SECP_P)
```

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

`BuiltinHintProcessor` now supports the following hint:
Expand Down
59 changes: 59 additions & 0 deletions cairo_programs/is_zero.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
%builtins range_check

from starkware.cairo.common.cairo_secp.bigint import BigInt3, UnreducedBigInt3, nondet_bigint3
from starkware.cairo.common.cairo_secp.field import unreduced_mul, verify_zero

// Returns 1 if x == 0 (mod secp256k1_prime), and 0 otherwise.
//
// Completeness assumption: x's limbs are in the range (-BASE, 2*BASE).
// Soundness assumption: x's limbs are in the range (-2**107.49, 2**107.49).
func is_zero{range_check_ptr}(x: BigInt3) -> (res: felt) {
%{
from starkware.cairo.common.cairo_secp.secp_utils import SECP_P, pack

x = pack(ids.x, PRIME) % SECP_P
%}
if (nondet %{ x == 0 %} != 0) {
verify_zero(UnreducedBigInt3(d0=x.d0, d1=x.d1, d2=x.d2));
return (res=1);
}

%{
from starkware.python.math_utils import div_mod

value = x_inv = div_mod(1, x, SECP_P)
%}
let (x_inv) = nondet_bigint3();
let (x_x_inv) = unreduced_mul(x, x_inv);

// Check that x * x_inv = 1 to verify that x != 0.
verify_zero(UnreducedBigInt3(d0=x_x_inv.d0 - 1, d1=x_x_inv.d1, d2=x_x_inv.d2));
return (res=0);
}

func test_is_zero{range_check_ptr}() -> () {
let zero = BigInt3(0, 0, 0);

let (res: felt) = is_zero(zero);
assert res = 1;

let one = BigInt3(1, 0, 0);

let (res: felt) = is_zero(one);
assert res = 0;

let secp256k1_prime = BigInt3(
77371252455336262886226991, 77371252455336267181195263, 19342813113834066795298815
);

let (res: felt) = is_zero(secp256k1_prime);
assert res = 1;

return ();
}

func main{range_check_ptr}() -> () {
test_is_zero();

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 @@ -35,8 +34,8 @@ use crate::{
fast_ec_add_assign_new_y,
},
field_utils::{
is_zero_assign_scope_variables, is_zero_nondet, is_zero_pack, reduce,
verify_zero,
is_zero_assign_scope_variables, is_zero_assign_scope_variables_external_const,
is_zero_nondet, is_zero_pack, reduce, verify_zero,
},
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 @@ -346,6 +344,9 @@ impl HintProcessor for BuiltinHintProcessor {
}
hint_code::IS_ZERO_NONDET => is_zero_nondet(vm, exec_scopes),
hint_code::IS_ZERO_ASSIGN_SCOPE_VARS => is_zero_assign_scope_variables(exec_scopes),
hint_code::IS_ZERO_ASSIGN_SCOPE_VARS_EXTERNAL_SECP => {
is_zero_assign_scope_variables_external_const(exec_scopes)
}
hint_code::DIV_MOD_N_PACKED_DIVMOD => div_mod_n_packed_divmod(
vm,
exec_scopes,
Expand Down
5 changes: 5 additions & 0 deletions src/hint_processor/builtin_hint_processor/hint_code.rs
Original file line number Diff line number Diff line change
Expand Up @@ -429,11 +429,16 @@ pub const IS_ZERO_NONDET: &str = "memory[ap] = to_felt_or_relocatable(x == 0)";
pub const IS_ZERO_PACK: &str = r#"from starkware.cairo.common.cairo_secp.secp_utils import SECP_P, pack

x = pack(ids.x, PRIME) % SECP_P"#;

pub const IS_ZERO_ASSIGN_SCOPE_VARS: &str = r#"from starkware.cairo.common.cairo_secp.secp_utils import SECP_P
from starkware.python.math_utils import div_mod

value = x_inv = div_mod(1, x, SECP_P)"#;

pub const IS_ZERO_ASSIGN_SCOPE_VARS_EXTERNAL_SECP: &str = r#"from starkware.python.math_utils import div_mod

value = x_inv = div_mod(1, x, SECP_P)"#;

pub const DIV_MOD_N_PACKED_DIVMOD: &str = r#"from starkware.cairo.common.cairo_secp.secp_utils import N, pack
from starkware.python.math_utils import div_mod, safe_div

Expand Down
87 changes: 58 additions & 29 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 @@ -114,6 +115,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 All @@ -123,6 +125,27 @@ pub fn is_zero_assign_scope_variables(exec_scopes: &mut ExecutionScopes) -> Resu
Ok(())
}

/*
Implements hint:
%{
from starkware.python.math_utils import div_mod

value = x_inv = div_mod(1, x, SECP_P)
%}
*/
pub fn is_zero_assign_scope_variables_external_const(
exec_scopes: &mut ExecutionScopes,
) -> Result<(), HintError> {
//Get variables from vm scope
let secp_p = exec_scopes.get_ref::<BigInt>("SECP_P")?;
let x = exec_scopes.get_ref::<BigInt>("x")?;

let value = div_mod(&BigInt::one(), x, secp_p);
exec_scopes.insert_value("value", value.clone());
exec_scopes.insert_value("x_inv", value);
Ok(())
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down Expand Up @@ -476,38 +499,44 @@ mod tests {
#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn is_zero_assign_scope_variables_ok() {
let hint_code = "from starkware.cairo.common.cairo_secp.secp_utils import SECP_P\nfrom starkware.python.math_utils import div_mod\n\nvalue = x_inv = div_mod(1, x, SECP_P)";
let mut vm = vm_with_range_check!();
let hint_codes = vec![
"from starkware.cairo.common.cairo_secp.secp_utils import SECP_P\nfrom starkware.python.math_utils import div_mod\n\nvalue = x_inv = div_mod(1, x, SECP_P)",
"from starkware.python.math_utils import div_mod\n\nvalue = x_inv = div_mod(1, x, SECP_P)"
];

//Initialize vm scope with variable `x`
let mut exec_scopes = ExecutionScopes::new();
exec_scopes.assign_or_update_variable(
"x",
any_box!(bigint_str!(
"52621538839140286024584685587354966255185961783273479086367"
)),
);
//Execute the hint
assert_matches!(
run_hint!(vm, HashMap::new(), hint_code, &mut exec_scopes),
Ok(())
);
for hint_code in hint_codes {
let mut vm = vm_with_range_check!();

//Check 'value' is defined in the vm scope
assert_matches!(
exec_scopes.get::<BigInt>("value"),
Ok(x) if x == bigint_str!(
"19429627790501903254364315669614485084365347064625983303617500144471999752609"
)
);
//Initialize vm scope with variable `x`
let mut exec_scopes = ExecutionScopes::new();
exec_scopes.assign_or_update_variable(
"x",
any_box!(bigint_str!(
"52621538839140286024584685587354966255185961783273479086367"
)),
);
//Execute the hint
assert_matches!(
run_hint!(vm, HashMap::new(), hint_code, &mut exec_scopes),
Ok(())
);

//Check 'value' is defined in the vm scope
assert_matches!(
exec_scopes.get::<BigInt>("value"),
Ok(x) if x == bigint_str!(
"19429627790501903254364315669614485084365347064625983303617500144471999752609"
)
);

//Check 'x_inv' is defined in the vm scope
assert_matches!(
exec_scopes.get::<BigInt>("x_inv"),
Ok(x) if x == bigint_str!(
"19429627790501903254364315669614485084365347064625983303617500144471999752609"
)
);
//Check 'x_inv' is defined in the vm scope
assert_matches!(
exec_scopes.get::<BigInt>("x_inv"),
Ok(x) if x == bigint_str!(
"19429627790501903254364315669614485084365347064625983303617500144471999752609"
)
);
}
}

#[test]
Expand Down
7 changes: 7 additions & 0 deletions src/tests/cairo_run_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1322,3 +1322,10 @@ fn cairo_run_efficient_secp256r1_ec() {
let program_data = include_bytes!("../../cairo_programs/efficient_secp256r1_ec.json");
run_program_simple(program_data.as_slice());
}

#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn cairo_run_is_zero() {
let program_data = include_bytes!("../../cairo_programs/is_zero.json");
run_program_simple(program_data.as_slice());
}