Skip to content

Commit 6ae455c

Browse files
blockifier: add fn encode_felt252_data_and_calc_blake_hash_cost
1 parent 0131283 commit 6ae455c

File tree

1 file changed

+87
-0
lines changed

1 file changed

+87
-0
lines changed

crates/blockifier/src/execution/execution_utils.rs

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ use cairo_vm::vm::vm_core::VirtualMachine;
1818
use num_bigint::BigUint;
1919
use starknet_api::core::ClassHash;
2020
use starknet_api::deprecated_contract_class::Program as DeprecatedProgram;
21+
use starknet_api::execution_resources::GasAmount;
2122
use starknet_api::transaction::fields::Calldata;
2223
use starknet_types_core::felt::Felt;
2324

@@ -44,6 +45,7 @@ use crate::execution::syscalls::hint_processor::{ENTRYPOINT_NOT_FOUND_ERROR, OUT
4445
use crate::execution::{deprecated_entry_point_execution, entry_point_execution};
4546
use crate::state::errors::StateError;
4647
use crate::state::state_api::State;
48+
use crate::utils::u64_from_usize;
4749

4850
pub 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

Comments
 (0)