@@ -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,139 @@ 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:: no_os_no_casm(
255+ & [ ] ,
256+ ExecutionResources :: default ( ) ,
257+ HashSet :: new( ) ,
258+ HashMap :: new( ) ,
259+ ) ]
260+ #[ case:: with_os_only(
261+ & [ ] ,
262+ ExecutionResources {
263+ builtin_instance_counter: HashMap :: from( [
264+ ( BuiltinName :: bitwise, 1 ) ,
265+ ] ) ,
266+ ..Default :: default ( )
267+ } ,
268+ HashSet :: new( ) ,
269+ HashMap :: new( ) ,
270+ ) ]
271+ #[ case:: with_casm_only(
272+ & [
273+ ( FeatureContract :: TestContract ( CairoVersion :: Cairo0 ) , 1 ) ,
274+ ( FeatureContract :: TestContract ( CairoVersion :: Cairo1 ( RunnableCairo1 :: Casm ) ) , 1 ) ,
275+ ] ,
276+ ExecutionResources :: default ( ) ,
277+ HashSet :: from( [
278+ FeatureContract :: TestContract ( CairoVersion :: Cairo0 ) . get_class_hash( ) ,
279+ FeatureContract :: TestContract ( CairoVersion :: Cairo1 ( RunnableCairo1 :: Casm ) ) . get_class_hash( ) ,
280+ ] ) ,
281+ // Builtins mapping computed by printing `additional_os_resources.builtin_instance_counter`
282+ // in `get_tx_weights` (bouncer_test.rs) for the above contracts.
283+ HashMap :: from( [
284+ ( BuiltinName :: poseidon, 11699 ) ,
285+ ( BuiltinName :: pedersen, 2205 ) ,
286+ ] ) ,
287+ ) ]
288+ #[ case:: with_os_and_casm(
289+ & [
290+ ( FeatureContract :: TestContract ( CairoVersion :: Cairo0 ) , 1 ) ,
291+ ( FeatureContract :: TestContract ( CairoVersion :: Cairo1 ( RunnableCairo1 :: Casm ) ) , 1 ) ,
292+ ] ,
293+ ExecutionResources {
294+ builtin_instance_counter: HashMap :: from( [
295+ ( BuiltinName :: range_check, 1 ) ,
296+ ( BuiltinName :: bitwise, 2 ) ,
297+ ] ) ,
298+ ..Default :: default ( )
299+ } ,
300+ HashSet :: from( [
301+ FeatureContract :: TestContract ( CairoVersion :: Cairo0 ) . get_class_hash( ) ,
302+ FeatureContract :: TestContract ( CairoVersion :: Cairo1 ( RunnableCairo1 :: Casm ) ) . get_class_hash( ) ,
303+ ] ) ,
304+ HashMap :: from( [
305+ ( BuiltinName :: poseidon, 11699 ) ,
306+ ( BuiltinName :: pedersen, 2205 ) ,
307+ ] ) ,
308+ ) ]
309+ fn test_proving_gas_minus_sierra_gas_equals_builtin_gas (
310+ #[ case] contract_instances : & [ ( FeatureContract , u16 ) ] ,
311+ #[ case] os_vm_resources : ExecutionResources ,
312+ #[ case] executed_class_hashes : HashSet < ClassHash > ,
313+ #[ case] casm_hash_computation_builtins : HashMap < BuiltinName , usize > ,
314+ ) {
315+ let block_context = BlockContext :: create_for_account_testing ( ) ;
316+ let state = test_state ( & block_context. chain_info , Fee ( 0 ) , contract_instances) ;
317+
318+ // Transaction builtin counters.
319+ let mut tx_builtin_counters =
320+ HashMap :: from ( [ ( BuiltinName :: range_check, 2 ) , ( BuiltinName :: pedersen, 1 ) ] ) ;
321+
322+ let tx_resources = TransactionResources {
323+ computation : ComputationResources {
324+ sierra_gas : GasAmount ( 100 ) ,
325+ tx_vm_resources : ExecutionResources {
326+ builtin_instance_counter : tx_builtin_counters. clone ( ) ,
327+ ..Default :: default ( )
328+ } ,
329+ os_vm_resources : os_vm_resources. clone ( ) ,
330+ ..Default :: default ( )
331+ } ,
332+ ..Default :: default ( )
333+ } ;
334+ // If we simulate CASM hash computation, we assume storage entries were visited.
335+ let n_visited_storage_entries = if casm_hash_computation_builtins. is_empty ( ) { 0 } else { 1 } ;
336+
337+ let result = get_tx_weights (
338+ & state,
339+ & executed_class_hashes,
340+ n_visited_storage_entries,
341+ & tx_resources,
342+ & StateMaps :: default ( ) . keys ( ) , // state changes keys
343+ & block_context. versioned_constants ,
344+ & tx_builtin_counters,
345+ & block_context. bouncer_config . builtin_weights ,
346+ )
347+ . unwrap ( ) ;
348+
349+ // Combine TX + OS + CASM builtin usage.
350+ add_maps ( & mut tx_builtin_counters, & os_vm_resources. builtin_instance_counter ) ;
351+ add_maps ( & mut tx_builtin_counters, & casm_hash_computation_builtins) ;
352+
353+ // Compute expected gas delta from builtin delta (Stwo - Stone).
354+ let expected_builtin_gas_delta = tx_builtin_counters
355+ . iter ( )
356+ . map ( |( name, count) | {
357+ let swo_gas = block_context. bouncer_config . builtin_weights . builtin_weight ( name) ;
358+ let stone_gas = block_context
359+ . versioned_constants
360+ . os_constants
361+ . gas_costs
362+ . builtins
363+ . get_builtin_gas_cost ( name)
364+ . expect ( ) ;
365+
366+ let stwo_total = swo_gas. checked_mul ( * count) . map ( u64_from_usize) . expect ( "overflow" ) ;
367+ let stone_total = u64_from_usize ( * count) . checked_mul ( stone_gas) . expect ( "overflow" ) ;
368+
369+ stwo_total. checked_sub ( stone_total) . expect ( "underflow" )
370+ } )
371+ // Sum the deltas.
372+ . try_fold ( 0u64 , |acc, val| acc. checked_add ( val) )
373+ . expect ( "overflow in sum" ) ;
374+
375+ assert_eq ! (
376+ result. proving_gas. 0 - result. sierra_gas. 0 ,
377+ expected_builtin_gas_delta,
378+ "Proving gas: {} - Sierra gas: {} ≠ builtins gap: {}" ,
379+ result. proving_gas. 0 ,
380+ result. sierra_gas. 0 ,
381+ expected_builtin_gas_delta
382+ ) ;
383+ }
0 commit comments