@@ -79,6 +79,21 @@ const UNMEASURABLE_SYSCALLS: [Selector; 31] = [
7979
8080const SYSCALLS_WITH_LINEAR_FACTOR : [ Selector ; 2 ] = [ Selector :: Deploy , Selector :: MetaTxV0 ] ;
8181
82+ /// Expected syscalls in the fee transfer call. Should be removed from the list of syscalls during
83+ /// measurement iteration - only the syscalls called during __execute__ should be measured.
84+ const FEE_TRANSFER_SYSCALLS : [ Selector ; 10 ] = [
85+ Selector :: GetExecutionInfo ,
86+ Selector :: StorageRead ,
87+ Selector :: StorageRead ,
88+ Selector :: StorageWrite ,
89+ Selector :: StorageWrite ,
90+ Selector :: StorageRead ,
91+ Selector :: StorageRead ,
92+ Selector :: StorageWrite ,
93+ Selector :: StorageWrite ,
94+ Selector :: EmitEvent ,
95+ ] ;
96+
8297/// Measure the OS overhead for each syscall, and compare the results with the latest VC.
8398///
8499/// This test relies on the [starknet_os::hint_processor::os_logger::OsLogger] to capture the
@@ -104,6 +119,46 @@ const SYSCALLS_WITH_LINEAR_FACTOR: [Selector; 2] = [Selector::Deploy, Selector::
104119/// the measurements, we use a stable dummy contract (that is not recompiled when the Cairo1
105120/// compiler's version changes), and we set the `deploy_from_zero` flag to `true` to make sure
106121/// changes in the deploying contract address are not reflected in the measurements.
122+ #[ tokio:: test]
123+ async fn test_fee_transfer_syscalls ( ) {
124+ let os_resources_contract = FeatureContract :: OsResourcesTest ( RunnableCairo1 :: Casm ) ;
125+ let ( mut builder, [ os_resources_contract_address] ) =
126+ TestBuilder :: create_standard ( [ ( os_resources_contract, calldata ! [ Felt :: ZERO ] ) ] ) . await ;
127+
128+ // Fund the contract - it will be used as the account.
129+ // Then, move on to the next block, so the syscall-measurement tx is in it's own block.
130+ builder. add_fund_address_tx_with_default_amount ( os_resources_contract_address) ;
131+
132+ // Invoke from the OS resources contract, with zeros as calldata, to make the __execute__ do
133+ // nothing. All resulting events should be from the fee transfer call.
134+ builder. add_invoke_tx (
135+ InvokeTransaction :: create (
136+ invoke_tx ( invoke_tx_args ! {
137+ sender_address: os_resources_contract_address,
138+ calldata: calldata![ Felt :: ZERO , Felt :: ZERO , Felt :: ZERO ] ,
139+ resource_bounds: * NON_TRIVIAL_RESOURCE_BOUNDS ,
140+ } ) ,
141+ & builder. chain_id ( ) ,
142+ )
143+ . unwrap ( ) ,
144+ None ,
145+ None ,
146+ ) ;
147+
148+ // Build, run, and get the syscalls list.
149+ let test_output = builder. build_and_run ( ) . await ;
150+ let syscalls = test_output
151+ . runner_output
152+ . txs_trace
153+ . last ( )
154+ . unwrap ( )
155+ . get_syscalls ( )
156+ . iter ( )
157+ . map ( |syscall_trace| syscall_trace. get_selector ( ) )
158+ . collect :: < Vec < _ > > ( ) ;
159+ assert_eq ! ( syscalls, FEE_TRANSFER_SYSCALLS . to_vec( ) ) ;
160+ }
161+
107162#[ tokio:: test]
108163async fn test_os_resources_regression ( ) {
109164 let os_resources_contract = FeatureContract :: OsResourcesTest ( RunnableCairo1 :: Casm ) ;
@@ -214,26 +269,23 @@ async fn test_os_resources_regression() {
214269 let mut inner_calls_iter = inner_calls. into_iter ( ) ;
215270
216271 // Extract syscall resources consumed per syscall.
217- // There should be two events emitted: the first is the syscall we are measuring, and the second
218- // is the last syscall in the tx, emitted from the fee transfer. Pop the second event.
219- let mut syscall_traces =
220- test_output. runner_output . txs_trace . last ( ) . unwrap ( ) . get_syscalls ( ) . clone ( ) ;
221- assert ! ( !UNMEASURABLE_SYSCALLS . contains( & Selector :: EmitEvent ) ) ;
272+ // Remove the fee transfer syscalls from the list by splitting the iterator into two. The second
273+ // part is the last `FEE_TRANSFER_SYSCALLS.len()` syscalls, and the first part should be the
274+ // rest.
275+ let all_syscalls = test_output. runner_output . txs_trace . last ( ) . unwrap ( ) . get_syscalls ( ) . clone ( ) ;
276+ let ( syscall_traces, fee_transfer_syscall_traces) =
277+ all_syscalls. split_at ( all_syscalls. len ( ) - FEE_TRANSFER_SYSCALLS . len ( ) ) ;
222278 assert_eq ! (
223- syscall_traces
279+ fee_transfer_syscall_traces
224280 . iter( )
225- . filter ( |syscall_trace| syscall_trace. get_selector( ) == Selector :: EmitEvent )
226- . count ( ) ,
227- 2
281+ . map ( |syscall_trace| syscall_trace. get_selector( ) )
282+ . collect :: < Vec <_>> ( ) ,
283+ FEE_TRANSFER_SYSCALLS . to_vec ( )
228284 ) ;
229- assert_eq ! ( syscall_traces. pop( ) . unwrap( ) . get_selector( ) , Selector :: EmitEvent ) ;
230285
231286 // Measure each syscall overhead. If the syscall incurs an inner call, subtract the inner call
232287 // overhead.
233- let syscall_traces = test_output. runner_output . txs_trace . last ( ) . unwrap ( ) . get_syscalls ( ) ;
234- let mut syscalls_iter = syscall_traces
235- . iter ( )
236- . filter ( |syscall_trace| !UNMEASURABLE_SYSCALLS . contains ( & syscall_trace. get_selector ( ) ) ) ;
288+ let mut syscalls_iter = syscall_traces. iter ( ) ;
237289 let mut measurements: IndexMap < Selector , VariableResourceParams > = IndexMap :: new ( ) ;
238290 // If the syscall incurs an inner call, subtract the inner call overhead.
239291 let mut fetch_inner_resources = |selector : Selector | -> ExecutionResources {
@@ -306,6 +358,13 @@ async fn test_os_resources_regression() {
306358 . collect:: <HashSet <_>>( )
307359 ) ;
308360
361+ // Make sure there are no more dangling syscalls.
362+ let dangling_syscall = syscalls_iter. next ( ) ;
363+ assert ! (
364+ dangling_syscall. is_none( ) ,
365+ "There are more syscalls than expected. Dangling syscall: {dangling_syscall:?}."
366+ ) ;
367+
309368 // Compare the measurements with the expected values on the latest VC.
310369 let version = StarknetVersion :: LATEST ;
311370 let mut raw_vc: RawVersionedConstants =
0 commit comments