From 8ce29ccfeb8bba7af703b7a2b0f563278a82e126 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s?= <47506558+MegaRedHand@users.noreply.github.com> Date: Mon, 17 Apr 2023 16:41:02 -0300 Subject: [PATCH] feat(hints): add NewHint#26 (#992) * Add NewHint#26 * Update changelog * Change assert_matches for assert + is_ok * Get and insert SECP_P from scope where applicable * Appease clippy --- CHANGELOG.md | 12 +++ cairo_programs/ed25519_field.cairo | 54 +++++++++++++ .../builtin_hint_processor_definition.rs | 24 +++--- .../builtin_hint_processor/hint_code.rs | 6 ++ .../builtin_hint_processor/secp/ec_utils.rs | 8 +- .../secp/field_utils.rs | 81 ++++++++++++++----- .../builtin_hint_processor/secp/signature.rs | 2 + 7 files changed, 156 insertions(+), 31 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 191b3be9eb..a09ff06ad4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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: diff --git a/cairo_programs/ed25519_field.cairo b/cairo_programs/ed25519_field.cairo index 955de5f811..cf5ddb06bd 100644 --- a/cairo_programs/ed25519_field.cairo +++ b/cairo_programs/ed25519_field.cairo @@ -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); @@ -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 (); } 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 d5684d481c..7383370e99 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 @@ -1,5 +1,3 @@ -use crate::stdlib::{any::Any, collections::HashMap, prelude::*, rc::Rc}; - use crate::{ hint_processor::{ builtin_hint_processor::{ @@ -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::{ @@ -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, @@ -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, @@ -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}, }; @@ -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, @@ -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) } diff --git a/src/hint_processor/builtin_hint_processor/hint_code.rs b/src/hint_processor/builtin_hint_processor/hint_code.rs index 13597204ae..6131ead533 100644 --- a/src/hint_processor/builtin_hint_processor/hint_code.rs +++ b/src/hint_processor/builtin_hint_processor/hint_code.rs @@ -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"#; 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 049c721d54..dc5822d28f 100644 --- a/src/hint_processor/builtin_hint_processor/secp/ec_utils.rs +++ b/src/hint_processor/builtin_hint_processor/secp/ec_utils.rs @@ -1,5 +1,5 @@ -use crate::stdlib::{collections::HashMap, ops::BitAnd, prelude::*}; use crate::{ + any_box, hint_processor::{ builtin_hint_processor::{ hint_utils::{ @@ -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}, }; @@ -58,6 +59,7 @@ pub fn ec_negate( ids_data: &HashMap, 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)?; @@ -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)?; @@ -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 @@ -150,6 +154,7 @@ pub fn ec_double_assign_new_x( ids_data: &HashMap, 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 @@ -208,6 +213,7 @@ pub fn fast_ec_add_assign_new_x( ids_data: &HashMap, 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 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 9ee1a3598b..cf9afb1d68 100644 --- a/src/hint_processor/builtin_hint_processor/secp/field_utils.rs +++ b/src/hint_processor/builtin_hint_processor/secp/field_utils.rs @@ -1,3 +1,4 @@ +use crate::any_box; use crate::stdlib::{collections::HashMap, prelude::*}; use crate::{ @@ -30,9 +31,11 @@ Implements hint: */ pub fn verify_zero( vm: &mut VirtualMachine, + exec_scopes: &mut ExecutionScopes, ids_data: &HashMap, 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() { @@ -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, + 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: %{ @@ -56,6 +85,7 @@ pub fn reduce( ids_data: &HashMap, 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(()) @@ -75,6 +105,8 @@ pub fn is_zero_pack( ids_data: &HashMap, 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); @@ -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::("x")?; @@ -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] diff --git a/src/hint_processor/builtin_hint_processor/secp/signature.rs b/src/hint_processor/builtin_hint_processor/secp/signature.rs index d83f2c4ad8..84dd9e5979 100644 --- a/src/hint_processor/builtin_hint_processor/secp/signature.rs +++ b/src/hint_processor/builtin_hint_processor/secp/signature.rs @@ -1,3 +1,4 @@ +use crate::any_box; use crate::stdlib::{collections::HashMap, ops::Shr, prelude::*}; use crate::{ @@ -87,6 +88,7 @@ pub fn get_point_from_x( ap_tracking: &ApTracking, constants: &HashMap, ) -> Result<(), HintError> { + exec_scopes.assign_or_update_variable("SECP_P", any_box!(SECP_P.clone())); #[allow(deprecated)] let beta = constants .get(BETA)