@@ -148,15 +148,14 @@ impl From<(ExecutionResources, HashVersion)> for EstimatedExecutionResources {
148
148
/// This provides resource estimates rather than exact values.
149
149
// TODO(AvivG): Remove allow once used.
150
150
#[ allow( unused) ]
151
- trait EstimateCasmHashResources {
151
+ pub ( crate ) trait EstimateCasmHashResources {
152
152
/// Specifies the hash function variant that the estimate is for.
153
153
fn hash_version ( & self ) -> HashVersion ;
154
154
155
155
/// Estimates the Cairo execution resources used when applying the hash function during CASM
156
156
/// hashing.
157
157
fn estimated_resources_of_hash_function (
158
- & mut self ,
159
- _felt_count : FeltSizeCount ,
158
+ _felt_size_groups : & FeltSizeCount ,
160
159
) -> EstimatedExecutionResources ;
161
160
162
161
/// Estimates the Cairo execution resources for `compiled_class_hash` in the
@@ -173,158 +172,142 @@ trait EstimateCasmHashResources {
173
172
174
173
// TODO(AvivG): Remove allow once used.
175
174
#[ allow( unused) ]
176
- struct CasmV1HashResourceEstimate { }
175
+ pub ( crate ) struct CasmV1HashResourceEstimate { }
177
176
178
177
impl EstimateCasmHashResources for CasmV1HashResourceEstimate {
179
178
fn hash_version ( & self ) -> HashVersion {
180
179
HashVersion :: V1
181
180
}
182
181
183
182
fn estimated_resources_of_hash_function (
184
- & mut self ,
185
- felt_count : FeltSizeCount ,
183
+ felt_size_groups : & FeltSizeCount ,
186
184
) -> EstimatedExecutionResources {
187
185
EstimatedExecutionResources :: V1Hash {
188
186
// TODO(AvivG): Consider inlining `poseidon_hash_many_cost` logic here.
189
- resources : poseidon_hash_many_cost ( felt_count . n_felts ( ) ) ,
187
+ resources : poseidon_hash_many_cost ( felt_size_groups . n_felts ( ) ) ,
190
188
}
191
189
}
192
190
}
193
191
194
192
// TODO(AvivG): Remove allow once used.
195
193
#[ allow( unused) ]
196
- struct CasmV2HashResourceEstimate { }
194
+ pub ( crate ) struct CasmV2HashResourceEstimate { }
197
195
198
196
impl EstimateCasmHashResources for CasmV2HashResourceEstimate {
199
197
fn hash_version ( & self ) -> HashVersion {
200
198
HashVersion :: V2
201
199
}
202
200
201
+ /// Estimates resource usage for `encode_felt252_data_and_calc_blake_hash` in the Starknet OS.
202
+ ///
203
+ /// # Encoding Details
204
+ /// - Small felts → 2 `u32`s each; Big felts → 8 `u32`s each.
205
+ /// - Each felt requires one `range_check` operation.
206
+ ///
207
+ /// # Returns:
208
+ /// - `ExecutionResources`: VM resource usage (e.g., n_steps, range checks).
209
+ /// - `usize`: number of Blake opcodes used, accounted for separately as those are not reported
210
+ /// via `ExecutionResources`.
203
211
fn estimated_resources_of_hash_function (
204
- & mut self ,
205
- _felt_count : FeltSizeCount ,
212
+ felt_size_groups : & FeltSizeCount ,
206
213
) -> EstimatedExecutionResources {
207
- // TODO(AvivG): Use `cost_of_encode_felt252_data_and_calc_blake_hash` once it returns ER.
208
- EstimatedExecutionResources :: new ( HashVersion :: V2 )
214
+ let n_steps =
215
+ Self :: estimate_steps_of_encode_felt252_data_and_calc_blake_hash ( felt_size_groups) ;
216
+ let builtin_instance_counter = match felt_size_groups. n_felts ( ) {
217
+ // The empty case does not use builtins at all.
218
+ 0 => HashMap :: new ( ) ,
219
+ // One `range_check` per input felt to validate its size + Overhead for the non empty
220
+ // case.
221
+ _ => HashMap :: from ( [ (
222
+ BuiltinName :: range_check,
223
+ felt_size_groups. n_felts ( ) + Self :: BASE_RANGE_CHECK_NON_EMPTY ,
224
+ ) ] ) ,
225
+ } ;
226
+
227
+ let resources = ExecutionResources { n_steps, n_memory_holes : 0 , builtin_instance_counter } ;
228
+
229
+ EstimatedExecutionResources :: V2Hash {
230
+ resources,
231
+ blake_count : felt_size_groups. blake_opcode_count ( ) ,
232
+ }
209
233
}
210
234
}
211
235
212
- // Constants used for estimating the cost of BLAKE hashing inside Starknet OS.
213
- // These values are based on empirical measurement by running
214
- // `encode_felt252_data_and_calc_blake_hash` on various combinations of big and small felts.
215
- mod blake_estimation {
216
- // Per-felt step cost (measured).
217
- pub const STEPS_BIG_FELT : usize = 45 ;
218
- 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.
219
240
220
- // One-time overhead.
221
- // 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).
222
247
pub const BASE_STEPS_FULL_MSG : usize = 217 ;
223
- // Overhead when input results in a partial message ( remainder < 16 u32s).
248
+ // Applied when the input leaves a remainder ( < 16 u32s).
224
249
pub const BASE_STEPS_PARTIAL_MSG : usize = 195 ;
225
- // Extra steps per 2-u32 remainder in partial messages.
250
+ // Extra steps added per 2-u32 remainder in partial messages.
226
251
pub const STEPS_PER_2_U32_REMINDER : usize = 3 ;
227
- // 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.
228
253
pub const BASE_RANGE_CHECK_NON_EMPTY : usize = 3 ;
229
- // Empty input steps.
230
- pub const STEPS_EMPTY_INPUT : usize = 170 ;
231
- }
232
254
233
- fn base_steps_for_blake_hash ( n_u32s : usize ) -> usize {
234
- let rem_u32s = n_u32s % FeltSizeCount :: U32_WORDS_PER_MESSAGE ;
235
- if rem_u32s == 0 {
236
- blake_estimation:: BASE_STEPS_FULL_MSG
237
- } else {
238
- // This computation is based on running blake2s with different inputs.
239
- // Note: all inputs expand to an even number of u32s --> `rem_u32s` is always even.
240
- blake_estimation:: BASE_STEPS_PARTIAL_MSG
241
- + ( rem_u32s / 2 ) * blake_estimation:: STEPS_PER_2_U32_REMINDER
242
- }
243
- }
255
+ // Applied when the input is completely empty.
256
+ pub const STEPS_EMPTY_INPUT : usize = 170 ;
244
257
245
- fn felts_steps ( n_big_felts : usize , n_small_felts : usize ) -> usize {
246
- let big_steps = n_big_felts
247
- . checked_mul ( blake_estimation:: STEPS_BIG_FELT )
248
- . expect ( "Overflow computing big felt steps" ) ;
249
- let small_steps = n_small_felts
250
- . checked_mul ( blake_estimation:: STEPS_SMALL_FELT )
251
- . expect ( "Overflow computing small felt steps" ) ;
252
- big_steps. checked_add ( small_steps) . expect ( "Overflow computing total felt steps" )
253
- }
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
+ }
254
268
255
- /// Estimates the number of VM steps needed to hash the given felts with Blake in Starknet OS.
256
- /// Each small felt unpacks into 2 u32s, and each big felt into 8 u32s.
257
- /// Adds a base cost depending on whether the total fits exactly into full 16-u32 messages.
258
- fn compute_blake_hash_steps ( felt_size_groups : & FeltSizeCount ) -> usize {
259
- let total_u32s = felt_size_groups. encoded_u32_len ( ) ;
260
- if total_u32s == 0 {
261
- // The empty input case is a special case.
262
- return blake_estimation:: STEPS_EMPTY_INPUT ;
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
+ } ;
279
+
280
+ base_steps
281
+ + felt_size_groups. large * Self :: STEPS_PER_LARGE_FELT
282
+ + felt_size_groups. small * Self :: STEPS_PER_SMALL_FELT
263
283
}
264
284
265
- let base_steps = base_steps_for_blake_hash ( total_u32s) ;
266
- let felt_steps = felts_steps ( felt_size_groups. large , felt_size_groups. small ) ;
267
-
268
- base_steps. checked_add ( felt_steps) . expect ( "Overflow computing total Blake hash steps" )
269
- }
270
-
271
- /// Estimates resource usage for `encode_felt252_data_and_calc_blake_hash` in the Starknet OS.
272
- ///
273
- /// # Encoding Details
274
- /// - Small felts → 2 `u32`s each; Big felts → 8 `u32`s each.
275
- /// - Each felt requires one `range_check` operation.
276
- ///
277
- /// # Returns:
278
- /// - `ExecutionResources`: VM resource usage (e.g., n_steps, range checks).
279
- /// - `usize`: number of Blake opcodes used, accounted for separately as those are not reported via
280
- /// `ExecutionResources`.
281
- pub fn encode_and_blake_hash_resources (
282
- felt_size_groups : & FeltSizeCount ,
283
- ) -> EstimatedExecutionResources {
284
- let n_steps = compute_blake_hash_steps ( felt_size_groups) ;
285
- let builtin_instance_counter = match felt_size_groups. n_felts ( ) {
286
- // The empty case does not use builtins at all.
287
- 0 => HashMap :: new ( ) ,
288
- // One `range_check` per input felt to validate its size + Overhead for the non empty case.
289
- _ => HashMap :: from ( [ (
290
- BuiltinName :: range_check,
291
- felt_size_groups. n_felts ( ) + blake_estimation:: BASE_RANGE_CHECK_NON_EMPTY ,
292
- ) ] ) ,
293
- } ;
294
-
295
- let resources = ExecutionResources { n_steps, n_memory_holes : 0 , builtin_instance_counter } ;
296
-
297
- EstimatedExecutionResources :: V2Hash {
298
- resources,
299
- blake_count : felt_size_groups. blake_opcode_count ( ) ,
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
+ )
300
312
}
301
313
}
302
-
303
- /// Converts the execution resources and blake opcode count to L2 gas.
304
- ///
305
- /// Used for both Stwo ("proving_gas") and Stone ("sierra_gas") estimations, which differ in
306
- /// builtin costs. This unified logic is valid because only the `range_check` builtin is used,
307
- /// and its cost is identical across provers (see `bouncer.get_tx_weights`).
308
- // TODO(AvivG): Move inside blake estimation struct.
309
- pub fn blake_execution_resources_estimation_to_gas (
310
- resources : EstimatedExecutionResources ,
311
- versioned_constants : & VersionedConstants ,
312
- blake_opcode_gas : usize ,
313
- ) -> GasAmount {
314
- // TODO(AvivG): Remove this once gas computation is separated from resource estimation.
315
- assert ! (
316
- resources
317
- . resources( )
318
- . builtin_instance_counter
319
- . keys( )
320
- . all( |& k| k == BuiltinName :: range_check) ,
321
- "Expected either empty builtins or only `range_check` builtin, got: {:?}. This breaks the \
322
- assumption that builtin costs are identical between provers.",
323
- resources. resources( ) . builtin_instance_counter. keys( ) . collect:: <Vec <_>>( )
324
- ) ;
325
-
326
- resources. to_sierra_gas (
327
- |resources| vm_resources_to_sierra_gas ( resources, versioned_constants) ,
328
- Some ( blake_opcode_gas) ,
329
- )
330
- }
0 commit comments