From 14cee591ddba720917ba199c718165c635e5db06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s?= <47506558+MegaRedHand@users.noreply.github.com> Date: Wed, 19 Apr 2023 22:24:18 -0300 Subject: [PATCH] feat(hints): add NewHint#32 (#1010) * Add NewHint#32 * Update changelog * Add rstest dependency and use it * Trigger benchmarks at @Oppen's request * Disable default features in rstest This disables async-timeout, that's only used for async tests. --------- Co-authored-by: Mario Rugiero --- CHANGELOG.md | 7 +++ Cargo.lock | 1 + Cargo.toml | 2 + cairo_programs/is_zero.cairo | 50 +++++++++++++++++ .../builtin_hint_processor_definition.rs | 2 +- .../builtin_hint_processor/hint_code.rs | 1 + .../secp/field_utils.rs | 55 ++++++++++--------- 7 files changed, 92 insertions(+), 26 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 15e5034f35..99cecdcd7e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,13 @@ #### Upcoming Changes +* Add missing hint on cairo_secp lib [#1010](https://github.com/lambdaclass/cairo-rs/pull/1010): + + `BuiltinHintProcessor` now supports the following hint: + + ```python + memory[ap] = int(x == 0) + ``` * Implement hint on `get_felt_bitlength` [#993](https://github.com/lambdaclass/cairo-rs/pull/993) diff --git a/Cargo.lock b/Cargo.lock index 5512e91856..80bf85a28f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -217,6 +217,7 @@ dependencies = [ "num-traits", "proptest", "rand_core", + "rstest", "rusty-hook", "serde", "serde_bytes", diff --git a/Cargo.toml b/Cargo.toml index e00bd25125..4d9d2525ed 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -81,11 +81,13 @@ bitvec = { version = "1", default-features = false, features = ["alloc"] } [target.'cfg(target_arch = "wasm32")'.dev-dependencies] wasm-bindgen-test = "0.3.34" assert_matches = "1.5.0" +rstest = { version = "0.17.0", default-features = false } [target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies] iai = "0.1" rusty-hook = "0.11" assert_matches = "1.5.0" +rstest = { version = "0.17.0", default-features = false } criterion = { version = "0.3", features = ["html_reports"] } proptest = "1.0.0" diff --git a/cairo_programs/is_zero.cairo b/cairo_programs/is_zero.cairo index edebe50b97..aeb2607045 100644 --- a/cairo_programs/is_zero.cairo +++ b/cairo_programs/is_zero.cairo @@ -31,6 +31,34 @@ func is_zero{range_check_ptr}(x: BigInt3) -> (res: felt) { return (res=0); } +func is_zero_alt{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 + %} + %{ memory[ap] = int(x == 0) %} + tempvar x_is_zero; + + if (x_is_zero != 0) { + verify_zero(UnreducedBigInt3(d0=x.d0, d1=x.d1, d2=x.d2)); + return (res=1); + } + + %{ + 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) + %} + 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); @@ -52,8 +80,30 @@ func test_is_zero{range_check_ptr}() -> () { return (); } +func test_is_zero_alt{range_check_ptr}() -> () { + let zero = BigInt3(0, 0, 0); + + let (res: felt) = is_zero_alt(zero); + assert res = 1; + + let one = BigInt3(1, 0, 0); + + let (res: felt) = is_zero_alt(one); + assert res = 0; + + let secp256k1_prime = BigInt3( + 77371252455336262886226991, 77371252455336267181195263, 19342813113834066795298815 + ); + + let (res: felt) = is_zero_alt(secp256k1_prime); + assert res = 1; + + return (); +} + func main{range_check_ptr}() -> () { test_is_zero(); + test_is_zero_alt(); 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 50bba6935a..130c750e49 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 @@ -352,13 +352,13 @@ impl HintProcessor for BuiltinHintProcessor { hint_code::IS_ZERO_PACK => { is_zero_pack(vm, exec_scopes, &hint_data.ids_data, &hint_data.ap_tracking) } + hint_code::IS_ZERO_NONDET | hint_code::IS_ZERO_INT => is_zero_nondet(vm, exec_scopes), hint_code::IS_ZERO_PACK_EXTERNAL_SECP => is_zero_pack_external_secp( vm, exec_scopes, &hint_data.ids_data, &hint_data.ap_tracking, ), - 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) diff --git a/src/hint_processor/builtin_hint_processor/hint_code.rs b/src/hint_processor/builtin_hint_processor/hint_code.rs index a603ded506..1bcc1c35fb 100644 --- a/src/hint_processor/builtin_hint_processor/hint_code.rs +++ b/src/hint_processor/builtin_hint_processor/hint_code.rs @@ -432,6 +432,7 @@ ids.high = int.from_bytes(hashed[:16], 'big') ids.low = int.from_bytes(hashed[16:32], 'big')"#; pub const IS_ZERO_NONDET: &str = "memory[ap] = to_felt_or_relocatable(x == 0)"; +pub const IS_ZERO_INT: &str = "memory[ap] = int(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"#; 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 e321801b9a..0234487d68 100644 --- a/src/hint_processor/builtin_hint_processor/secp/field_utils.rs +++ b/src/hint_processor/builtin_hint_processor/secp/field_utils.rs @@ -216,6 +216,7 @@ mod tests { }; use assert_matches::assert_matches; + use rstest::rstest; #[cfg(target_arch = "wasm32")] use wasm_bindgen_test::*; @@ -448,10 +449,11 @@ mod tests { ); } - #[test] + #[rstest] + #[case(hint_code::IS_ZERO_NONDET)] + #[case(hint_code::IS_ZERO_INT)] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] - fn run_is_zero_nondet_ok_true() { - let hint_code = "memory[ap] = to_felt_or_relocatable(x == 0)"; + fn run_is_zero_nondet_ok_true(#[case] hint_code: &str) { let mut vm = vm_with_range_check!(); //Initialize memory @@ -475,10 +477,11 @@ mod tests { check_memory!(vm.segments.memory, ((1, 15), 1)); } - #[test] + #[rstest] + #[case(hint_code::IS_ZERO_NONDET)] + #[case(hint_code::IS_ZERO_INT)] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] - fn run_is_zero_nondet_ok_false() { - let hint_code = "memory[ap] = to_felt_or_relocatable(x == 0)"; + fn run_is_zero_nondet_ok_false(#[case] hint_code: &str) { let mut vm = vm_with_range_check!(); //Initialize memory @@ -502,10 +505,11 @@ mod tests { check_memory!(vm.segments.memory, ((1, 15), 0)); } - #[test] + #[rstest] + #[case(hint_code::IS_ZERO_NONDET)] + #[case(hint_code::IS_ZERO_INT)] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] - fn run_is_zero_nondet_scope_error() { - let hint_code = "memory[ap] = to_felt_or_relocatable(x == 0)"; + fn run_is_zero_nondet_scope_error(#[case] hint_code: &str) { let mut vm = vm_with_range_check!(); //Initialize memory @@ -523,10 +527,11 @@ mod tests { ); } - #[test] + #[rstest] + #[case(hint_code::IS_ZERO_NONDET)] + #[case(hint_code::IS_ZERO_INT)] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] - fn run_is_zero_nondet_invalid_memory_insert() { - let hint_code = "memory[ap] = to_felt_or_relocatable(x == 0)"; + fn run_is_zero_nondet_invalid_memory_insert(#[case] hint_code: &str) { let mut vm = vm_with_range_check!(); //Insert a value in ap before the hint execution, so the hint memory insert fails @@ -540,18 +545,17 @@ mod tests { exec_scopes.assign_or_update_variable("x", any_box!(BigInt::zero())); //Execute the hint assert_matches!( - run_hint!(vm, HashMap::new(), hint_code, &mut exec_scopes), - Err(HintError::Memory( - MemoryError::InconsistentMemory( - x, - y, - z - ) - )) if x == - vm.run_context.get_ap() && - y == MaybeRelocatable::from(Felt252::new(55i32)) && - z == MaybeRelocatable::from(Felt252::new(1i32)) - ); + run_hint!(vm, HashMap::new(), hint_code, &mut exec_scopes), + Err(HintError::Memory( + MemoryError::InconsistentMemory( + x, + y, + z + ) + )) if x == vm.run_context.get_ap() + && y == MaybeRelocatable::from(Felt252::new(55i32)) + && z == MaybeRelocatable::from(Felt252::new(1i32)) + ); } #[test] @@ -560,6 +564,7 @@ mod tests { let mut exec_scopes = ExecutionScopes::new(); let hint_codes = vec![ hint_code::IS_ZERO_ASSIGN_SCOPE_VARS, + // NOTE: this one requires IS_ZERO_ASSIGN_SCOPE_VARS to execute first. hint_code::IS_ZERO_ASSIGN_SCOPE_VARS_EXTERNAL_SECP, ]; @@ -597,7 +602,7 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn is_zero_assign_scope_variables_scope_error() { - 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 hint_code = hint_code::IS_ZERO_ASSIGN_SCOPE_VARS; let mut vm = vm_with_range_check!(); //Skip `x` assignment //Execute the hint