diff --git a/crates/blockifier/src/execution/casm_hash_estimation.rs b/crates/blockifier/src/execution/casm_hash_estimation.rs index 85e27cd4bae..0ec8666db0c 100644 --- a/crates/blockifier/src/execution/casm_hash_estimation.rs +++ b/crates/blockifier/src/execution/casm_hash_estimation.rs @@ -242,15 +242,28 @@ pub struct CasmV2HashResourceEstimate {} impl CasmV2HashResourceEstimate { // Constants that define how felts are encoded into u32s for BLAKE hashing. // Number of `u32` words a large felt expands into. - pub(crate) const U32_WORDS_PER_LARGE_FELT: usize = 8; + pub const U32_WORDS_PER_LARGE_FELT: usize = 8; // Number of `u32` words a small felt expands into. - pub(crate) const U32_WORDS_PER_SMALL_FELT: usize = 2; + pub const U32_WORDS_PER_SMALL_FELT: usize = 2; // Input for Blake hash function is a sequence of 16 `u32` words. - pub(crate) const U32_WORDS_PER_MESSAGE: usize = 16; + pub const U32_WORDS_PER_MESSAGE: usize = 16; // Base number of VM steps applied when the input to Blake hashing is empty. // Determined empirically by running `encode_felt252_data_and_calc_blake_hash` on empty input. - pub(crate) const STEPS_EMPTY_INPUT: usize = 170; + pub const STEPS_EMPTY_INPUT: usize = 170; + + // The constants used are empirical, based on running `encode_felt252_data_and_calc_blake_hash` + // on combinations of large and small felts. + // VM steps per large felt. + pub const STEPS_PER_LARGE_FELT: usize = 45; + // VM steps per small felt. + pub const STEPS_PER_SMALL_FELT: usize = 15; + // Base overhead when input exactly fills a 16-u32 Blake message. + pub const BASE_STEPS_FULL_MSG: usize = 217; + // Base overhead when the input leaves a remainder (< 16 u32s) for a Blake message. + pub const BASE_STEPS_PARTIAL_MSG: usize = 195; + // Extra VM steps added per 2-u32 remainder in partial Blake messages. + pub const STEPS_PER_2_U32_REMINDER: usize = 3; /// Estimates the number of VM steps required to hash the given felts with Blake in Starknet OS. /// @@ -261,19 +274,6 @@ impl CasmV2HashResourceEstimate { fn estimate_steps_of_encode_felt252_data_and_calc_blake_hash( felt_size_groups: &FeltSizeCount, ) -> usize { - // The constants used are empirical, based on running - // `encode_felt252_data_and_calc_blake_hash` on combinations of large and small - // felts. VM steps per large felt. - const STEPS_PER_LARGE_FELT: usize = 45; - // VM steps per small felt. - const STEPS_PER_SMALL_FELT: usize = 15; - // Base overhead when input exactly fills a 16-u32 Blake message. - const BASE_STEPS_FULL_MSG: usize = 217; - // Base overhead when the input leaves a remainder (< 16 u32s) for a Blake message. - const BASE_STEPS_PARTIAL_MSG: usize = 195; - // Extra VM steps added per 2-u32 remainder in partial Blake messages. - const STEPS_PER_2_U32_REMINDER: usize = 3; - let encoded_u32_len = felt_size_groups.encoded_u32_len(); if encoded_u32_len == 0 { // The empty input case is a special case. @@ -282,17 +282,18 @@ impl CasmV2HashResourceEstimate { // Adds a base cost depending on whether the total fits exactly into full 16-u32 messages. let base_steps = if encoded_u32_len.is_multiple_of(Self::U32_WORDS_PER_MESSAGE) { - BASE_STEPS_FULL_MSG + Self::BASE_STEPS_FULL_MSG } else { // This computation is based on running blake2s with different inputs. // Note: all inputs expand to an even number of u32s --> `rem_u32s` is always even. - BASE_STEPS_PARTIAL_MSG - + (encoded_u32_len % Self::U32_WORDS_PER_MESSAGE / 2) * STEPS_PER_2_U32_REMINDER + Self::BASE_STEPS_PARTIAL_MSG + + (encoded_u32_len % Self::U32_WORDS_PER_MESSAGE / 2) + * Self::STEPS_PER_2_U32_REMINDER }; base_steps - + felt_size_groups.large * STEPS_PER_LARGE_FELT - + felt_size_groups.small * STEPS_PER_SMALL_FELT + + felt_size_groups.large * Self::STEPS_PER_LARGE_FELT + + felt_size_groups.small * Self::STEPS_PER_SMALL_FELT } } diff --git a/crates/starknet_os/src/hints/hint_implementation/blake2s/blake2s_test.rs b/crates/starknet_os/src/hints/hint_implementation/blake2s/blake2s_test.rs index 09d8d0d5a45..886419a37ea 100644 --- a/crates/starknet_os/src/hints/hint_implementation/blake2s/blake2s_test.rs +++ b/crates/starknet_os/src/hints/hint_implementation/blake2s/blake2s_test.rs @@ -35,17 +35,8 @@ fn estimated_encode_and_blake_hash_execution_resources(data: &[Felt]) -> Executi resources } -/// Test that compares Cairo and Rust implementations of -/// encode_felt252_data_and_calc_blake_hash. -#[rstest] -// TODO(Aviv): Add the empty case once the cairo implementation supports it. -#[case::empty(vec![])] -#[case::boundary_small_felt(vec![Felt::from((1u64 << 63) - 1)])] -#[case::boundary_at_2_63(vec![Felt::from(1u64 << 63)])] -#[case::very_large_felt(vec![Felt::from_hex("0x800000000000011000000000000000000000000000000000000000000000000").unwrap()])] -#[case::mixed_small_large(vec![Felt::from(42), Felt::from(1u64 << 63), Felt::from(1337)])] -#[case::many_large(vec![Felt::from(1u64 << 63); 100])] -fn test_cairo_vs_rust_blake2s_implementation(#[case] test_data: Vec) { +/// Returns the result and the resources used. +fn cairo_encode_felt252_data_and_calc_blake_hash(input: &[Felt]) -> (Felt, ExecutionResources) { let runner_config = EntryPointRunnerConfig { layout: LayoutName::all_cairo, trace_enabled: false, @@ -55,26 +46,21 @@ fn test_cairo_vs_rust_blake2s_implementation(#[case] test_data: Vec) { validate_builtins_offset: true, }; - let rust_hash = Blake2Felt252::encode_felt252_data_and_calc_blake_hash(&test_data); - - let data_len = test_data.len(); + let data_len = input.len(); let explicit_args = vec![ EndpointArg::from(Felt::from(data_len)), EndpointArg::Pointer(PointerArg::Array( - test_data.iter().map(|felt| MaybeRelocatable::Int(*felt)).collect(), + input.iter().map(|felt| MaybeRelocatable::Int(*felt)).collect(), )), ]; - let implicit_args = vec![ImplicitArg::Builtin(BuiltinName::range_check)]; - let expected_return_values = vec![EndpointArg::from(Felt::ZERO)]; - let hint_locals: HashMap> = HashMap::new(); // Call the Cairo entrypoint. // This entrypoint does not use state reader. let state_reader = None; - let result = initialize_and_run_cairo_0_entry_point( + let (_, explicit_return_values, cairo_runner) = initialize_and_run_cairo_0_entry_point( &runner_config, apollo_starknet_os_program::OS_PROGRAM_BYTES, "starkware.cairo.common.cairo_blake2s.blake2s.encode_felt252_data_and_calc_blake_hash", @@ -83,34 +69,42 @@ fn test_cairo_vs_rust_blake2s_implementation(#[case] test_data: Vec) { &expected_return_values, hint_locals, state_reader, - ); + ) + .unwrap_or_else(|e| panic!("Failed to run Cairo blake2s function: {e:?}")); - match result { - Ok((_, explicit_return_values, cairo_runner)) => { - assert_eq!(explicit_return_values.len(), 1, "Expected exactly one return value"); + assert_eq!(explicit_return_values.len(), 1, "Expected exactly one return value"); - let EndpointArg::Value(ValueArg::Single(MaybeRelocatable::Int(cairo_hash_felt))) = - &explicit_return_values[0] - else { - panic!("Expected a single felt return value"); - }; - assert_eq!( - rust_hash, *cairo_hash_felt, - "Blake2s hash mismatch: Rust={rust_hash}, Cairo={cairo_hash_felt}", - ); + let EndpointArg::Value(ValueArg::Single(MaybeRelocatable::Int(cairo_hash_felt))) = + &explicit_return_values[0] + else { + panic!("Expected a single felt return value"); + }; + (*cairo_hash_felt, cairo_runner.get_execution_resources().unwrap().filter_unused_builtins()) +} + +/// Test that compares Cairo and Rust implementations of +/// encode_felt252_data_and_calc_blake_hash. +#[rstest] +// TODO(Aviv): Add the empty case once the cairo implementation supports it. +#[case::empty(vec![])] +#[case::boundary_small_felt(vec![Felt::from((1u64 << 63) - 1)])] +#[case::boundary_at_2_63(vec![Felt::from(1u64 << 63)])] +#[case::very_large_felt(vec![Felt::from_hex("0x800000000000011000000000000000000000000000000000000000000000000").unwrap()])] +#[case::mixed_small_large(vec![Felt::from(42), Felt::from(1u64 << 63), Felt::from(1337)])] +#[case::many_large(vec![Felt::from(1u64 << 63); 100])] +fn test_cairo_vs_rust_blake2s_implementation(#[case] test_data: Vec) { + let (cairo_hash_felt, actual_resources) = + cairo_encode_felt252_data_and_calc_blake_hash(&test_data); + let rust_hash = Blake2Felt252::encode_felt252_data_and_calc_blake_hash(&test_data); + assert_eq!( + rust_hash, cairo_hash_felt, + "Blake2s hash mismatch: Rust={rust_hash}, Cairo={cairo_hash_felt}", + ); - // TODO(AvivG): consider moving this to the where the estimate methods are defined. - let actual_resources = - cairo_runner.get_execution_resources().unwrap().filter_unused_builtins(); - let estimated_resources = - estimated_encode_and_blake_hash_execution_resources(&test_data); - // Asserts that actual Cairo execution resources match the estimate. - assert_eq!(actual_resources, estimated_resources); - } - Err(e) => { - panic!("Failed to run Cairo blake2s function: {e:?}"); - } - } + // TODO(AvivG): consider moving this to the where the estimate methods are defined. + let estimated_resources = estimated_encode_and_blake_hash_execution_resources(&test_data); + // Asserts that actual Cairo execution resources match the estimate. + assert_eq!(actual_resources, estimated_resources); } /// Test that compares the Cairo0 `calc_naive_blake_hash` with its Rust equivalent.