From a7332983b3461049a19be2e4d67ad7cd87b3788e Mon Sep 17 00:00:00 2001 From: fmoletta <99273364+fmoletta@users.noreply.github.com> Date: Fri, 28 Apr 2023 18:35:35 +0300 Subject: [PATCH] feat(hints): Implement NewHint#46 (#1055) * Add hint code * Start implementing hint * Implement hint * Fix hint * Add integration test * Add changelog entry * Expand test * Update CHANGELOG.md --------- Co-authored-by: Pedro Fontana --- CHANGELOG.md | 20 ++++++ cairo_programs/split_xx_hint.cairo | 36 +++++++++++ .../builtin_hint_processor_definition.rs | 1 + .../builtin_hint_processor/hint_code.rs | 11 ++++ .../builtin_hint_processor/math_utils.rs | 62 ++++++++++++++++++- src/tests/cairo_run_test.rs | 7 +++ 6 files changed, 136 insertions(+), 1 deletion(-) create mode 100644 cairo_programs/split_xx_hint.cairo diff --git a/CHANGELOG.md b/CHANGELOG.md index 6d45ab1f61..7e80362f45 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,26 @@ #### Upcoming Changes +* Add missing hint on vrf.json whitelist [#1055](https://github.com/lambdaclass/cairo-rs/pull/1055): + + `BuiltinHintProcessor` now supports the following hint: + + ```python + %{ + PRIME = 2**255 - 19 + II = pow(2, (PRIME - 1) // 4, PRIME) + + xx = ids.xx.low + (ids.xx.high<<128) + x = pow(xx, (PRIME + 3) // 8, PRIME) + if (x * x - xx) % PRIME != 0: + x = (x * II) % PRIME + if x % 2 != 0: + x = PRIME - x + ids.x.low = x & ((1<<128)-1) + ids.x.high = x >> 128 + %} + ``` + * Implement hint variant for finalize_blake2s[#1072](https://github.com/lambdaclass/cairo-rs/pull/1072) `BuiltinHintProcessor` now supports the following hint: diff --git a/cairo_programs/split_xx_hint.cairo b/cairo_programs/split_xx_hint.cairo new file mode 100644 index 0000000000..33c4332805 --- /dev/null +++ b/cairo_programs/split_xx_hint.cairo @@ -0,0 +1,36 @@ +from starkware.cairo.common.uint256 import Uint256 + +func split_xx(xx: Uint256) -> Uint256{ + alloc_locals; + local x: Uint256; + %{ + PRIME = 2**255 - 19 + II = pow(2, (PRIME - 1) // 4, PRIME) + + xx = ids.xx.low + (ids.xx.high<<128) + x = pow(xx, (PRIME + 3) // 8, PRIME) + if (x * x - xx) % PRIME != 0: + x = (x * II) % PRIME + if x % 2 != 0: + x = PRIME - x + ids.x.low = x & ((1<<128)-1) + ids.x.high = x >> 128 + %} + return x; +} + +func main() { + let xx: Uint256 = Uint256(7, 17); + let x = split_xx(xx); + + assert x.low = 316161011683971866381321160306766491472; + assert x.high = 30265492890921847871084892076606437231; + + let bb: Uint256 = Uint256(1, 1); + let b = split_xx(bb); + + assert b.low = 60511716334934151406684885798996722026; + assert b.high = 47999103702266454150683157633393234489; + + 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 2cd865df4f..337329fe98 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 @@ -758,6 +758,7 @@ impl HintProcessor for BuiltinHintProcessor { ec_recover_product_mod(vm, exec_scopes, &hint_data.ids_data, &hint_data.ap_tracking) } hint_code::EC_RECOVER_PRODUCT_DIV_M => ec_recover_product_div_m(exec_scopes), + hint_code::SPLIT_XX => split_xx(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 fc3eb17066..b4fefe6ee3 100644 --- a/src/hint_processor/builtin_hint_processor/hint_code.rs +++ b/src/hint_processor/builtin_hint_processor/hint_code.rs @@ -1361,5 +1361,16 @@ ids.b_inverse_mod_p.high = b_inverse_mod_p_split[1]"#; pub const EC_RECOVER_PRODUCT_DIV_M: &str = "value = k = product // m"; +pub const SPLIT_XX: &str = "PRIME = 2**255 - 19 +II = pow(2, (PRIME - 1) // 4, PRIME) + +xx = ids.xx.low + (ids.xx.high<<128) +x = pow(xx, (PRIME + 3) // 8, PRIME) +if (x * x - xx) % PRIME != 0: + x = (x * II) % PRIME +if x % 2 != 0: + x = PRIME - x +ids.x.low = x & ((1<<128)-1) +ids.x.high = x >> 128"; #[cfg(feature = "skip_next_instruction_hint")] pub const SKIP_NEXT_INSTRUCTION: &str = "skip_next_instruction()"; diff --git a/src/hint_processor/builtin_hint_processor/math_utils.rs b/src/hint_processor/builtin_hint_processor/math_utils.rs index 04e291ec97..81a239f3f8 100644 --- a/src/hint_processor/builtin_hint_processor/math_utils.rs +++ b/src/hint_processor/builtin_hint_processor/math_utils.rs @@ -3,6 +3,7 @@ use crate::stdlib::{ ops::{Shl, Shr}, prelude::*, }; +use lazy_static::lazy_static; use num_traits::{Bounded, Pow}; use crate::utils::CAIRO_PRIME; @@ -30,7 +31,10 @@ use num_integer::Integer; use num_traits::One; use num_traits::{Signed, Zero}; -use super::hint_utils::get_maybe_relocatable_from_var_name; +use super::{ + hint_utils::{get_maybe_relocatable_from_var_name, get_relocatable_from_var_name}, + uint256_utils::Uint256, +}; const ADDR_BOUND: &str = "starkware.starknet.common.storage.ADDR_BOUND"; @@ -702,6 +706,62 @@ pub fn a_b_bitand_1( insert_value_from_var_name("b_lsb", b_lsb, vm, ids_data, ap_tracking) } +lazy_static! { + static ref SPLIT_XX_PRIME: BigUint = BigUint::parse_bytes( + b"57896044618658097711785492504343953926634992332820282019728792003956564819949", + 10 + ) + .unwrap(); + static ref II: BigUint = BigUint::parse_bytes( + b"19681161376707505956807079304988542015446066515923890162744021073123829784752", + 10 + ) + .unwrap(); +} + +/* Implements hint: + PRIME = 2**255 - 19 + II = pow(2, (PRIME - 1) // 4, PRIME) + + xx = ids.xx.low + (ids.xx.high<<128) + x = pow(xx, (PRIME + 3) // 8, PRIME) + if (x * x - xx) % PRIME != 0: + x = (x * II) % PRIME + if x % 2 != 0: + x = PRIME - x + ids.x.low = x & ((1<<128)-1) + ids.x.high = x >> 128 + + Note: doesnt belong to and is not variation of any hint from common/math +*/ +pub fn split_xx( + vm: &mut VirtualMachine, + ids_data: &HashMap, + ap_tracking: &ApTracking, +) -> Result<(), HintError> { + let xx = Uint256::from_var_name("xx", vm, ids_data, ap_tracking)?; + let x_addr = get_relocatable_from_var_name("x", vm, ids_data, ap_tracking)?; + let xx = xx.low.to_biguint() + (xx.high.to_biguint() << 128_u32); + let mut x = xx.modpow( + &(&*SPLIT_XX_PRIME + 3_u32).div_floor(&BigUint::from(8_u32)), + &SPLIT_XX_PRIME, + ); + if !(&x * &x - xx).mod_floor(&SPLIT_XX_PRIME).is_zero() { + x = (&x * &*II).mod_floor(&SPLIT_XX_PRIME) + }; + if !x.mod_floor(&2_u32.into()).is_zero() { + x = &*SPLIT_XX_PRIME - x; + } + + vm.insert_value( + x_addr, + Felt252::from(&x & &BigUint::from(u128::max_value())), + )?; + vm.insert_value((x_addr + 1)?, Felt252::from(x >> 128_u32))?; + + Ok(()) +} + #[cfg(test)] mod tests { use super::*; diff --git a/src/tests/cairo_run_test.rs b/src/tests/cairo_run_test.rs index b43b6642c3..fcdeac8b8f 100644 --- a/src/tests/cairo_run_test.rs +++ b/src/tests/cairo_run_test.rs @@ -912,6 +912,13 @@ fn ec_double_assign_new_x_v3() { run_program_simple(program_data.as_slice()); } +#[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +fn split_xx_hint() { + let program_data = include_bytes!("../../cairo_programs/split_xx_hint.json"); + run_program_simple(program_data.as_slice()); +} + #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn nondet_bigint3_v2() {