diff --git a/CHANGELOG.md b/CHANGELOG.md index 9b6ec3562c..bda3fad616 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,14 @@ #### Upcoming Changes +* Add missing hint on cairo_secp lib [#1008](https://github.com/lambdaclass/cairo-rs/pull/1008): + + `BuiltinHintProcessor` now supports the following hint: + + ```python + ids.len_hi = max(ids.scalar_u.d2.bit_length(), ids.scalar_v.d2.bit_length())-1 + ``` + * Update `starknet-crypto` to version `0.4.3` [#1011](https://github.com/lambdaclass/cairo-rs/pull/1011) * The new version carries an 85% reduction in execution time for ECDSA signature verification diff --git a/cairo_programs/highest_bitlen.cairo b/cairo_programs/highest_bitlen.cairo new file mode 100644 index 0000000000..d0b21807be --- /dev/null +++ b/cairo_programs/highest_bitlen.cairo @@ -0,0 +1,30 @@ +%builtins range_check + +from starkware.cairo.common.cairo_secp.bigint import BigInt3 + +func get_highlen{range_check_ptr}(scalar_u: BigInt3, scalar_v: BigInt3) -> felt { + alloc_locals; + local len_hi; + + %{ ids.len_hi = max(ids.scalar_u.d2.bit_length(), ids.scalar_v.d2.bit_length())-1 %} + + return len_hi; +} + +func test_highest_len{range_check_ptr}() { + assert get_highlen(BigInt3(0, 0, 8), BigInt3(0, 0, 0)) = 3; + assert get_highlen(BigInt3(0, 0, 0), BigInt3(0, 0, 1)) = 0; + assert get_highlen(BigInt3(0, 0, 2), BigInt3(0, 0, 1)) = 1; + + // This overflows + let res = get_highlen(BigInt3(0, 0, 0), BigInt3(0, 0, 0)); + assert res = 3618502788666131213697322783095070105623107215331596699973092056135872020480; + + return (); +} + +func main{range_check_ptr}() { + test_highest_len(); + + 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 127198796b..6c279e81ab 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 @@ -27,7 +27,7 @@ use crate::{ poseidon_utils::{n_greater_than_10, n_greater_than_2}, pow_utils::pow, secp::{ - bigint_utils::{bigint_to_uint256, nondet_bigint3}, + bigint_utils::{bigint_to_uint256, hi_max_bitlen, nondet_bigint3}, ec_utils::{ compute_doubling_slope, compute_slope, ec_double_assign_new_x, ec_double_assign_new_y, ec_mul_inner, ec_negate, fast_ec_add_assign_new_x, @@ -62,6 +62,7 @@ use crate::{ add_no_uint384_check, uint384_signed_nn, uint384_split_128, uint384_sqrt, uint384_unsigned_div_rem, uint384_unsigned_div_rem_expanded, }, + uint384_extension::unsigned_div_rem_uint768_by_uint384, usort::{ usort_body, usort_enter_scope, verify_multiplicity_assert, verify_multiplicity_body, verify_usort, @@ -79,8 +80,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::uint384_extension::unsigned_div_rem_uint768_by_uint384; - pub struct HintProcessorData { pub code: String, pub ap_tracking: ApTracking, @@ -537,6 +536,9 @@ impl HintProcessor for BuiltinHintProcessor { hint_code::UINT256_MUL_DIV_MOD => { uint256_mul_div_mod(vm, &hint_data.ids_data, &hint_data.ap_tracking) } + hint_code::HI_MAX_BITLEN => { + hi_max_bitlen(vm, &hint_data.ids_data, &hint_data.ap_tracking) + } hint_code::QUAD_BIT => quad_bit(vm, &hint_data.ids_data, &hint_data.ap_tracking), #[cfg(feature = "skip_next_instruction_hint")] hint_code::SKIP_NEXT_INSTRUCTION => skip_next_instruction(vm), diff --git a/src/hint_processor/builtin_hint_processor/hint_code.rs b/src/hint_processor/builtin_hint_processor/hint_code.rs index 4c16e46f59..4b23fe2ae8 100644 --- a/src/hint_processor/builtin_hint_processor/hint_code.rs +++ b/src/hint_processor/builtin_hint_processor/hint_code.rs @@ -819,6 +819,9 @@ ids.remainder.d1 = remainder_split[1] ids.remainder.d2 = remainder_split[2]"; pub const UINT384_SIGNED_NN: &str = "memory[ap] = 1 if 0 <= (ids.a.d2 % PRIME) < 2 ** 127 else 0"; +pub const HI_MAX_BITLEN: &str = + "ids.len_hi = max(ids.scalar_u.d2.bit_length(), ids.scalar_v.d2.bit_length())-1"; + pub const QUAD_BIT: &str = r#"ids.quad_bit = ( 8 * ((ids.scalar_v >> ids.m) & 1) + 4 * ((ids.scalar_u >> ids.m) & 1) diff --git a/src/hint_processor/builtin_hint_processor/secp/bigint_utils.rs b/src/hint_processor/builtin_hint_processor/secp/bigint_utils.rs index cdba93b806..d27c8007be 100644 --- a/src/hint_processor/builtin_hint_processor/secp/bigint_utils.rs +++ b/src/hint_processor/builtin_hint_processor/secp/bigint_utils.rs @@ -15,6 +15,7 @@ use crate::{ vm::{errors::hint_errors::HintError, vm_core::VirtualMachine}, }; use felt::Felt252; +use num_traits::Bounded; #[derive(Debug, PartialEq)] pub(crate) struct BigInt3<'a> { @@ -100,6 +101,31 @@ pub fn bigint_to_uint256( insert_value_from_var_name("low", low, vm, ids_data, ap_tracking) } +// Implements hint +// %{ ids.len_hi = max(ids.scalar_u.d2.bit_length(), ids.scalar_v.d2.bit_length())-1 %} +pub fn hi_max_bitlen( + vm: &mut VirtualMachine, + ids_data: &HashMap, + ap_tracking: &ApTracking, +) -> Result<(), HintError> { + let scalar_u = BigInt3::from_var_name("scalar_u", vm, ids_data, ap_tracking)?; + let scalar_v = BigInt3::from_var_name("scalar_v", vm, ids_data, ap_tracking)?; + + let len_hi_u = scalar_u.d2.bits(); + let len_hi_v = scalar_v.d2.bits(); + + let len_hi = len_hi_u.max(len_hi_v); + + // equal to `len_hi.wrapping_sub(1)` + let res = if len_hi == 0 { + Felt252::max_value() + } else { + (len_hi - 1).into() + }; + + insert_value_from_var_name("len_hi", res, vm, ids_data, ap_tracking) +} + #[cfg(test)] mod tests { use super::*; @@ -246,4 +272,29 @@ mod tests { let r = BigInt3::from_var_name("x", &vm, &ids_data, &ApTracking::default()); assert_matches!(r, Err(HintError::UnknownIdentifier(x)) if x == "x") } + + #[test] + fn run_hi_max_bitlen_ok() { + let hint_code = + "ids.len_hi = max(ids.scalar_u.d2.bit_length(), ids.scalar_v.d2.bit_length())-1"; + + let mut vm = vm_with_range_check!(); + + // Initialize RunContext + run_context!(vm, 0, 7, 0); + + vm.segments = segments![ + ((1, 0), 0), + ((1, 1), 0), + ((1, 2), 1), + ((1, 3), 0), + ((1, 4), 0), + ((1, 5), 1) + ]; + // Create hint_data + let ids_data = non_continuous_ids_data![("scalar_u", 0), ("scalar_v", 3), ("len_hi", 6)]; + assert!(run_hint!(vm, ids_data, hint_code, exec_scopes_ref!()).is_ok()); + //Check hint memory inserts + check_memory![vm.segments.memory, ((1, 6), 0)]; + } } diff --git a/src/tests/cairo_run_test.rs b/src/tests/cairo_run_test.rs index 36974cf363..de2d2951f5 100644 --- a/src/tests/cairo_run_test.rs +++ b/src/tests/cairo_run_test.rs @@ -1357,3 +1357,10 @@ fn cairo_run_quad_bit() { let program_data = include_bytes!("../../cairo_programs/quad_bit.json"); run_program_simple(program_data.as_slice()); } + +#[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +fn cairo_run_highest_bitlen() { + let program_data = include_bytes!("../../cairo_programs/highest_bitlen.json"); + run_program_simple(program_data.as_slice()); +}