diff --git a/crates/blockifier/src/execution/contract_class.rs b/crates/blockifier/src/execution/contract_class.rs index 902cacf0552..a376b5955ce 100644 --- a/crates/blockifier/src/execution/contract_class.rs +++ b/crates/blockifier/src/execution/contract_class.rs @@ -47,6 +47,7 @@ use crate::execution::casm_hash_estimation::EstimatedExecutionResources; use crate::execution::entry_point::{EntryPointExecutionContext, EntryPointTypeAndSelector}; use crate::execution::errors::PreExecutionError; use crate::execution::execution_utils::{ + blake_encoding, blake_execution_resources_estimation_to_gas, encode_and_blake_hash_resources, poseidon_hash_many_cost, @@ -80,6 +81,20 @@ impl FeltSizeCount { self.small + self.large } + /// Returns the total number of `u32` words required to encode all felts + /// according to encode_felts_to_u32s func. + pub(crate) fn encoded_u32_len(&self) -> usize { + self.large * blake_encoding::N_U32S_BIG_FELT + + self.small * blake_encoding::N_U32S_SMALL_FELT + } + + /// Returns the number of BLAKE opcodes required to hash the felts. + /// Each BLAKE opcode processes one message block of [`U32_WORDS_PER_MESSAGE`] `u32`s + /// (partial messages are padded). + pub(crate) fn blake_opcode_count(&self) -> usize { + self.encoded_u32_len().div_ceil(blake_encoding::N_U32S_MESSAGE) + } + /// Creates a `FeltSizeCount` by counting how many items in the slice are "small" or "large". /// The `is_small` function determines whether each item is considered small (`true`) or large /// (`false`). diff --git a/crates/blockifier/src/execution/contract_class_test.rs b/crates/blockifier/src/execution/contract_class_test.rs index 786cd2a3506..fccfb5822c0 100644 --- a/crates/blockifier/src/execution/contract_class_test.rs +++ b/crates/blockifier/src/execution/contract_class_test.rs @@ -2,6 +2,7 @@ use std::collections::HashSet; use std::sync::Arc; use assert_matches::assert_matches; +use blake2s::encode_felts_to_u32s; use blockifier_test_utils::cairo_versions::{CairoVersion, RunnableCairo1}; use blockifier_test_utils::contracts::FeatureContract; use cairo_lang_starknet_classes::NestedIntList; @@ -171,3 +172,16 @@ fn test_felt_size_count_from_biguintashex_slice() { assert_eq!(empty_count.small, 0); assert_eq!(empty_count.large, 0); } + +#[rstest] +#[case::boundary_small_felt(&[Felt::from((1u64 << 63) - 1)])] +#[case::boundary_at_2_63(&[Felt::from(1u64 << 63)])] +#[case::very_large_felt(&[Felt::from_hex("0x800000000000011000000000000000000000000000000000000000000000000").unwrap()])] +#[case::mixed_small_large(&[Felt::from(42), Felt::from(1u64 << 63), Felt::from(1337)])] +#[case::many_large(&[Felt::from(1u64 << 63); 100])] +fn test_encoded_u32_len(#[case] test_data: &[Felt]) { + let estimated_u32_len = FeltSizeCount::from(test_data).encoded_u32_len(); + let actual_u32_len = encode_felts_to_u32s(test_data.to_vec()).len(); + + assert_eq!(actual_u32_len, estimated_u32_len); +} \ No newline at end of file diff --git a/crates/blockifier/src/execution/execution_utils.rs b/crates/blockifier/src/execution/execution_utils.rs index 1473aeaa1c6..f131752fdf2 100644 --- a/crates/blockifier/src/execution/execution_utils.rs +++ b/crates/blockifier/src/execution/execution_utils.rs @@ -374,7 +374,7 @@ pub fn poseidon_hash_many_cost(data_length: usize) -> ExecutionResources { } // Constants that define how felts are encoded into u32s for BLAKE hashing. -mod blake_encoding { +pub(crate) mod blake_encoding { /// Number of u32s in a Blake input message. pub const N_U32S_MESSAGE: usize = 16; @@ -404,18 +404,6 @@ mod blake_estimation { pub const STEPS_EMPTY_INPUT: usize = 170; } -/// Calculates the total number of u32s required to encode the given number of big and small felts. -/// Big felts encode to 8 u32s each, small felts encode to 2 u32s each. -fn total_u32s_from_felts(n_big_felts: usize, n_small_felts: usize) -> usize { - let big_u32s = n_big_felts - .checked_mul(blake_encoding::N_U32S_BIG_FELT) - .expect("Overflow computing big felts u32s"); - let small_u32s = n_small_felts - .checked_mul(blake_encoding::N_U32S_SMALL_FELT) - .expect("Overflow computing small felts u32s"); - big_u32s.checked_add(small_u32s).expect("Overflow computing total u32s") -} - fn base_steps_for_blake_hash(n_u32s: usize) -> usize { let rem_u32s = n_u32s % blake_encoding::N_U32S_MESSAGE; if rem_u32s == 0 { @@ -444,7 +432,7 @@ fn felts_steps(n_big_felts: usize, n_small_felts: usize) -> usize { fn estimate_steps_of_encode_felt252_data_and_calc_blake_hash( felt_size_groups: &FeltSizeCount, ) -> usize { - let total_u32s = total_u32s_from_felts(felt_size_groups.large, felt_size_groups.small); + let total_u32s = felt_size_groups.encoded_u32_len(); if total_u32s == 0 { // The empty input case is a special case. return blake_estimation::STEPS_EMPTY_INPUT; @@ -456,14 +444,6 @@ fn estimate_steps_of_encode_felt252_data_and_calc_blake_hash( base_steps.checked_add(felt_steps).expect("Overflow computing total Blake hash steps") } -/// Returns the number of BLAKE opcodes needed to hash the given felts. -/// Each BLAKE opcode processes 16 u32s (partial messages are padded). -fn count_blake_opcode(felt_size_groups: &FeltSizeCount) -> usize { - // Count the total number of u32s to be hashed. - let total_u32s = total_u32s_from_felts(felt_size_groups.large, felt_size_groups.small); - total_u32s.div_ceil(blake_encoding::N_U32S_MESSAGE) -} - /// Estimates resource usage for `encode_felt252_data_and_calc_blake_hash` in the Starknet OS. /// /// # Encoding Details @@ -492,7 +472,7 @@ pub fn encode_and_blake_hash_resources( EstimatedExecutionResources::V2Hash { resources, - blake_count: count_blake_opcode(felt_size_groups), + blake_count: felt_size_groups.blake_opcode_count(), } } diff --git a/crates/blockifier/src/execution/execution_utils_test.rs b/crates/blockifier/src/execution/execution_utils_test.rs index f7238db5719..e10c6fba3a9 100644 --- a/crates/blockifier/src/execution/execution_utils_test.rs +++ b/crates/blockifier/src/execution/execution_utils_test.rs @@ -7,7 +7,6 @@ use crate::execution::contract_class::FeltSizeCount; use crate::execution::execution_utils::blake_estimation::STEPS_EMPTY_INPUT; use crate::execution::execution_utils::{ blake_encoding, - count_blake_opcode, encode_and_blake_hash_resources, estimate_steps_of_encode_felt252_data_and_calc_blake_hash, }; @@ -36,7 +35,7 @@ fn test_zero_inputs() { assert_eq!(steps, STEPS_EMPTY_INPUT, "Unexpected base step cost for zero inputs"); // No opcodes should be emitted. - let opcodes = count_blake_opcode(&FeltSizeCount::default()); + let opcodes = FeltSizeCount::default().blake_opcode_count(); assert_eq!(opcodes, 0, "Expected zero BLAKE opcodes for zero inputs"); // Should result in base cost only (no opcode cost). @@ -48,7 +47,7 @@ fn test_zero_inputs() { // TODO(AvivG): Add tests for: // - `estimate_steps_of_encode_felt252_data_and_calc_blake_hash` simple cases (felts input). -// - `count_blake_opcode` simple cases (felts input). +// - `blake_opcode_count` simple cases (felts input). // - `cost_of_encode_felt252_data_and_calc_blake_hash` simple cases (felts input) (including partial // remainder). // - `cost_of_encode_felt252_data_and_calc_blake_hash` compare against actual execution resources