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} ;
@@ -169,7 +170,7 @@ async fn setup_test_builder(raw_vc: Option<&RawVersionedConstants>) -> OsResourc
169170 ) ;
170171 test_builder. add_fund_address_tx_with_default_amount ( os_resources_contract_address) ;
171172
172- // Declare and deploy an instance of the stable contract.
173+ // Declare and deploy an instance of the stable contract. Also, fund it.
173174 let stable_contract_sierra = & DEPLOYABLE_FOR_RESOURCE_MEASUREMENT_CONTRACT_SIERRA ;
174175 let stable_contract_casm = & DEPLOYABLE_FOR_RESOURCE_MEASUREMENT_CONTRACT_CASM ;
175176 let stable_contract_class_hash = stable_contract_sierra. calculate_class_hash ( ) ;
@@ -195,6 +196,7 @@ async fn setup_test_builder(raw_vc: Option<&RawVersionedConstants>) -> OsResourc
195196 deploy_from_zero,
196197 ) ;
197198 test_builder. add_invoke_tx ( deploy_tx, None , None ) ;
199+ test_builder. add_fund_address_tx_with_default_amount ( stable_contract_address) ;
198200
199201 // Move on to the next block, so the measurement txs are in their own block.
200202 test_builder. move_to_next_block ( ) ;
@@ -207,6 +209,13 @@ async fn setup_test_builder(raw_vc: Option<&RawVersionedConstants>) -> OsResourc
207209 }
208210}
209211
212+ /// Utility method to create dummy calldata to a cairo Span argument.
213+ fn span_calldata ( n_elements : usize ) -> Calldata {
214+ let mut calldata = vec ! [ Felt :: from( n_elements) ] ;
215+ calldata. extend ( vec ! [ Felt :: ZERO ; n_elements] ) ;
216+ Calldata ( Arc :: new ( calldata) )
217+ }
218+
210219/// Regression test for the list of syscalls called during the fee transfer phase of a transaction.
211220#[ tokio:: test]
212221async fn test_fee_transfer_syscalls ( ) {
@@ -474,3 +483,123 @@ async fn test_os_resources_regression() {
474483 expect_file ! [ VersionedConstants :: json_path( & version) . unwrap( ) ]
475484 . assert_eq ( & raw_vc. to_string_pretty ( ) ) ;
476485}
486+
487+ /// Measures the per-transaction-type overhead of `execute_transaction_inner` in the OS and
488+ /// compares it against the versioned constants.
489+ ///
490+ /// Methodology:
491+ /// - Run a minimal transaction of the given type through the full OS.
492+ /// - Compute: overhead = OS trace resources − blockifier business-logic resources
493+ /// (execute_call_info + validate_call_info).
494+ /// - The remainder is the pure OS scaffolding cost stored under `execute_txs_inner`.
495+ #[ tokio:: test]
496+ async fn test_execute_txs_inner_resources ( ) {
497+ let version = StarknetVersion :: LATEST ;
498+ let mut raw_vc: RawVersionedConstants =
499+ serde_json:: from_str ( VersionedConstants :: json_str ( & version) . unwrap ( ) ) . unwrap ( ) ;
500+ // TODO(Dori): Declare, DeployAccount, L1Handler.
501+ const N_TXS : usize = 2 ;
502+
503+ // For linear factor measurements, it's not enough to just add one more calldata element; the
504+ // increase is not the same per element. The linear scale is on average.
505+ const INVOKE_SCALING_FACTOR : usize = 2 ;
506+ const INVOKE_EXTRA_ARGS : usize = 10 ;
507+
508+ let OsResourcesTestSetup { stable_contract_address, mut test_builder, .. } =
509+ setup_test_builder ( Some ( & raw_vc) ) . await ;
510+
511+ // Invoke.
512+ let invoke_args = invoke_tx_args ! {
513+ sender_address: stable_contract_address,
514+ calldata: calldata![ Felt :: ZERO ] ,
515+ resource_bounds: * NON_TRIVIAL_RESOURCE_BOUNDS ,
516+ nonce: test_builder. next_nonce( stable_contract_address) ,
517+ } ;
518+ test_builder. add_invoke_tx (
519+ InvokeTransaction :: create ( invoke_tx ( invoke_args) , & test_builder. chain_id ( ) ) . unwrap ( ) ,
520+ None ,
521+ None ,
522+ ) ;
523+ // Invoke: scale-factor more calldata elements.
524+ let invoke_args = invoke_tx_args ! {
525+ sender_address: stable_contract_address,
526+ calldata: span_calldata( INVOKE_EXTRA_ARGS ) ,
527+ resource_bounds: * NON_TRIVIAL_RESOURCE_BOUNDS ,
528+ nonce: test_builder. next_nonce( stable_contract_address) ,
529+ } ;
530+ test_builder. add_invoke_tx (
531+ InvokeTransaction :: create ( invoke_tx ( invoke_args) , & test_builder. chain_id ( ) ) . unwrap ( ) ,
532+ None ,
533+ None ,
534+ ) ;
535+
536+ // Execute the business logic and extract the business logic resources for each tx.
537+ let test_runner = test_builder. build ( ) . await ;
538+ let business_logic_resources: [ ExecutionResources ; N_TXS ] = test_runner
539+ . os_hints
540+ . os_input
541+ . os_block_inputs
542+ . last ( )
543+ . unwrap ( )
544+ . tx_execution_infos
545+ . iter ( )
546+ . map ( |exec_info| {
547+ let mut business_logic_resources =
548+ [ exec_info. execute_call_info . as_ref ( ) , exec_info. validate_call_info . as_ref ( ) ]
549+ . into_iter ( )
550+ . flatten ( )
551+ . map ( |ci| ci. resources . vm_resources . clone ( ) )
552+ . fold ( ExecutionResources :: default ( ) , |acc, resources| & acc + & resources) ;
553+ // TODO(Dori): Consider supporting memory-hole counting in the OsLogger. Until then, we
554+ // cannot subtract inner calls with positive memory-hole counts from the OsLogger
555+ // resources.
556+ business_logic_resources. n_memory_holes = 0 ;
557+ business_logic_resources
558+ } )
559+ . collect :: < Vec < _ > > ( )
560+ . try_into ( )
561+ . unwrap ( ) ;
562+
563+ // Run the OS part.
564+ let test_output = test_runner. run ( ) ;
565+ test_output. perform_default_validations ( ) ;
566+
567+ // Fetch the OS resources for each tx.
568+ let [ invoke_first, invoke_second] : [ ExecutionResources ; N_TXS ] = test_output
569+ . runner_output
570+ . txs_trace
571+ . iter ( )
572+ . rev ( )
573+ . take ( N_TXS )
574+ . rev ( )
575+ . map ( |trace| trace. get_resources ( ) . unwrap ( ) . clone ( ) )
576+ . zip ( business_logic_resources)
577+ . map ( |( os_resources, business_logic_resources) | {
578+ ( & os_resources - & business_logic_resources) . filter_unused_builtins ( )
579+ } )
580+ . collect :: < Vec < _ > > ( )
581+ . try_into ( )
582+ . unwrap ( ) ;
583+
584+ // Update the VC with the measurements.
585+ // For transaction types with linear factors, the first call has one linear element (calldata
586+ // length, of zero), so one linear cost must be subtracted from the first measurement to get the
587+ // base cost.
588+ let invoke_linear_factor = ( & ( & invoke_second - & invoke_first) . filter_unused_builtins ( )
589+ * INVOKE_SCALING_FACTOR )
590+ . div_ceil ( INVOKE_EXTRA_ARGS ) ;
591+ raw_vc. os_resources . execute_txs_inner . extend ( [ (
592+ TransactionType :: InvokeFunction ,
593+ VariableResourceParams :: WithFactor ( ResourcesParams {
594+ constant : ( & invoke_first - & invoke_linear_factor) . filter_unused_builtins ( ) ,
595+ calldata_factor : VariableCallDataFactor :: Scaled ( CallDataFactor {
596+ resources : invoke_linear_factor,
597+ scaling_factor : INVOKE_SCALING_FACTOR ,
598+ } ) ,
599+ } ) ,
600+ ) ] ) ;
601+
602+ // Verify computation.
603+ expect_file ! [ VersionedConstants :: json_path( & version) . unwrap( ) ]
604+ . assert_eq ( & raw_vc. to_string_pretty ( ) ) ;
605+ }
0 commit comments