Skip to content

Commit 9d6cd2b

Browse files
blockifier: move logic inside casm_v2hash_resource_estimate
1 parent 7afade2 commit 9d6cd2b

File tree

3 files changed

+85
-86
lines changed

3 files changed

+85
-86
lines changed

crates/blockifier/src/execution/casm_hash_estimation.rs

Lines changed: 70 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ pub trait EstimateCasmHashResources {
172172

173173
// TODO(AvivG): Remove allow once used.
174174
#[allow(unused)]
175-
struct CasmV1HashResourceEstimate {}
175+
pub(crate) struct CasmV1HashResourceEstimate {}
176176

177177
impl EstimateCasmHashResources for CasmV1HashResourceEstimate {
178178
fn hash_version(&self) -> HashVersion {
@@ -211,15 +211,16 @@ impl EstimateCasmHashResources for CasmV2HashResourceEstimate {
211211
fn estimated_resources_of_hash_function(
212212
felt_size_groups: &FeltSizeCount,
213213
) -> EstimatedExecutionResources {
214-
let n_steps = compute_blake_hash_steps(felt_size_groups);
214+
let n_steps =
215+
Self::estimate_steps_of_encode_felt252_data_and_calc_blake_hash(felt_size_groups);
215216
let builtin_instance_counter = match felt_size_groups.n_felts() {
216217
// The empty case does not use builtins at all.
217218
0 => HashMap::new(),
218219
// One `range_check` per input felt to validate its size + Overhead for the non empty
219220
// case.
220221
_ => HashMap::from([(
221222
BuiltinName::range_check,
222-
felt_size_groups.n_felts() + blake_estimation::BASE_RANGE_CHECK_NON_EMPTY,
223+
felt_size_groups.n_felts() + Self::BASE_RANGE_CHECK_NON_EMPTY,
223224
)]),
224225
};
225226

@@ -232,90 +233,81 @@ impl EstimateCasmHashResources for CasmV2HashResourceEstimate {
232233
}
233234
}
234235

235-
// Constants used for estimating the cost of BLAKE hashing inside Starknet OS.
236-
// These values are based on empirical measurement by running
237-
// `encode_felt252_data_and_calc_blake_hash` on various combinations of big and small felts.
238-
mod blake_estimation {
239-
// Per-felt step cost (measured).
240-
pub const STEPS_BIG_FELT: usize = 45;
241-
pub const STEPS_SMALL_FELT: usize = 15;
236+
impl CasmV2HashResourceEstimate {
237+
// Constants used for estimating the VM execution resources of BLAKE hashing in the Starknet OS.
238+
// Values were obtained empirically by running
239+
// `encode_felt252_data_and_calc_blake_hash` on various combinations of large and small felts.
242240

243-
// One-time overhead.
244-
// Overhead when input fills a full Blake message (16 u32s).
241+
// Per-felt contribution.
242+
pub const STEPS_PER_LARGE_FELT: usize = 45;
243+
pub const STEPS_PER_SMALL_FELT: usize = 15;
244+
245+
// One-time overheads for `encode_felt252_data_and_calc_blake_hash` execution.
246+
// Applied when the input fills an exact Blake message (16-u32).
245247
pub const BASE_STEPS_FULL_MSG: usize = 217;
246-
// Overhead when input results in a partial message (remainder < 16 u32s).
248+
// Applied when the input leaves a remainder (< 16 u32s).
247249
pub const BASE_STEPS_PARTIAL_MSG: usize = 195;
248-
// Extra steps per 2-u32 remainder in partial messages.
250+
// Extra steps added per 2-u32 remainder in partial messages.
249251
pub const STEPS_PER_2_U32_REMINDER: usize = 3;
250-
// Overhead when input for `encode_felt252_data_and_calc_blake_hash` is non-empty.
252+
// Additional `range_check` instances required when the input is non-empty.
251253
pub const BASE_RANGE_CHECK_NON_EMPTY: usize = 3;
252-
// Empty input steps.
254+
255+
// Applied when the input is completely empty.
253256
pub const STEPS_EMPTY_INPUT: usize = 170;
254-
}
255257

256-
fn base_steps_for_blake_hash(n_u32s: usize) -> usize {
257-
let rem_u32s = n_u32s % FeltSizeCount::U32_WORDS_PER_MESSAGE;
258-
if rem_u32s == 0 {
259-
blake_estimation::BASE_STEPS_FULL_MSG
260-
} else {
261-
// This computation is based on running blake2s with different inputs.
262-
// Note: all inputs expand to an even number of u32s --> `rem_u32s` is always even.
263-
blake_estimation::BASE_STEPS_PARTIAL_MSG
264-
+ (rem_u32s / 2) * blake_estimation::STEPS_PER_2_U32_REMINDER
265-
}
266-
}
258+
/// Estimates the total number of VM steps needed to hash the given felts with Blake in the
259+
/// Starknet OS.
260+
fn estimate_steps_of_encode_felt252_data_and_calc_blake_hash(
261+
felt_size_groups: &FeltSizeCount,
262+
) -> usize {
263+
let encoded_u32_len = felt_size_groups.encoded_u32_len();
264+
if encoded_u32_len == 0 {
265+
// The empty input case is a special case.
266+
return Self::STEPS_EMPTY_INPUT;
267+
}
267268

268-
fn felts_steps(n_big_felts: usize, n_small_felts: usize) -> usize {
269-
let big_steps = n_big_felts
270-
.checked_mul(blake_estimation::STEPS_BIG_FELT)
271-
.expect("Overflow computing big felt steps");
272-
let small_steps = n_small_felts
273-
.checked_mul(blake_estimation::STEPS_SMALL_FELT)
274-
.expect("Overflow computing small felt steps");
275-
big_steps.checked_add(small_steps).expect("Overflow computing total felt steps")
276-
}
269+
// Adds a base cost depending on whether the total fits exactly into full 16-u32 messages.
270+
let base_steps = if encoded_u32_len % FeltSizeCount::U32_WORDS_PER_MESSAGE == 0 {
271+
Self::BASE_STEPS_FULL_MSG
272+
} else {
273+
// This computation is based on running blake2s with different inputs.
274+
// Note: all inputs expand to an even number of u32s --> `rem_u32s` is always even.
275+
Self::BASE_STEPS_PARTIAL_MSG
276+
+ (encoded_u32_len % FeltSizeCount::U32_WORDS_PER_MESSAGE / 2)
277+
* Self::STEPS_PER_2_U32_REMINDER
278+
};
277279

278-
/// Estimates the number of VM steps needed to hash the given felts with Blake in Starknet OS.
279-
/// Each small felt unpacks into 2 u32s, and each big felt into 8 u32s.
280-
/// Adds a base cost depending on whether the total fits exactly into full 16-u32 messages.
281-
fn compute_blake_hash_steps(felt_size_groups: &FeltSizeCount) -> usize {
282-
let total_u32s = felt_size_groups.encoded_u32_len();
283-
if total_u32s == 0 {
284-
// The empty input case is a special case.
285-
return blake_estimation::STEPS_EMPTY_INPUT;
280+
base_steps
281+
+ felt_size_groups.large * Self::STEPS_PER_LARGE_FELT
282+
+ felt_size_groups.small * Self::STEPS_PER_SMALL_FELT
286283
}
287284

288-
let base_steps = base_steps_for_blake_hash(total_u32s);
289-
let felt_steps = felts_steps(felt_size_groups.large, felt_size_groups.small);
290-
291-
base_steps.checked_add(felt_steps).expect("Overflow computing total Blake hash steps")
292-
}
293-
294-
/// Converts the execution resources and blake opcode count to L2 gas.
295-
///
296-
/// Used for both Stwo ("proving_gas") and Stone ("sierra_gas") estimations, which differ in
297-
/// builtin costs. This unified logic is valid because only the `range_check` builtin is used,
298-
/// and its cost is identical across provers (see `bouncer.get_tx_weights`).
299-
// TODO(AvivG): Move inside blake estimation struct.
300-
pub fn blake_execution_resources_estimation_to_gas(
301-
resources: EstimatedExecutionResources,
302-
versioned_constants: &VersionedConstants,
303-
blake_opcode_gas: usize,
304-
) -> GasAmount {
305-
// TODO(AvivG): Remove this once gas computation is separated from resource estimation.
306-
assert!(
307-
resources
308-
.resources()
309-
.builtin_instance_counter
310-
.keys()
311-
.all(|&k| k == BuiltinName::range_check),
312-
"Expected either empty builtins or only `range_check` builtin, got: {:?}. This breaks the \
313-
assumption that builtin costs are identical between provers.",
314-
resources.resources().builtin_instance_counter.keys().collect::<Vec<_>>()
315-
);
316-
317-
resources.to_sierra_gas(
318-
|resources| vm_resources_to_sierra_gas(resources, versioned_constants),
319-
Some(blake_opcode_gas),
320-
)
285+
/// Converts the execution resources and blake opcode count to L2 gas.
286+
///
287+
/// Used for both Stwo ("proving_gas") and Stone ("sierra_gas") estimations, which differ in
288+
/// builtin costs. This unified logic is valid because only the `range_check` builtin is used,
289+
/// and its cost is identical across provers (see `bouncer.get_tx_weights`).
290+
// TODO(AvivG): Move inside blake estimation struct.
291+
pub(crate) fn blake_execution_resources_estimation_to_gas(
292+
resources: EstimatedExecutionResources,
293+
versioned_constants: &VersionedConstants,
294+
blake_opcode_gas: usize,
295+
) -> GasAmount {
296+
// TODO(AvivG): Remove this once gas computation is separated from resource estimation.
297+
assert!(
298+
resources
299+
.resources()
300+
.builtin_instance_counter
301+
.keys()
302+
.all(|&k| k == BuiltinName::range_check),
303+
"Expected either empty builtins or only `range_check` builtin, got: {:?}. This breaks \
304+
the assumption that builtin costs are identical between provers.",
305+
resources.resources().builtin_instance_counter.keys().collect::<Vec<_>>()
306+
);
307+
308+
resources.to_sierra_gas(
309+
|resources| vm_resources_to_sierra_gas(resources, versioned_constants),
310+
Some(blake_opcode_gas),
311+
)
312+
}
321313
}

crates/blockifier/src/execution/casm_hash_estimation_test.rs

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,7 @@ use pretty_assertions::assert_eq;
77
use rstest::rstest;
88
use starknet_types_core::felt::Felt;
99

10-
use crate::execution::casm_hash_estimation::blake_estimation::STEPS_EMPTY_INPUT;
1110
use crate::execution::casm_hash_estimation::{
12-
compute_blake_hash_steps,
1311
CasmV2HashResourceEstimate,
1412
EstimateCasmHashResources,
1513
EstimatedExecutionResources,
@@ -123,8 +121,15 @@ fn test_u32_constants() {
123121
#[test]
124122
fn test_zero_inputs() {
125123
// logic was written.
126-
let steps = compute_blake_hash_steps(&FeltSizeCount { large: 0, small: 0 });
127-
assert_eq!(steps, STEPS_EMPTY_INPUT, "Unexpected base step cost for zero inputs");
124+
let steps =
125+
CasmV2HashResourceEstimate::estimate_steps_of_encode_felt252_data_and_calc_blake_hash(
126+
&FeltSizeCount { large: 0, small: 0 },
127+
);
128+
assert_eq!(
129+
steps,
130+
CasmV2HashResourceEstimate::STEPS_EMPTY_INPUT,
131+
"Unexpected base step cost for zero inputs"
132+
);
128133

129134
// No opcodes should be emitted.
130135
let opcodes = FeltSizeCount { large: 0, small: 0 }.blake_opcode_count();
@@ -136,7 +141,10 @@ fn test_zero_inputs() {
136141
large: 0,
137142
small: 0,
138143
});
139-
let expected = ExecutionResources { n_steps: STEPS_EMPTY_INPUT, ..Default::default() };
144+
let expected = ExecutionResources {
145+
n_steps: CasmV2HashResourceEstimate::STEPS_EMPTY_INPUT,
146+
..Default::default()
147+
};
140148
assert_eq!(resources.resources(), &expected, "Unexpected resources values for zero-input hash");
141149
assert_eq!(resources.blake_count(), 0, "Expected zero BLAKE opcodes for zero inputs");
142150
}

crates/blockifier/src/execution/contract_class.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@ use crate::blockifier_versioned_constants::VersionedConstants;
4343
use crate::bouncer::vm_resources_to_sierra_gas;
4444
use crate::execution::call_info::BuiltinCounterMap;
4545
use crate::execution::casm_hash_estimation::{
46-
blake_execution_resources_estimation_to_gas,
4746
CasmV2HashResourceEstimate,
4847
EstimateCasmHashResources,
4948
};
@@ -596,7 +595,7 @@ fn leaf_cost(
596595
// TODO(AvivG): Call `estimated_resources_of_hash_function` directly, and perform the
597596
// resource→gas conversion only after `estimate_casm_blake_hash_computation_resources`
598597
// executes.
599-
blake_execution_resources_estimation_to_gas(
598+
CasmV2HashResourceEstimate::blake_execution_resources_estimation_to_gas(
600599
CasmV2HashResourceEstimate::estimated_resources_of_hash_function(felt_size_groups),
601600
versioned_constants,
602601
blake_opcode_gas,
@@ -635,7 +634,7 @@ fn node_cost(
635634
// TODO(AvivG): Call `estimated_resources_of_hash_function` directly, and perform the
636635
// resource→gas conversion only after `estimate_casm_blake_hash_computation_resources`
637636
// executes.
638-
let node_hash_cost = blake_execution_resources_estimation_to_gas(
637+
let node_hash_cost = CasmV2HashResourceEstimate::blake_execution_resources_estimation_to_gas(
639638
CasmV2HashResourceEstimate::estimated_resources_of_hash_function(&FeltSizeCount {
640639
large: segs.len(),
641640
small: segs.len(),

0 commit comments

Comments
 (0)