diff --git a/CHANGELOG.md b/CHANGELOG.md index 6770935dce..6cc325fc45 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,8 @@ * feat: Add `ProverInfo` and extract the relevant information for it from the runner [#2001](https://github.com/lambdaclass/cairo-vm/pull/2001) +* feat: Support hints for new blake felt serialization library code [#1994](https://github.com/lambdaclass/cairo-vm/pull/1994) + #### [2.0.1] - 2025-03-17 * feat: Limited padding of builtin segments to >=16 [#1981](https://github.com/lambdaclass/cairo-vm/pull/1981) diff --git a/vm/src/hint_processor/builtin_hint_processor/blake2s_utils.rs b/vm/src/hint_processor/builtin_hint_processor/blake2s_utils.rs index 3251cff0e8..8dfcedf030 100644 --- a/vm/src/hint_processor/builtin_hint_processor/blake2s_utils.rs +++ b/vm/src/hint_processor/builtin_hint_processor/blake2s_utils.rs @@ -1,3 +1,4 @@ +use crate::hint_processor::hint_processor_utils::felt_to_usize; use crate::stdlib::{borrow::Cow, collections::HashMap, prelude::*}; use crate::types::errors::math_errors::MathError; @@ -17,9 +18,11 @@ use crate::{ vm::{errors::hint_errors::HintError, vm_core::VirtualMachine}, }; +use num_bigint::BigUint; +use num_integer::Integer; use num_traits::ToPrimitive; -use super::hint_utils::get_integer_from_var_name; +use super::hint_utils::{get_integer_from_var_name, insert_value_into_ap}; fn get_fixed_size_u32_array( h_range: &Vec>, @@ -242,6 +245,84 @@ pub fn blake2s_add_uint256_bigend( Ok(()) } +/* Implements Hint: +memory[ap] = (ids.end != ids.packed_values) and (memory[ids.packed_values] < 2**63) +*/ +pub fn is_less_than_63_bits_and_not_end( + vm: &mut VirtualMachine, + ids_data: &HashMap, + ap_tracking: &ApTracking, +) -> Result<(), HintError> { + let end = get_ptr_from_var_name("end", vm, ids_data, ap_tracking)?; + let packed_values = get_ptr_from_var_name("packed_values", vm, ids_data, ap_tracking)?; + + if end == packed_values { + insert_value_into_ap(vm, 0)? + } else { + let val = vm.get_integer(packed_values)?; + insert_value_into_ap( + vm, + (val.to_biguint() < (BigUint::from(1_u32) << 63)) as usize, + )? + } + Ok(()) +} + +/* Implements Hint: +offset = 0 +for i in range(ids.packed_values_len): + val = (memory[ids.packed_values + i] % PRIME) + val_len = 2 if val < 2**63 else 8 + if val_len == 8: + val += 2**255 + for i in range(val_len - 1, -1, -1): + val, memory[ids.unpacked_u32s + offset + i] = divmod(val, 2**32) + assert val == 0 + offset += val_len +*/ +pub fn blake2s_unpack_felts( + vm: &mut VirtualMachine, + ids_data: &HashMap, + ap_tracking: &ApTracking, +) -> Result<(), HintError> { + let packed_values_len = + get_integer_from_var_name("packed_values_len", vm, ids_data, ap_tracking)?; + let packed_values = get_ptr_from_var_name("packed_values", vm, ids_data, ap_tracking)?; + let unpacked_u32s = get_ptr_from_var_name("unpacked_u32s", vm, ids_data, ap_tracking)?; + + let vals = vm.get_integer_range(packed_values, felt_to_usize(&packed_values_len)?)?; + let pow2_32 = BigUint::from(1_u32) << 32; + let pow2_63 = BigUint::from(1_u32) << 63; + let pow2_255 = BigUint::from(1_u32) << 255; + + // Split value into either 2 or 8 32-bit limbs. + let out: Vec = vals + .into_iter() + .map(|val| val.to_biguint()) + .flat_map(|val| { + if val < pow2_63 { + let (high, low) = val.div_rem(&pow2_32); + vec![high, low] + } else { + let mut limbs = vec![BigUint::from(0_u32); 8]; + let mut val: BigUint = val + &pow2_255; + for limb in limbs.iter_mut().rev() { + let (q, r) = val.div_rem(&pow2_32); + *limb = r; + val = q; + } + limbs + } + }) + .map(Felt252::from) + .map(MaybeRelocatable::from) + .collect(); + + vm.load_data(unpacked_u32s, &out) + .map_err(HintError::Memory)?; + Ok(()) +} + /* Implements Hint: %{ from starkware.cairo.common.cairo_blake2s.blake2s_utils import IV, blake2s_compress @@ -604,6 +685,103 @@ mod tests { .is_none()); } + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + fn is_less_than_63_bits_and_not_end_ends() { + let hint_code = hint_code::IS_LESS_THAN_63_BITS_AND_NOT_END; + //Create vm + let mut vm = vm!(); + //Insert ids into memory + vm.segments = segments![((1, 0), (1, 2)), ((1, 1), (1, 2)), ((1, 2), 123)]; + vm.set_fp(3); + vm.set_ap(3); + let ids_data = ids_data!["end", "packed_values", "value"]; + //Execute the hint + assert_matches!(run_hint!(vm, ids_data, hint_code), Ok(())); + //Check data ptr + check_memory![vm.segments.memory, ((1, 3), 0)]; + } + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + fn is_less_than_63_bits_and_not_end_small() { + let hint_code = hint_code::IS_LESS_THAN_63_BITS_AND_NOT_END; + //Create vm + let mut vm = vm!(); + //Insert ids into memory + vm.segments = segments![((1, 0), (1, 3)), ((1, 1), (1, 2)), ((1, 2), 123)]; + vm.set_fp(3); + vm.set_ap(3); + let ids_data = ids_data!["end", "packed_values", "value"]; + //Execute the hint + assert_matches!(run_hint!(vm, ids_data, hint_code), Ok(())); + //Check data ptr + check_memory![vm.segments.memory, ((1, 3), 1)]; + } + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + fn is_less_than_63_bits_and_not_end_big() { + let hint_code = hint_code::IS_LESS_THAN_63_BITS_AND_NOT_END; + //Create vm + let mut vm = vm!(); + //Insert ids into memory + vm.segments = segments![ + ((1, 0), (1, 3)), + ((1, 1), (1, 2)), + ((1, 2), 0x10000000000000000) + ]; + vm.set_fp(3); + vm.set_ap(3); + let ids_data = ids_data!["end", "packed_values", "value"]; + //Execute the hint + assert_matches!(run_hint!(vm, ids_data, hint_code), Ok(())); + //Check data ptr + check_memory![vm.segments.memory, ((1, 3), 0)]; + } + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + fn blake2s_unpack_felts() { + let hint_code = hint_code::BLAKE2S_UNPACK_FELTS; + //Create vm + let mut vm = vm!(); + //Insert ids into memory + vm.segments = segments![ + ((1, 0), 2), + ((1, 1), (1, 3)), + ((1, 2), (2, 0)), + ((1, 3), 0x123456781234), + ((1, 4), 0x1234abcd5678efab1234abcd) + ]; + vm.set_fp(5); + vm.set_ap(5); + let ids_data = ids_data![ + "packed_values_len", + "packed_values", + "unpacked_u32s", + "small_value", + "big_value" + ]; + vm.segments.add(); + //Execute the hint + assert_matches!(run_hint!(vm, ids_data, hint_code), Ok(())); + //Check data ptr + check_memory![ + vm.segments.memory, + ((2, 0), 0x1234), + ((2, 1), 0x56781234), + ((2, 2), 0x80000000), + ((2, 3), 0), + ((2, 4), 0), + ((2, 5), 0), + ((2, 6), 0), + ((2, 7), 0x1234abcd), + ((2, 8), 0x5678efab), + ((2, 9), 0x1234abcd) + ]; + } + #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn blake2s_add_uint256_bigend_valid_non_zero() { diff --git a/vm/src/hint_processor/builtin_hint_processor/builtin_hint_processor_definition.rs b/vm/src/hint_processor/builtin_hint_processor/builtin_hint_processor_definition.rs index f5ae7a96c4..020014615e 100644 --- a/vm/src/hint_processor/builtin_hint_processor/builtin_hint_processor_definition.rs +++ b/vm/src/hint_processor/builtin_hint_processor/builtin_hint_processor_definition.rs @@ -1,7 +1,7 @@ #[cfg(feature = "cairo-0-secp-hints")] use super::secp::cairo0_hints; use super::{ - blake2s_utils::finalize_blake2s_v3, + blake2s_utils::{blake2s_unpack_felts, finalize_blake2s_v3, is_less_than_63_bits_and_not_end}, ec_recover::{ ec_recover_divmod_n_packed, ec_recover_product_div_m, ec_recover_product_mod, ec_recover_sub_a_b, @@ -360,6 +360,12 @@ impl HintProcessorLogic for BuiltinHintProcessor { hint_code::BLAKE2S_ADD_UINT256_BIGEND => { blake2s_add_uint256_bigend(vm, &hint_data.ids_data, &hint_data.ap_tracking) } + hint_code::IS_LESS_THAN_63_BITS_AND_NOT_END => { + is_less_than_63_bits_and_not_end(vm, &hint_data.ids_data, &hint_data.ap_tracking) + } + hint_code::BLAKE2S_UNPACK_FELTS => { + blake2s_unpack_felts(vm, &hint_data.ids_data, &hint_data.ap_tracking) + } hint_code::UNSAFE_KECCAK => { unsafe_keccak(vm, exec_scopes, &hint_data.ids_data, &hint_data.ap_tracking) } diff --git a/vm/src/hint_processor/builtin_hint_processor/hint_code.rs b/vm/src/hint_processor/builtin_hint_processor/hint_code.rs index c8c5aaebc2..0985d9b410 100644 --- a/vm/src/hint_processor/builtin_hint_processor/hint_code.rs +++ b/vm/src/hint_processor/builtin_hint_processor/hint_code.rs @@ -426,6 +426,17 @@ segments.write_arg(ids.data + 4, [(ids.high >> (B * i)) & MASK for i in range(4) MASK = 2 ** 32 - 1 segments.write_arg(ids.data, [(ids.high >> (B * (3 - i))) & MASK for i in range(4)]) segments.write_arg(ids.data + 4, [(ids.low >> (B * (3 - i))) & MASK for i in range(4)])"#}), +(IS_LESS_THAN_63_BITS_AND_NOT_END, indoc! {r#"memory[ap] = to_felt_or_relocatable((ids.end != ids.packed_values) and (memory[ids.packed_values] < 2**63))"#}), +(BLAKE2S_UNPACK_FELTS, indoc! {r#"offset = 0 +for i in range(ids.packed_values_len): + val = (memory[ids.packed_values + i] % PRIME) + val_len = 2 if val < 2**63 else 8 + if val_len == 8: + val += 2**255 + for i in range(val_len - 1, -1, -1): + val, memory[ids.unpacked_u32s + offset + i] = divmod(val, 2**32) + assert val == 0 + offset += val_len"#}), (EXAMPLE_BLAKE2S_COMPRESS, indoc! {r#"from starkware.cairo.common.cairo_blake2s.blake2s_utils import IV, blake2s_compress _blake2s_input_chunk_size_felts = int(ids.BLAKE2S_INPUT_CHUNK_SIZE_FELTS)