diff --git a/crates/blockifier/src/execution/contract_class.rs b/crates/blockifier/src/execution/contract_class.rs index c44b8d23415..82f4813cae8 100644 --- a/crates/blockifier/src/execution/contract_class.rs +++ b/crates/blockifier/src/execution/contract_class.rs @@ -34,7 +34,11 @@ use starknet_types_core::felt::Felt; use crate::abi::constants::{self}; use crate::execution::entry_point::{EntryPointExecutionContext, EntryPointTypeAndSelector}; use crate::execution::errors::PreExecutionError; -use crate::execution::execution_utils::{poseidon_hash_many_cost, sn_api_to_cairo_vm_program}; +use crate::execution::execution_utils::{ + cost_of_encode_felt252_data_and_calc_blake_hash, + poseidon_hash_many_cost, + sn_api_to_cairo_vm_program, +}; #[cfg(feature = "cairo_native")] use crate::execution::native::contract_class::NativeCompiledClassV1; use crate::transaction::errors::TransactionExecutionError; @@ -287,7 +291,7 @@ impl CompiledClassV1 { /// This is an empiric measurement of several bytecode lengths, which constitutes as the /// dominant factor in it. fn estimate_casm_hash_computation_resources(&self) -> ExecutionResources { - estimate_casm_hash_computation_resources(&self.bytecode_segment_lengths) + estimate_casm_poseidon_hash_computation_resources(&self.bytecode_segment_lengths) } /// Estimate the VM gas required to perform a CompiledClassHash migration, @@ -321,7 +325,7 @@ impl CompiledClassV1 { /// /// Note: the function focuses on the bytecode size, and currently ignores the cost handling the /// class entry points. -pub fn estimate_casm_hash_computation_resources( +pub fn estimate_casm_poseidon_hash_computation_resources( bytecode_segment_lengths: &NestedIntList, ) -> ExecutionResources { // The constants in this function were computed by running the Casm code on a few values @@ -361,6 +365,80 @@ pub fn estimate_casm_hash_computation_resources( } } +/// Cost to hash a single flat segment of `len` felts. +fn leaf_cost(len: usize, resources_to_gas_fn: F) -> GasAmount +where + F: Fn(&ExecutionResources) -> GasAmount, +{ + // All `len` inputs treated as “big” felts; no small-felt optimization here. + cost_of_encode_felt252_data_and_calc_blake_hash(len, 0, resources_to_gas_fn) +} + +/// Cost to hash a multi-segment contract: +fn node_cost(segs: &[NestedIntList], resources_to_gas_fn: F) -> GasAmount +where + F: Fn(&ExecutionResources) -> GasAmount, +{ + // TODO(AvivG): Add base estimation for node. + let mut gas = GasAmount::ZERO; + + // TODO(AvivG): Add base estimation of each segment. Could this be part of 'leaf_cost'? + let segment_overhead = GasAmount::ZERO; + + // For each segment, hash its felts. + for seg in segs { + match seg { + NestedIntList::Leaf(len) => { + gas = gas.checked_add_panic_on_overflow(segment_overhead); + gas = gas.checked_add_panic_on_overflow(leaf_cost(*len, &resources_to_gas_fn)); + } + _ => panic!("Estimating hash cost only supports at most one level of segmentation."), + } + } + + // Node‐level hash over (hash1, len1, hash2, len2, …): one segment hash (“big” felt)) + // and one segment length (“small” felt) per segment. + let node_hash_cost = cost_of_encode_felt252_data_and_calc_blake_hash( + segs.len(), + segs.len(), + resources_to_gas_fn, + ); + + gas.checked_add_panic_on_overflow(node_hash_cost) +} + +/// Estimates the VM resources to compute the CASM Blake hash for a Cairo-1 contract: +/// - Uses only bytecode size (treats all felts as “big”, ignores the small-felt optimization). +pub fn estimate_casm_blake_hash_computation_resources( + bytecode_segment_lengths: &NestedIntList, + resources_to_gas_fn: F, +) -> GasAmount +where + F: Fn(&ExecutionResources) -> GasAmount, +{ + // TODO(AvivG): Currently ignores entry-point hashing costs. + // TODO(AvivG): Missing base overhead estimation for compiled_class_hash. + + // Basic frame overhead. + // TODO(AvivG): Once compiled_class_hash estimation is complete, + // revisit whether this should be moved into cost_of_encode_felt252_data_and_calc_blake_hash. + let resources = ExecutionResources { + n_steps: 0, + n_memory_holes: 0, + builtin_instance_counter: HashMap::from([(BuiltinName::range_check, 3)]), + }; + let gas = resources_to_gas_fn(&resources); + + // Add leaf vs node cost + let added_gas = match bytecode_segment_lengths { + // Single-segment contract (e.g., older Sierra contracts). + NestedIntList::Leaf(len) => leaf_cost(*len, &resources_to_gas_fn), + NestedIntList::Node(segs) => node_cost(segs, resources_to_gas_fn), + }; + + gas.checked_add_panic_on_overflow(added_gas) +} + // Returns the set of segments that were visited according to the given visited PCs and segment // lengths. // Each visited segment must have its starting PC visited, and is represented by it. diff --git a/crates/native_blockifier/src/py_testing_wrappers.rs b/crates/native_blockifier/src/py_testing_wrappers.rs index 5eed69a77b1..e6c5d5d2e10 100644 --- a/crates/native_blockifier/src/py_testing_wrappers.rs +++ b/crates/native_blockifier/src/py_testing_wrappers.rs @@ -1,4 +1,4 @@ -use blockifier::execution::contract_class::estimate_casm_hash_computation_resources; +use blockifier::execution::contract_class::estimate_casm_poseidon_hash_computation_resources; use blockifier::transaction::errors::{TransactionExecutionError, TransactionFeeError}; use cairo_lang_starknet_classes::NestedIntList; use pyo3::{pyfunction, PyResult}; @@ -14,17 +14,17 @@ pub fn raise_error_for_testing() -> NativeBlockifierResult<()> { .into()) } -/// Wrapper for [estimate_casm_hash_computation_resources] that can be used for testing. +/// Wrapper for [estimate_casm_poseidon_hash_computation_resources] that can be used for testing. /// Takes a leaf. #[pyfunction] pub fn estimate_casm_hash_computation_resources_for_testing_single( bytecode_segment_lengths: usize, ) -> PyResult { let node = NestedIntList::Leaf(bytecode_segment_lengths); - Ok(estimate_casm_hash_computation_resources(&node).into()) + Ok(estimate_casm_poseidon_hash_computation_resources(&node).into()) } -/// Wrapper for [estimate_casm_hash_computation_resources] that can be used for testing. +/// Wrapper for [estimate_casm_poseidon_hash_computation_resources] that can be used for testing. /// Takes a node of leaves. #[pyfunction] pub fn estimate_casm_hash_computation_resources_for_testing_list( @@ -33,5 +33,5 @@ pub fn estimate_casm_hash_computation_resources_for_testing_list( let node = NestedIntList::Node( bytecode_segment_lengths.into_iter().map(NestedIntList::Leaf).collect(), ); - Ok(estimate_casm_hash_computation_resources(&node).into()) + Ok(estimate_casm_poseidon_hash_computation_resources(&node).into()) }