@@ -18,6 +18,7 @@ use cairo_vm::vm::vm_core::VirtualMachine;
1818use num_bigint:: BigUint ;
1919use starknet_api:: core:: ClassHash ;
2020use starknet_api:: deprecated_contract_class:: Program as DeprecatedProgram ;
21+ use starknet_api:: execution_resources:: GasAmount ;
2122use starknet_api:: transaction:: fields:: Calldata ;
2223use starknet_types_core:: felt:: Felt ;
2324
@@ -44,6 +45,7 @@ use crate::execution::syscalls::hint_processor::{ENTRYPOINT_NOT_FOUND_ERROR, OUT
4445use crate :: execution:: { deprecated_entry_point_execution, entry_point_execution} ;
4546use crate :: state:: errors:: StateError ;
4647use crate :: state:: state_api:: State ;
48+ use crate :: utils:: u64_from_usize;
4749
4850pub type Args = Vec < CairoArg > ;
4951
@@ -361,3 +363,88 @@ pub fn poseidon_hash_many_cost(data_length: usize) -> ExecutionResources {
361363 builtin_instance_counter : HashMap :: from ( [ ( BuiltinName :: poseidon, data_length / 2 + 1 ) ] ) ,
362364 }
363365}
366+
367+ mod blake_cost {
368+ // U-32 counts
369+ pub const N_U32S_MESSAGE : usize = 16 ;
370+ pub const N_U32S_BIG_FELT : usize = 8 ;
371+ pub const N_U32S_SMALL_FELT : usize = 2 ;
372+
373+ // Steps counts
374+ pub const STEPS_BIG_FELT : usize = 45 ;
375+ pub const STEPS_SMALL_FELT : usize = 15 ;
376+
377+ // One-time segment setup cost (full vs partial)
378+ pub const BASE_STEPS_FULL_MSG : usize = 217 ;
379+ pub const BASE_STEPS_PARTIAL_MSG : usize = 195 ;
380+ pub const STEPS_PER_2_U32_REMINDER : usize = 3 ;
381+
382+ // TODO(AvivG): This is a placeholder, add the actual gas cost for the BLAKE opcode
383+ pub const BLAKE_OPCODE_GAS : usize = 0 ;
384+ }
385+
386+ /// Estimates the number of VM steps needed to hash the given felts with Blake in Starknet OS.
387+ /// Each small felt unpacks into 2 u32s, and each big felt into 8 u32s.
388+ /// Adds a base cost depending on whether the total fits exactly into full 16-u32 messages.
389+ fn compute_blake_hash_steps ( n_big_felts : usize , n_small_felts : usize ) -> usize {
390+ let total_u32s =
391+ n_big_felts * blake_cost:: N_U32S_BIG_FELT + n_small_felts * blake_cost:: N_U32S_SMALL_FELT ;
392+ let rem_u32s = total_u32s % blake_cost:: N_U32S_MESSAGE ;
393+
394+ let base_steps = if rem_u32s == 0 {
395+ blake_cost:: BASE_STEPS_FULL_MSG
396+ } else {
397+ // This computation is based on manual calculations of running blake2s with different
398+ // inputs. Note: `rem_u32s` is always even, since all inputs expand to an even number of
399+ // u32s.
400+ blake_cost:: BASE_STEPS_PARTIAL_MSG + blake_cost:: STEPS_PER_2_U32_REMINDER * ( rem_u32s / 2 )
401+ } ;
402+
403+ n_big_felts * blake_cost:: STEPS_BIG_FELT
404+ + n_small_felts * blake_cost:: STEPS_SMALL_FELT
405+ + base_steps
406+ }
407+
408+ /// Returns the number of BLAKE opcodes needed to hash the given felts.
409+ /// Each BLAKE opcode processes 16 u32s (partial messages are padded).
410+ fn count_blake_opcode ( n_big_felts : usize , n_small_felts : usize ) -> usize {
411+ // Count the total number of u32s to be hashed.
412+ let total_u32s =
413+ n_big_felts * blake_cost:: N_U32S_BIG_FELT + n_small_felts * blake_cost:: N_U32S_SMALL_FELT ;
414+
415+ let full_msgs = total_u32s / blake_cost:: N_U32S_MESSAGE ;
416+ let has_partial = total_u32s % blake_cost:: N_U32S_MESSAGE != 0 ;
417+
418+ if has_partial { full_msgs + 1 } else { full_msgs }
419+ }
420+
421+ /// Estimates the VM resources for `encode_felt252_data_and_calc_blake_hash` in the Starknet OS.
422+ /// Assumes small felts unpack into 2 u32s and big felts into 8 u32s, matching the logic of the OS
423+ /// function being estimated.
424+ /// TODO(AvivG): Consider evaluating the cost of `encode_felt252_to_u32s` and `blake_with_opcode`
425+ /// separately for better granularity.
426+ pub fn cost_of_encode_felt252_data_and_calc_blake_hash < F > (
427+ n_big_felts : usize ,
428+ n_small_felts : usize ,
429+ resources_to_gas_fn : F ,
430+ ) -> GasAmount
431+ where
432+ F : Fn ( & ExecutionResources ) -> GasAmount ,
433+ {
434+ let n_steps = compute_blake_hash_steps ( n_big_felts, n_small_felts) ;
435+ let n_felts = n_big_felts + n_small_felts;
436+ // One `range_check` per input felt to validate its size.
437+ let builtins = HashMap :: from ( [ ( BuiltinName :: range_check, n_felts) ] ) ;
438+ let resources =
439+ ExecutionResources { n_steps, n_memory_holes : 0 , builtin_instance_counter : builtins } ;
440+ let gas = resources_to_gas_fn ( & resources) ;
441+
442+ let blake_op_count = count_blake_opcode ( n_big_felts, n_small_felts) ;
443+ let blake_op_gas = blake_op_count
444+ . checked_mul ( blake_cost:: BLAKE_OPCODE_GAS )
445+ . map ( u64_from_usize)
446+ . map ( GasAmount )
447+ . expect ( "Overflow computing Blake opcode gas." ) ;
448+
449+ gas. checked_add_panic_on_overflow ( blake_op_gas)
450+ }
0 commit comments