From 5104becf239fa1a9241ec22cd989e42fbff6f704 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s?= Date: Mon, 17 Apr 2023 18:03:53 -0300 Subject: [PATCH 1/6] Add NewHint#13 --- cairo_programs/quad_bit.cairo | 31 ++++++++ .../builtin_hint_processor_definition.rs | 3 +- .../builtin_hint_processor/hint_code.rs | 7 ++ .../builtin_hint_processor/secp/ec_utils.rs | 77 +++++++++++++++++-- src/tests/cairo_run_test.rs | 7 ++ 5 files changed, 119 insertions(+), 6 deletions(-) create mode 100644 cairo_programs/quad_bit.cairo 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 5dc58ab18c..9de12a8795 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_nondet, is_zero_pack, reduce, @@ -522,6 +522,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 872a6ec5cf..636dcbf928 100644 --- a/src/hint_processor/builtin_hint_processor/hint_code.rs +++ b/src/hint_processor/builtin_hint_processor/hint_code.rs @@ -776,5 +776,12 @@ ids.root.d1 = root_split[1] ids.root.d2 = root_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 dc5822d28f..ce888c5829 100644 --- a/src/hint_processor/builtin_hint_processor/secp/ec_utils.rs +++ b/src/hint_processor/builtin_hint_processor/secp/ec_utils.rs @@ -3,9 +3,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, }, @@ -18,9 +22,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> { @@ -272,6 +274,48 @@ 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(); + let Some(m) = m_cow.as_ref().to_u32() else { + 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::*; @@ -809,4 +853,27 @@ 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)]; + } } diff --git a/src/tests/cairo_run_test.rs b/src/tests/cairo_run_test.rs index 465d51e8f0..2fec5dff5d 100644 --- a/src/tests/cairo_run_test.rs +++ b/src/tests/cairo_run_test.rs @@ -1329,3 +1329,10 @@ fn cairo_run_div_mod_n() { let program_data = include_bytes!("../../cairo_programs/div_mod_n.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()); +} From b93268ea042f973b62f07282e12a43731e8e7cf7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s?= Date: Tue, 18 Apr 2023 11:25:29 -0300 Subject: [PATCH 2/6] Update changelog --- CHANGELOG.md | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e6e11d7e2d..9d481fa2df 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,9 +31,23 @@ * 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 hints on cairo_secp lib [#994](https://github.com/lambdaclass/cairo-rs/pull/994):: +* 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 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 From 368cc3db15e1c5289ec82f7defc4b0fd48e59467 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s?= Date: Tue, 18 Apr 2023 12:17:27 -0300 Subject: [PATCH 3/6] Add test for when m is too high --- .../builtin_hint_processor/secp/ec_utils.rs | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) 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 ce888c5829..6e41d30169 100644 --- a/src/hint_processor/builtin_hint_processor/secp/ec_utils.rs +++ b/src/hint_processor/builtin_hint_processor/secp/ec_utils.rs @@ -296,6 +296,8 @@ pub fn quad_bit( 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 Some(m) = m_cow.as_ref().to_u32() else { return insert_value_from_var_name("quad_bit", 0, vm, ids_data, ap_tracking); }; @@ -876,4 +878,28 @@ mod tests { // 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)]; + } } From 12cc28d60111fe12adfd77b3065eec8b6963d22c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s?= Date: Tue, 18 Apr 2023 13:38:41 -0300 Subject: [PATCH 4/6] Restrict the guard clause in quad_bit --- src/hint_processor/builtin_hint_processor/secp/ec_utils.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) 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 6e41d30169..eecef4767f 100644 --- a/src/hint_processor/builtin_hint_processor/secp/ec_utils.rs +++ b/src/hint_processor/builtin_hint_processor/secp/ec_utils.rs @@ -298,8 +298,11 @@ pub fn quad_bit( let scalar_u = scalar_u_cow.as_ref(); // If m is too high the shift result will always be zero - let Some(m) = m_cow.as_ref().to_u32() else { - return insert_value_from_var_name("quad_bit", 0, vm, ids_data, ap_tracking); + let m = match m_cow.as_ref().to_u32() { + Some(m) if m <= 252 => m, + None | Some(_) => { + return insert_value_from_var_name("quad_bit", 0, vm, ids_data, ap_tracking) + } }; let one = &Felt252::one(); From de1dddbbc8d12b12128632555689e996ddd8a8d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s?= <47506558+MegaRedHand@users.noreply.github.com> Date: Tue, 18 Apr 2023 15:47:59 -0300 Subject: [PATCH 5/6] Simplify match with unwrap_or Feels dirty! Co-authored-by: Mario Rugiero --- .../builtin_hint_processor/secp/ec_utils.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) 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 eecef4767f..7a47dbc30f 100644 --- a/src/hint_processor/builtin_hint_processor/secp/ec_utils.rs +++ b/src/hint_processor/builtin_hint_processor/secp/ec_utils.rs @@ -298,11 +298,10 @@ pub fn quad_bit( let scalar_u = scalar_u_cow.as_ref(); // If m is too high the shift result will always be zero - let m = match m_cow.as_ref().to_u32() { - Some(m) if m <= 252 => m, - None | Some(_) => { - return insert_value_from_var_name("quad_bit", 0, vm, ids_data, ap_tracking) - } + let m = match 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(); From b329c5012d9bebb0237d82df4937f9f9f0b0fe56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s?= Date: Tue, 18 Apr 2023 15:53:06 -0300 Subject: [PATCH 6/6] Fix build errors --- src/hint_processor/builtin_hint_processor/secp/ec_utils.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) 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 7a47dbc30f..1823d44b07 100644 --- a/src/hint_processor/builtin_hint_processor/secp/ec_utils.rs +++ b/src/hint_processor/builtin_hint_processor/secp/ec_utils.rs @@ -298,11 +298,10 @@ pub fn quad_bit( let scalar_u = scalar_u_cow.as_ref(); // If m is too high the shift result will always be zero - let m = match m_cow.as_ref().to_u32().unwrap_or(253); + 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); + return insert_value_from_var_name("quad_bit", 0, vm, ids_data, ap_tracking); } - }; let one = &Felt252::one();