@@ -2,14 +2,17 @@ use std::collections::{HashMap, HashSet};
22
33use assert_matches:: assert_matches;
44use cairo_vm:: types:: builtin_name:: BuiltinName ;
5+ use cairo_vm:: vm:: runners:: cairo_runner:: ExecutionResources ;
56use rstest:: rstest;
7+ use starknet_api:: core:: ClassHash ;
68use starknet_api:: execution_resources:: GasAmount ;
79use starknet_api:: transaction:: fields:: Fee ;
810use starknet_api:: { class_hash, contract_address, storage_key} ;
911
1012use super :: BouncerConfig ;
1113use crate :: blockifier:: transaction_executor:: TransactionExecutorError ;
1214use crate :: bouncer:: {
15+ get_tx_weights,
1316 verify_tx_weights_within_max_capacity,
1417 Bouncer ,
1518 BouncerWeights ,
@@ -18,9 +21,12 @@ use crate::bouncer::{
1821use crate :: context:: BlockContext ;
1922use crate :: execution:: call_info:: ExecutionSummary ;
2023use crate :: fee:: resources:: { ComputationResources , TransactionResources } ;
21- use crate :: state:: cached_state:: { StateChangesKeys , TransactionalState } ;
24+ use crate :: state:: cached_state:: { StateChangesKeys , StateMaps , TransactionalState } ;
25+ use crate :: test_utils:: contracts:: FeatureContract ;
2226use crate :: test_utils:: initial_test_state:: test_state;
27+ use crate :: test_utils:: { CairoVersion , RunnableCairo1 } ;
2328use crate :: transaction:: errors:: TransactionExecutionError ;
29+ use crate :: utils:: { add_maps, u64_from_usize} ;
2430
2531#[ test]
2632fn test_block_weights_has_room ( ) {
@@ -239,3 +245,125 @@ fn test_transaction_too_large_sierra_gas_based() {
239245 )
240246 ) if * max_capacity == bouncer_config. block_max_capacity && * tx_size == expected_weights) ;
241247}
248+
249+ /// Verifies that the difference between proving gas and Sierra gas
250+ /// is fully accounted for by the builtin gas delta (Stone vs Stwo).
251+ ///
252+ /// Covers combinations of OS computation builtins and CASM hash computation builtins.
253+ #[ rstest]
254+ #[ case:: tx_builtins_only( & [ ] , ExecutionResources :: default ( ) ) ]
255+ #[ case:: tx_builtins_plus_os_builtins(
256+ & [ ] ,
257+ ExecutionResources {
258+ builtin_instance_counter: HashMap :: from( [
259+ ( BuiltinName :: bitwise, 1 ) ,
260+ ] ) ,
261+ ..Default :: default ( )
262+ } ,
263+ ) ]
264+ #[ case:: tx_builtins_plus_casm_hash_computation(
265+ & [
266+ ( FeatureContract :: TestContract ( CairoVersion :: Cairo0 ) , 1 ) ,
267+ ( FeatureContract :: TestContract ( CairoVersion :: Cairo1 ( RunnableCairo1 :: Casm ) ) , 1 ) ,
268+ ] ,
269+ ExecutionResources :: default ( ) ,
270+ ) ]
271+ #[ case:: tx_builtins_plus_os_builtins_plus_casm_hash_computation(
272+ & [
273+ ( FeatureContract :: TestContract ( CairoVersion :: Cairo0 ) , 1 ) ,
274+ ( FeatureContract :: TestContract ( CairoVersion :: Cairo1 ( RunnableCairo1 :: Casm ) ) , 1 ) ,
275+ ] ,
276+ ExecutionResources {
277+ builtin_instance_counter: HashMap :: from( [
278+ ( BuiltinName :: range_check, 1 ) ,
279+ ( BuiltinName :: bitwise, 2 ) ,
280+ ] ) ,
281+ ..Default :: default ( )
282+ } ,
283+ ) ]
284+ fn test_proving_gas_minus_sierra_gas_equals_builtin_gas (
285+ #[ case] contract_instances : & [ ( FeatureContract , u16 ) ] ,
286+ #[ case] os_vm_resources : ExecutionResources ,
287+ ) {
288+ let block_context = BlockContext :: create_for_account_testing ( ) ;
289+ let state = test_state ( & block_context. chain_info , Fee ( 0 ) , contract_instances) ;
290+
291+ // Derive executed_class_hashes from contract_instances
292+ let executed_class_hashes: HashSet < ClassHash > =
293+ contract_instances. iter ( ) . map ( |( contract, _) | contract. get_class_hash ( ) ) . collect ( ) ;
294+
295+ // Create CASM hash computation builtins only in case CASM computation aren't trivial.
296+ let casm_hash_computation_builtins = if contract_instances. is_empty ( ) {
297+ HashMap :: new ( )
298+ } else {
299+ HashMap :: from ( [ ( BuiltinName :: poseidon, 11699 ) , ( BuiltinName :: pedersen, 2205 ) ] )
300+ } ;
301+
302+ // Transaction builtin counters.
303+ let mut tx_builtin_counters =
304+ HashMap :: from ( [ ( BuiltinName :: range_check, 2 ) , ( BuiltinName :: pedersen, 1 ) ] ) ;
305+
306+ let tx_resources = TransactionResources {
307+ computation : ComputationResources {
308+ sierra_gas : GasAmount :: ZERO ,
309+ tx_vm_resources : ExecutionResources {
310+ builtin_instance_counter : tx_builtin_counters. clone ( ) ,
311+ ..Default :: default ( )
312+ } ,
313+ os_vm_resources : os_vm_resources. clone ( ) ,
314+ ..Default :: default ( )
315+ } ,
316+ ..Default :: default ( )
317+ } ;
318+ // The os additional resources contains both patricia updates and
319+ // CASM hash computation.
320+ let n_visited_storage_entries = if casm_hash_computation_builtins. is_empty ( ) { 0 } else { 1 } ;
321+
322+ let result = get_tx_weights (
323+ & state,
324+ & executed_class_hashes,
325+ n_visited_storage_entries,
326+ & tx_resources,
327+ & StateMaps :: default ( ) . keys ( ) , // state changes keys
328+ & block_context. versioned_constants ,
329+ & tx_builtin_counters,
330+ & block_context. bouncer_config . builtin_weights ,
331+ )
332+ . unwrap ( ) ;
333+
334+ // Combine TX + TX overhead (OS) + CASM and patricia builtin usage.
335+ add_maps ( & mut tx_builtin_counters, & os_vm_resources. builtin_instance_counter ) ;
336+ add_maps ( & mut tx_builtin_counters, & casm_hash_computation_builtins) ;
337+
338+ // Compute expected gas delta from builtin delta (Stwo - Stone).
339+ let expected_builtin_gas_delta = tx_builtin_counters
340+ . iter ( )
341+ . map ( |( name, count) | {
342+ let stwo_gas = block_context. bouncer_config . builtin_weights . builtin_weight ( name) ;
343+ let stone_gas = block_context
344+ . versioned_constants
345+ . os_constants
346+ . gas_costs
347+ . builtins
348+ . get_builtin_gas_cost ( name)
349+ . unwrap ( ) ;
350+
351+ let stwo_total = stwo_gas. checked_mul ( * count) . map ( u64_from_usize) . expect ( "overflow" ) ;
352+ let stone_total = u64_from_usize ( * count) . checked_mul ( stone_gas) . expect ( "overflow" ) ;
353+
354+ // This assumes that the Stone gas is always less than or equal to Stwo gas.
355+ stwo_total. checked_sub ( stone_total) . expect ( "underflow" )
356+ } )
357+ // Sum the deltas.
358+ . try_fold ( 0u64 , |acc, val| acc. checked_add ( val) )
359+ . expect ( "overflow in sum" ) ;
360+
361+ assert_eq ! (
362+ result. proving_gas. 0 - result. sierra_gas. 0 ,
363+ expected_builtin_gas_delta,
364+ "Proving gas: {} - Sierra gas: {} ≠ builtins gap: {}" ,
365+ result. proving_gas. 0 ,
366+ result. sierra_gas. 0 ,
367+ expected_builtin_gas_delta
368+ ) ;
369+ }
0 commit comments