11use std:: collections:: { HashMap , HashSet } ;
2- use std:: sync:: LazyLock ;
2+ use std:: sync:: { Arc , LazyLock } ;
33
44use blockifier:: blockifier_versioned_constants:: {
5+ CallDataFactor ,
56 RawStepGasCost ,
67 RawVersionedConstants ,
78 ResourcesParams ,
@@ -21,9 +22,9 @@ use indexmap::IndexMap;
2122use starknet_api:: block:: StarknetVersion ;
2223use starknet_api:: contract_class:: SierraVersion ;
2324use starknet_api:: core:: { ClassHash , ContractAddress , EthAddress } ;
24- use starknet_api:: executable_transaction:: InvokeTransaction ;
25+ use starknet_api:: executable_transaction:: { InvokeTransaction , TransactionType } ;
2526use starknet_api:: test_utils:: invoke:: invoke_tx;
26- use starknet_api:: transaction:: fields:: ContractAddressSalt ;
27+ use starknet_api:: transaction:: fields:: { Calldata , ContractAddressSalt } ;
2728use starknet_api:: transaction:: { L2ToL1Payload , MessageToL1 } ;
2829use starknet_api:: versioned_constants_logic:: VersionedConstantsTrait ;
2930use starknet_api:: { calldata, declare_tx_args, invoke_tx_args} ;
@@ -172,7 +173,7 @@ async fn setup_test_builder(raw_vc: Option<&RawVersionedConstants>) -> OsResourc
172173 ) ;
173174 test_builder. add_fund_address_tx_with_default_amount ( os_resources_contract_address) ;
174175
175- // Declare and deploy an instance of the stable contract.
176+ // Declare and deploy an instance of the stable contract. Also, fund it.
176177 let stable_contract_sierra = & DEPLOYABLE_FOR_RESOURCE_MEASUREMENT_CONTRACT_SIERRA ;
177178 let stable_contract_casm = & DEPLOYABLE_FOR_RESOURCE_MEASUREMENT_CONTRACT_CASM ;
178179 let stable_contract_class_hash = stable_contract_sierra. calculate_class_hash ( ) ;
@@ -198,6 +199,7 @@ async fn setup_test_builder(raw_vc: Option<&RawVersionedConstants>) -> OsResourc
198199 deploy_from_zero,
199200 ) ;
200201 test_builder. add_invoke_tx ( deploy_tx, None , None ) ;
202+ test_builder. add_fund_address_tx_with_default_amount ( stable_contract_address) ;
201203
202204 // Move on to the next block, so the measurement txs are in their own block.
203205 test_builder. move_to_next_block ( ) ;
@@ -210,6 +212,13 @@ async fn setup_test_builder(raw_vc: Option<&RawVersionedConstants>) -> OsResourc
210212 }
211213}
212214
215+ /// Utility method to create dummy calldata to a cairo Span argument.
216+ fn span_calldata ( n_elements : usize ) -> Calldata {
217+ let mut calldata = vec ! [ Felt :: from( n_elements) ] ;
218+ calldata. extend ( vec ! [ Felt :: ZERO ; n_elements] ) ;
219+ Calldata ( Arc :: new ( calldata) )
220+ }
221+
213222/// Regression test for the list of syscalls called during the fee transfer phase of a transaction.
214223#[ tokio:: test]
215224async fn test_fee_transfer_syscalls ( ) {
@@ -479,3 +488,123 @@ async fn test_os_resources_regression() {
479488 expect_file ! [ VersionedConstants :: json_path( & version) . unwrap( ) ]
480489 . assert_eq ( & raw_vc. to_string_pretty ( ) ) ;
481490}
491+
492+ /// Measures the per-transaction-type overhead of `execute_transaction_inner` in the OS and
493+ /// compares it against the versioned constants.
494+ ///
495+ /// Methodology:
496+ /// - Run a minimal transaction of the given type through the full OS.
497+ /// - Compute: overhead = OS trace resources − blockifier business-logic resources
498+ /// (execute_call_info + validate_call_info).
499+ /// - The remainder is the pure OS scaffolding cost stored under `execute_txs_inner`.
500+ #[ tokio:: test]
501+ async fn test_execute_txs_inner_resources ( ) {
502+ let version = StarknetVersion :: LATEST ;
503+ let mut raw_vc: RawVersionedConstants =
504+ serde_json:: from_str ( VersionedConstants :: json_str ( & version) . unwrap ( ) ) . unwrap ( ) ;
505+ // TODO(Dori): Declare, DeployAccount, L1Handler.
506+ const N_TXS : usize = 2 ;
507+
508+ // For linear factor measurements, it's not enough to just add one more calldata element; the
509+ // increase is not the same per element. The linear scale is on average.
510+ const INVOKE_SCALING_FACTOR : usize = 2 ;
511+ const INVOKE_EXTRA_ARGS : usize = 10 ;
512+
513+ let OsResourcesTestSetup { stable_contract_address, mut test_builder, .. } =
514+ setup_test_builder ( Some ( & raw_vc) ) . await ;
515+
516+ // Invoke.
517+ let invoke_args = invoke_tx_args ! {
518+ sender_address: stable_contract_address,
519+ calldata: calldata![ Felt :: ZERO ] ,
520+ resource_bounds: * NON_TRIVIAL_RESOURCE_BOUNDS ,
521+ nonce: test_builder. next_nonce( stable_contract_address) ,
522+ } ;
523+ test_builder. add_invoke_tx (
524+ InvokeTransaction :: create ( invoke_tx ( invoke_args) , & test_builder. chain_id ( ) ) . unwrap ( ) ,
525+ None ,
526+ None ,
527+ ) ;
528+ // Invoke: scale-factor more calldata elements.
529+ let invoke_args = invoke_tx_args ! {
530+ sender_address: stable_contract_address,
531+ calldata: span_calldata( INVOKE_EXTRA_ARGS ) ,
532+ resource_bounds: * NON_TRIVIAL_RESOURCE_BOUNDS ,
533+ nonce: test_builder. next_nonce( stable_contract_address) ,
534+ } ;
535+ test_builder. add_invoke_tx (
536+ InvokeTransaction :: create ( invoke_tx ( invoke_args) , & test_builder. chain_id ( ) ) . unwrap ( ) ,
537+ None ,
538+ None ,
539+ ) ;
540+
541+ // Execute the business logic and extract the business logic resources for each tx.
542+ let test_runner = test_builder. build ( ) . await ;
543+ let business_logic_resources: [ ExecutionResources ; N_TXS ] = test_runner
544+ . os_hints
545+ . os_input
546+ . os_block_inputs
547+ . last ( )
548+ . unwrap ( )
549+ . tx_execution_infos
550+ . iter ( )
551+ . map ( |exec_info| {
552+ let mut business_logic_resources =
553+ [ exec_info. execute_call_info . as_ref ( ) , exec_info. validate_call_info . as_ref ( ) ]
554+ . into_iter ( )
555+ . flatten ( )
556+ . map ( |ci| ci. resources . vm_resources . clone ( ) )
557+ . fold ( ExecutionResources :: default ( ) , |acc, resources| & acc + & resources) ;
558+ // TODO(Dori): Consider supporting memory-hole counting in the OsLogger. Until then, we
559+ // cannot subtract inner calls with positive memory-hole counts from the OsLogger
560+ // resources.
561+ business_logic_resources. n_memory_holes = 0 ;
562+ business_logic_resources
563+ } )
564+ . collect :: < Vec < _ > > ( )
565+ . try_into ( )
566+ . unwrap ( ) ;
567+
568+ // Run the OS part.
569+ let test_output = test_runner. run ( ) ;
570+ test_output. perform_default_validations ( ) ;
571+
572+ // Fetch the OS resources for each tx.
573+ let [ invoke_first, invoke_second] : [ ExecutionResources ; N_TXS ] = test_output
574+ . runner_output
575+ . txs_trace
576+ . iter ( )
577+ . rev ( )
578+ . take ( N_TXS )
579+ . rev ( )
580+ . map ( |trace| trace. get_resources ( ) . unwrap ( ) . clone ( ) )
581+ . zip ( business_logic_resources)
582+ . map ( |( os_resources, business_logic_resources) | {
583+ ( & os_resources - & business_logic_resources) . filter_unused_builtins ( )
584+ } )
585+ . collect :: < Vec < _ > > ( )
586+ . try_into ( )
587+ . unwrap ( ) ;
588+
589+ // Update the VC with the measurements.
590+ // For transaction types with linear factors, the first call has one linear element (calldata
591+ // length, of zero), so one linear cost must be subtracted from the first measurement to get the
592+ // base cost.
593+ let invoke_linear_factor = ( & ( & invoke_second - & invoke_first) . filter_unused_builtins ( )
594+ * INVOKE_SCALING_FACTOR )
595+ . div_ceil ( INVOKE_EXTRA_ARGS ) ;
596+ raw_vc. os_resources . execute_txs_inner . extend ( [ (
597+ TransactionType :: InvokeFunction ,
598+ VariableResourceParams :: WithFactor ( ResourcesParams {
599+ constant : ( & invoke_first - & invoke_linear_factor) . filter_unused_builtins ( ) ,
600+ calldata_factor : VariableCallDataFactor :: Scaled ( CallDataFactor {
601+ resources : invoke_linear_factor,
602+ scaling_factor : INVOKE_SCALING_FACTOR ,
603+ } ) ,
604+ } ) ,
605+ ) ] ) ;
606+
607+ // Verify computation.
608+ expect_file ! [ VersionedConstants :: json_path( & version) . unwrap( ) ]
609+ . assert_eq ( & raw_vc. to_string_pretty ( ) ) ;
610+ }
0 commit comments