@@ -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,84 @@ 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.
399+ blake_cost:: BASE_STEPS_PARTIAL_MSG + blake_cost:: STEPS_PER_2_U32_REMINDER * ( rem_u32s / 2 )
400+ } ;
401+
402+ n_big_felts * blake_cost:: STEPS_BIG_FELT
403+ + n_small_felts * blake_cost:: STEPS_SMALL_FELT
404+ + base_steps
405+ }
406+
407+ /// Returns the number of BLAKE opcodes needed to hash the given felts.
408+ /// Each BLAKE opcode processes 16 u32s (partial messages are padded).
409+ fn count_blake_opcode ( n_big_felts : usize , n_small_felts : usize ) -> usize {
410+ // Count the total number of u32s to be hashed.
411+ let total_u32s =
412+ n_big_felts * blake_cost:: N_U32S_BIG_FELT + n_small_felts * blake_cost:: N_U32S_SMALL_FELT ;
413+
414+ let full_msgs = total_u32s / blake_cost:: N_U32S_MESSAGE ;
415+ let has_partial = total_u32s % blake_cost:: N_U32S_MESSAGE != 0 ;
416+
417+ if has_partial { full_msgs + 1 } else { full_msgs }
418+ }
419+
420+ /// Estimates the VM resources for `encode_felt252_data_and_calc_blake_hash` in the Starknet OS.
421+ /// Assumes small felts unpack into 2 u32s and big felts into 8 u32s.
422+ pub fn cost_of_encode_felt252_data_and_calc_blake_hash < F > (
423+ n_big_felts : usize ,
424+ n_small_felts : usize ,
425+ resources_to_gas_fn : F ,
426+ ) -> GasAmount
427+ where
428+ F : Fn ( & ExecutionResources ) -> GasAmount ,
429+ {
430+ let n_steps = compute_blake_hash_steps ( n_big_felts, n_small_felts) ;
431+ let n_felts = n_big_felts + n_small_felts;
432+ // One `range_check` per input felt to validate its size.
433+ let builtins = HashMap :: from ( [ ( BuiltinName :: range_check, n_felts) ] ) ;
434+ let resources =
435+ ExecutionResources { n_steps, n_memory_holes : 0 , builtin_instance_counter : builtins } ;
436+ let gas = resources_to_gas_fn ( & resources) ;
437+
438+ let blake_op_count = count_blake_opcode ( n_big_felts, n_small_felts) ;
439+ let blake_op_gas = blake_op_count
440+ . checked_mul ( blake_cost:: BLAKE_OPCODE_GAS )
441+ . map ( u64_from_usize)
442+ . map ( GasAmount )
443+ . expect ( "Overflow computing Blake opcode gas." ) ;
444+
445+ gas. checked_add_panic_on_overflow ( blake_op_gas)
446+ }
0 commit comments