Skip to content

Commit 83c44e9

Browse files
starknet_os: tets compare rust vs cairo encode_felt252_data_and_calc_224_bit_blake_hash
1 parent 959befc commit 83c44e9

File tree

4 files changed

+170
-75
lines changed

4 files changed

+170
-75
lines changed

crates/starknet_os/src/hints/enum_definition.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use crate::hints::hint_implementation::aggregator::{
1919
set_state_update_pointers_to_none,
2020
write_da_segment,
2121
};
22-
use crate::hints::hint_implementation::blake2s::{check_packed_values_end_and_size, unpack_felts_to_u32s};
22+
use crate::hints::hint_implementation::blake2s::implementation::{check_packed_values_end_and_size, unpack_felts_to_u32s};
2323
use crate::hints::hint_implementation::block_context::{
2424
block_number,
2525
block_timestamp,
Lines changed: 3 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -1,74 +1,3 @@
1-
use cairo_vm::hint_processor::builtin_hint_processor::hint_utils::{
2-
get_integer_from_var_name,
3-
get_ptr_from_var_name,
4-
insert_value_into_ap,
5-
};
6-
use cairo_vm::hint_processor::hint_processor_utils::felt_to_usize;
7-
use cairo_vm::types::relocatable::MaybeRelocatable;
8-
use cairo_vm::vm::errors::hint_errors::HintError;
9-
use cairo_vm::Felt252;
10-
use num_bigint::BigUint;
11-
use num_integer::Integer;
12-
13-
use crate::hints::error::OsHintResult;
14-
use crate::hints::types::HintArgs;
15-
16-
/// Unpacks felt values into u32 arrays for Blake2s processing.
17-
/// This implements the Cairo hint that converts felt values to u32 arrays
18-
/// following the Blake2s encoding scheme.
19-
pub(crate) fn unpack_felts_to_u32s(
20-
HintArgs { vm, ids_data, ap_tracking, .. }: HintArgs<'_>,
21-
) -> OsHintResult {
22-
let packed_values_len =
23-
get_integer_from_var_name("packed_values_len", vm, ids_data, ap_tracking)?;
24-
let packed_values = get_ptr_from_var_name("packed_values", vm, ids_data, ap_tracking)?;
25-
let unpacked_u32s = get_ptr_from_var_name("unpacked_u32s", vm, ids_data, ap_tracking)?;
26-
27-
let vals = vm.get_integer_range(packed_values, felt_to_usize(&packed_values_len)?)?;
28-
let pow2_32 = BigUint::from(1_u32) << 32;
29-
let pow2_63 = BigUint::from(1_u32) << 63;
30-
let pow2_255 = BigUint::from(1_u32) << 255;
31-
32-
// Split value into either 2 or 8 32-bit limbs.
33-
let out: Vec<MaybeRelocatable> = vals
34-
.into_iter()
35-
.map(|val| val.to_biguint())
36-
.flat_map(|val| {
37-
if val < pow2_63 {
38-
let (high, low) = val.div_rem(&pow2_32);
39-
vec![high, low]
40-
} else {
41-
let mut limbs = vec![BigUint::from(0_u32); 8];
42-
let mut val: BigUint = val + &pow2_255;
43-
for limb in limbs.iter_mut().rev() {
44-
let (q, r) = val.div_rem(&pow2_32);
45-
*limb = r;
46-
val = q;
47-
}
48-
limbs
49-
}
50-
})
51-
.map(Felt252::from)
52-
.map(MaybeRelocatable::from)
53-
.collect();
54-
55-
vm.load_data(unpacked_u32s, &out).map_err(HintError::Memory)?;
56-
Ok(())
57-
}
58-
59-
/// Checks if we've reached the end of packed_values and if the current value is small (< 2^63).
60-
/// This implements the Cairo hint that determines loop continuation and value size.
61-
pub(crate) fn check_packed_values_end_and_size(
62-
HintArgs { vm, ids_data, ap_tracking, .. }: HintArgs<'_>,
63-
) -> OsHintResult {
64-
let end = get_ptr_from_var_name("end", vm, ids_data, ap_tracking)?;
65-
let packed_values = get_ptr_from_var_name("packed_values", vm, ids_data, ap_tracking)?;
66-
67-
if end == packed_values {
68-
insert_value_into_ap(vm, 0)?
69-
} else {
70-
let val = vm.get_integer(packed_values)?;
71-
insert_value_into_ap(vm, (val.to_biguint() < (BigUint::from(1_u32) << 63)) as usize)?
72-
}
73-
Ok(())
74-
}
1+
#[cfg(test)]
2+
pub(crate) mod blake2s_test;
3+
pub(crate) mod implementation;
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
use std::collections::HashMap;
2+
3+
use apollo_starknet_os_program::OS_PROGRAM_BYTES;
4+
use blake2s::encode_felt252_data_and_calc_224_bit_blake_hash;
5+
use cairo_vm::types::builtin_name::BuiltinName;
6+
use cairo_vm::types::layout_name::LayoutName;
7+
use rstest::rstest;
8+
use starknet_types_core::felt::Felt;
9+
10+
use crate::test_utils::cairo_runner::{
11+
run_cairo_0_entry_point,
12+
EndpointArg,
13+
EntryPointRunnerConfig,
14+
ImplicitArg,
15+
PointerArg,
16+
ValueArg,
17+
};
18+
19+
/// Test that compares Cairo and Rust implementations of
20+
/// encode_felt252_data_and_calc_224_bit_blake_hash.
21+
#[rstest]
22+
#[case::boundary_under_2_63(vec![Felt::from((1u64 << 63) - 1)])]
23+
#[case::boundary_at_2_63(vec![Felt::from(1u64 << 63)])]
24+
#[case::very_large_felt(vec![Felt::from_hex("0x800000000000011000000000000000000000000000000000000000000000000").unwrap()])]
25+
#[case::mixed_small_large(vec![Felt::from(42), Felt::from(1u64 << 63), Felt::from(1337)])]
26+
#[case::max_u64(vec![Felt::from(u64::MAX)])]
27+
fn test_cairo_vs_rust_blake2s_implementation(#[case] test_data: Vec<Felt>) {
28+
let runner_config = EntryPointRunnerConfig {
29+
layout: LayoutName::all_cairo,
30+
trace_enabled: false,
31+
verify_secure: false,
32+
proof_mode: false,
33+
add_main_prefix_to_entrypoint: false,
34+
};
35+
36+
// Get the OS program as bytes
37+
let program_bytes = OS_PROGRAM_BYTES;
38+
39+
println!("Testing case: {:?}", test_data);
40+
41+
// Calculate hash using Rust implementation
42+
let rust_hash = encode_felt252_data_and_calc_224_bit_blake_hash(&test_data);
43+
44+
// Calculate hash using Cairo implementation
45+
let data_len = test_data.len();
46+
let explicit_args = vec![
47+
EndpointArg::from(Felt::from(data_len)), // data_len
48+
EndpointArg::Pointer(PointerArg::Array(test_data.clone())), // data array
49+
];
50+
51+
let implicit_args = vec![ImplicitArg::Builtin(BuiltinName::range_check)];
52+
53+
let expected_return_values = vec![EndpointArg::from(Felt::ZERO)]; // Placeholder
54+
55+
let hint_locals: HashMap<String, Box<dyn std::any::Any>> = HashMap::new();
56+
57+
// Call the Cairo function
58+
let result = run_cairo_0_entry_point(
59+
&runner_config,
60+
program_bytes,
61+
"starkware.cairo.common.cairo_blake2s.blake2s.\
62+
encode_felt252_data_and_calc_224_bit_blake_hash",
63+
&explicit_args,
64+
&implicit_args,
65+
&expected_return_values,
66+
hint_locals,
67+
None, // state_reader
68+
);
69+
70+
match result {
71+
Ok((_, explicit_return_values, _)) => {
72+
assert_eq!(explicit_return_values.len(), 1, "Expected exactly one return value");
73+
74+
match &explicit_return_values[0] {
75+
EndpointArg::Value(ValueArg::Single(cairo_hash)) => {
76+
println!("Rust hash: {}, Cairo hash: {}", rust_hash, cairo_hash);
77+
assert_eq!(
78+
rust_hash, *cairo_hash,
79+
"Blake2s hash mismatch: Rust={}, Cairo={}",
80+
rust_hash, cairo_hash
81+
);
82+
}
83+
_ => panic!("Expected a single felt return value"),
84+
}
85+
}
86+
Err(e) => {
87+
panic!("Failed to run Cairo blake2s function: {:?}", e);
88+
}
89+
}
90+
91+
println!("✅ Test case passed! Cairo and Rust Blake2s implementations match.");
92+
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
use cairo_vm::hint_processor::builtin_hint_processor::hint_utils::{
2+
get_integer_from_var_name,
3+
get_ptr_from_var_name,
4+
insert_value_into_ap,
5+
};
6+
use cairo_vm::hint_processor::hint_processor_utils::felt_to_usize;
7+
use cairo_vm::types::relocatable::MaybeRelocatable;
8+
use cairo_vm::vm::errors::hint_errors::HintError;
9+
use cairo_vm::Felt252;
10+
use num_bigint::BigUint;
11+
use num_integer::Integer;
12+
13+
use crate::hints::error::OsHintResult;
14+
use crate::hints::types::HintArgs;
15+
16+
/// Unpacks felt values into u32 arrays for Blake2s processing.
17+
/// This implements the Cairo hint that converts felt values to u32 arrays
18+
/// following the Blake2s encoding scheme.
19+
pub(crate) fn unpack_felts_to_u32s(
20+
HintArgs { vm, ids_data, ap_tracking, .. }: HintArgs<'_>,
21+
) -> OsHintResult {
22+
let packed_values_len =
23+
get_integer_from_var_name("packed_values_len", vm, ids_data, ap_tracking)?;
24+
let packed_values = get_ptr_from_var_name("packed_values", vm, ids_data, ap_tracking)?;
25+
let unpacked_u32s = get_ptr_from_var_name("unpacked_u32s", vm, ids_data, ap_tracking)?;
26+
27+
let vals = vm.get_integer_range(packed_values, felt_to_usize(&packed_values_len)?)?;
28+
let pow2_32 = BigUint::from(1_u32) << 32;
29+
let pow2_63 = BigUint::from(1_u32) << 63;
30+
let pow2_255 = BigUint::from(1_u32) << 255;
31+
32+
// Split value into either 2 or 8 32-bit limbs.
33+
let out: Vec<MaybeRelocatable> = vals
34+
.into_iter()
35+
.map(|val| val.to_biguint())
36+
.flat_map(|val| {
37+
if val < pow2_63 {
38+
let (high, low) = val.div_rem(&pow2_32);
39+
vec![high, low]
40+
} else {
41+
let mut limbs = vec![BigUint::from(0_u32); 8];
42+
let mut val: BigUint = val + &pow2_255;
43+
for limb in limbs.iter_mut().rev() {
44+
let (q, r) = val.div_rem(&pow2_32);
45+
*limb = r;
46+
val = q;
47+
}
48+
limbs
49+
}
50+
})
51+
.map(Felt252::from)
52+
.map(MaybeRelocatable::from)
53+
.collect();
54+
55+
vm.load_data(unpacked_u32s, &out).map_err(HintError::Memory)?;
56+
Ok(())
57+
}
58+
59+
/// Checks if we've reached the end of packed_values and if the current value is small (< 2^63).
60+
/// This implements the Cairo hint that determines loop continuation and value size.
61+
pub(crate) fn check_packed_values_end_and_size(
62+
HintArgs { vm, ids_data, ap_tracking, .. }: HintArgs<'_>,
63+
) -> OsHintResult {
64+
let end = get_ptr_from_var_name("end", vm, ids_data, ap_tracking)?;
65+
let packed_values = get_ptr_from_var_name("packed_values", vm, ids_data, ap_tracking)?;
66+
67+
if end == packed_values {
68+
insert_value_into_ap(vm, 0)?
69+
} else {
70+
let val = vm.get_integer(packed_values)?;
71+
insert_value_into_ap(vm, (val.to_biguint() < (BigUint::from(1_u32) << 63)) as usize)?
72+
}
73+
Ok(())
74+
}

0 commit comments

Comments
 (0)