diff --git a/CHANGELOG.md b/CHANGELOG.md index abfd0a8fa4..9b6ec3562c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -79,6 +79,19 @@ * 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 [#1006](https://github.com/lambdaclass/cairo-rs/pull/1006): + + `BuiltinHintProcessor` now supports the following hint: + + ```python + ids.quad_bit = ( + 8 * ((ids.scalar_v >> ids.m) & 1) + + 4 * ((ids.scalar_u >> ids.m) & 1) + + 2 * ((ids.scalar_v >> (ids.m - 1)) & 1) + + ((ids.scalar_u >> (ids.m - 1)) & 1) + ) + ``` + * Add missing hint on cairo_secp lib [#1003](https://github.com/lambdaclass/cairo-rs/pull/1003): `BuiltinHintProcessor` now supports the following hint: @@ -98,9 +111,10 @@ value = x_inv = div_mod(1, x, SECP_P) ``` -* Add missing hints on cairo_secp lib [#994](https://github.com/lambdaclass/cairo-rs/pull/994):: +* Add missing hints on cairo_secp lib [#994](https://github.com/lambdaclass/cairo-rs/pull/994): `BuiltinHintProcessor` now supports the following hints: + ```python from starkware.cairo.common.cairo_secp.secp_utils import pack from starkware.python.math_utils import div_mod, safe_div diff --git a/cairo_programs/quad_bit.cairo b/cairo_programs/quad_bit.cairo new file mode 100644 index 0000000000..0dab02ae00 --- /dev/null +++ b/cairo_programs/quad_bit.cairo @@ -0,0 +1,31 @@ +%builtins range_check + +func get_quad_bit{range_check_ptr}(scalar_u: felt, scalar_v: felt, m: felt) -> felt { + alloc_locals; + local quad_bit: felt; + %{ + ids.quad_bit = ( + 8 * ((ids.scalar_v >> ids.m) & 1) + + 4 * ((ids.scalar_u >> ids.m) & 1) + + 2 * ((ids.scalar_v >> (ids.m - 1)) & 1) + + ((ids.scalar_u >> (ids.m - 1)) & 1) + ) + %} + return quad_bit; +} + +func test_quad_bit{range_check_ptr}() { + let u = 4194304; // 1 << 22 + let v = 8388608; // 1 << 23 + + // 8 * 1 + 4 * 0 + 2 * 0 + 1 * 1 + assert get_quad_bit(u, v, 23) = 9; + + return (); +} + +func main{range_check_ptr}() { + test_quad_bit(); + + 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 69e0618f84..127198796b 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 @@ -31,7 +31,7 @@ use crate::{ 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, - fast_ec_add_assign_new_y, + fast_ec_add_assign_new_y, quad_bit, }, field_utils::{ is_zero_assign_scope_variables, is_zero_assign_scope_variables_external_const, @@ -537,6 +537,7 @@ 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::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), code => Err(HintError::UnknownHint(code.to_string())), diff --git a/src/hint_processor/builtin_hint_processor/hint_code.rs b/src/hint_processor/builtin_hint_processor/hint_code.rs index b1698eb051..4c16e46f59 100644 --- a/src/hint_processor/builtin_hint_processor/hint_code.rs +++ b/src/hint_processor/builtin_hint_processor/hint_code.rs @@ -819,5 +819,12 @@ 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 QUAD_BIT: &str = r#"ids.quad_bit = ( + 8 * ((ids.scalar_v >> ids.m) & 1) + + 4 * ((ids.scalar_u >> ids.m) & 1) + + 2 * ((ids.scalar_v >> (ids.m - 1)) & 1) + + ((ids.scalar_u >> (ids.m - 1)) & 1) +)"#; + #[cfg(feature = "skip_next_instruction_hint")] pub const SKIP_NEXT_INSTRUCTION: &str = "skip_next_instruction()"; 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 cbcdc37bf0..ff7f312f35 100644 --- a/src/hint_processor/builtin_hint_processor/secp/ec_utils.rs +++ b/src/hint_processor/builtin_hint_processor/secp/ec_utils.rs @@ -2,9 +2,13 @@ use crate::{ hint_processor::{ builtin_hint_processor::{ hint_utils::{ - get_integer_from_var_name, get_relocatable_from_var_name, insert_value_into_ap, + get_integer_from_var_name, get_relocatable_from_var_name, + insert_value_from_var_name, insert_value_into_ap, + }, + secp::{ + bigint_utils::BigInt3, + secp_utils::{pack, SECP_P}, }, - secp::secp_utils::pack, }, hint_processor_definition::HintReference, }, @@ -17,9 +21,7 @@ use crate::{ use felt::Felt252; use num_bigint::BigInt; use num_integer::Integer; -use num_traits::{One, Zero}; - -use super::{bigint_utils::BigInt3, secp_utils::SECP_P}; +use num_traits::{One, ToPrimitive, Zero}; #[derive(Debug, PartialEq)] struct EcPoint<'a> { @@ -271,6 +273,51 @@ pub fn ec_mul_inner( insert_value_into_ap(vm, scalar) } +/* +Implements hint: +%{ + ids.quad_bit = ( + 8 * ((ids.scalar_v >> ids.m) & 1) + + 4 * ((ids.scalar_u >> ids.m) & 1) + + 2 * ((ids.scalar_v >> (ids.m - 1)) & 1) + + ((ids.scalar_u >> (ids.m - 1)) & 1) + ) +%} +*/ +pub fn quad_bit( + vm: &mut VirtualMachine, + ids_data: &HashMap, + ap_tracking: &ApTracking, +) -> Result<(), HintError> { + let scalar_v_cow = get_integer_from_var_name("scalar_v", vm, ids_data, ap_tracking)?; + let scalar_u_cow = get_integer_from_var_name("scalar_u", vm, ids_data, ap_tracking)?; + let m_cow = get_integer_from_var_name("m", vm, ids_data, ap_tracking)?; + + let scalar_v = scalar_v_cow.as_ref(); + let scalar_u = scalar_u_cow.as_ref(); + + // If m is too high the shift result will always be zero + let m = m_cow.as_ref().to_u32().unwrap_or(253); + if m >= 253 { + return insert_value_from_var_name("quad_bit", 0, vm, ids_data, ap_tracking); + } + + let one = &Felt252::one(); + + // 8 * ((ids.scalar_v >> ids.m) & 1) + let quad_bit_3 = ((scalar_v >> m) & one) << 3u32; + // 4 * ((ids.scalar_u >> ids.m) & 1) + let quad_bit_2 = ((scalar_u >> m) & one) << 2u32; + // 2 * ((ids.scalar_v >> (ids.m - 1)) & 1) + let quad_bit_1 = ((scalar_v >> (m - 1)) & one) << 1u32; + // 1 * ((ids.scalar_u >> (ids.m - 1)) & 1) + let quad_bit_0 = (scalar_u >> (m - 1)) & one; + + let res = quad_bit_0 + quad_bit_1 + quad_bit_2 + quad_bit_3; + + insert_value_from_var_name("quad_bit", res, vm, ids_data, ap_tracking) +} + #[cfg(test)] mod tests { use super::*; @@ -808,4 +855,51 @@ mod tests { let r = EcPoint::from_var_name("e", &vm, &ids_data, &ap_tracking); assert_matches!(r, Err(HintError::UnknownIdentifier(x)) if x == "e") } + + #[test] + fn run_quad_bit_ok() { + let hint_code = "ids.quad_bit = (\n 8 * ((ids.scalar_v >> ids.m) & 1)\n + 4 * ((ids.scalar_u >> ids.m) & 1)\n + 2 * ((ids.scalar_v >> (ids.m - 1)) & 1)\n + ((ids.scalar_u >> (ids.m - 1)) & 1)\n)"; + let mut vm = vm_with_range_check!(); + + let scalar_u = 89712; + let scalar_v = 1478396; + let m = 4; + // Insert ids.scalar into memory + vm.segments = segments![((1, 0), scalar_u), ((1, 1), scalar_v), ((1, 2), m)]; + + // Initialize RunContext + run_context!(vm, 0, 4, 4); + + let ids_data = ids_data!["scalar_u", "scalar_v", "m", "quad_bit"]; + + // Execute the hint + assert_matches!(run_hint!(vm, ids_data, hint_code), Ok(())); + + // Check hint memory inserts + check_memory![vm.segments.memory, ((1, 3), 14)]; + } + + #[test] + fn run_quad_bit_with_max_m_ok() { + let hint_code = "ids.quad_bit = (\n 8 * ((ids.scalar_v >> ids.m) & 1)\n + 4 * ((ids.scalar_u >> ids.m) & 1)\n + 2 * ((ids.scalar_v >> (ids.m - 1)) & 1)\n + ((ids.scalar_u >> (ids.m - 1)) & 1)\n)"; + let mut vm = vm_with_range_check!(); + + let scalar_u = 89712; + let scalar_v = 1478396; + // Value is so high the result will always be zero + let m = i128::MAX; + // Insert ids.scalar into memory + vm.segments = segments![((1, 0), scalar_u), ((1, 1), scalar_v), ((1, 2), m)]; + + // Initialize RunContext + run_context!(vm, 0, 4, 4); + + let ids_data = ids_data!["scalar_u", "scalar_v", "m", "quad_bit"]; + + // Execute the hint + assert_matches!(run_hint!(vm, ids_data, hint_code), Ok(())); + + // Check hint memory inserts + check_memory![vm.segments.memory, ((1, 3), 0)]; + } } diff --git a/src/tests/cairo_run_test.rs b/src/tests/cairo_run_test.rs index 216b2b8597..36974cf363 100644 --- a/src/tests/cairo_run_test.rs +++ b/src/tests/cairo_run_test.rs @@ -1350,3 +1350,10 @@ fn cairo_run_is_zero_pack() { let program_data = include_bytes!("../../cairo_programs/is_zero_pack.json"); run_program_simple(program_data.as_slice()); } + +#[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +fn cairo_run_quad_bit() { + let program_data = include_bytes!("../../cairo_programs/quad_bit.json"); + run_program_simple(program_data.as_slice()); +}