@@ -9,6 +9,7 @@ use blockifier::context::{BlockContext, ChainInfo, FeeTokenAddresses};
99use blockifier:: state:: cached_state:: { CommitmentStateDiff , StateMaps } ;
1010use blockifier:: state:: stateful_compression_test_utils:: decompress;
1111use blockifier:: test_utils:: ALIAS_CONTRACT_ADDRESS ;
12+ use blockifier:: transaction:: objects:: TransactionExecutionInfo ;
1213use blockifier:: transaction:: transaction_execution:: Transaction as BlockifierTransaction ;
1314use blockifier_test_utils:: contracts:: FeatureContract ;
1415use itertools:: Itertools ;
@@ -88,13 +89,18 @@ pub(crate) struct TestParameters {
8889 pub ( crate ) messages_to_l2 : Vec < MessageToL2 > ,
8990}
9091
92+ pub ( crate ) struct FlowTestTx {
93+ tx : BlockifierTransaction ,
94+ expected_revert_reason : Option < String > ,
95+ }
96+
9197/// Manages the execution of flow tests by maintaining the initial state and transactions.
9298pub ( crate ) struct TestManager < S : FlowTestState > {
9399 pub ( crate ) initial_state : InitialState < S > ,
94100 pub ( crate ) nonce_manager : NonceManager ,
95101 pub ( crate ) execution_contracts : OsExecutionContracts ,
96102
97- per_block_transactions : Vec < Vec < BlockifierTransaction > > ,
103+ per_block_transactions : Vec < Vec < FlowTestTx > > ,
98104}
99105
100106pub ( crate ) struct OsTestExpectedValues {
@@ -270,7 +276,7 @@ impl<S: FlowTestState> TestManager<S> {
270276 self . per_block_transactions . push ( vec ! [ ] ) ;
271277 }
272278
273- fn last_block_txs_mut ( & mut self ) -> & mut Vec < BlockifierTransaction > {
279+ fn last_block_txs_mut ( & mut self ) -> & mut Vec < FlowTestTx > {
274280 self . per_block_transactions
275281 . last_mut ( )
276282 . expect ( "Always initialized with at least one tx list (at least one block)." )
@@ -286,9 +292,12 @@ impl<S: FlowTestState> TestManager<S> {
286292 else {
287293 panic ! ( "Expected a V1 contract class" ) ;
288294 } ;
289- self . last_block_txs_mut ( ) . push ( BlockifierTransaction :: new_for_sequencing (
290- StarknetApiTransaction :: Account ( AccountTransaction :: Declare ( tx) ) ,
291- ) ) ;
295+ self . last_block_txs_mut ( ) . push ( FlowTestTx {
296+ tx : BlockifierTransaction :: new_for_sequencing ( StarknetApiTransaction :: Account (
297+ AccountTransaction :: Declare ( tx) ,
298+ ) ) ,
299+ expected_revert_reason : None ,
300+ } ) ;
292301
293302 self . execution_contracts
294303 . declared_class_hash_to_component_hashes
@@ -300,14 +309,29 @@ impl<S: FlowTestState> TestManager<S> {
300309 . insert ( compiled_class_hash, casm. clone ( ) ) ;
301310 }
302311
303- pub ( crate ) fn add_invoke_tx ( & mut self , tx : InvokeTransaction ) {
304- self . last_block_txs_mut ( ) . push ( BlockifierTransaction :: new_for_sequencing (
305- StarknetApiTransaction :: Account ( AccountTransaction :: Invoke ( tx) ) ,
306- ) ) ;
312+ pub ( crate ) fn add_invoke_tx (
313+ & mut self ,
314+ tx : InvokeTransaction ,
315+ expected_revert_reason : Option < String > ,
316+ ) {
317+ self . last_block_txs_mut ( ) . push ( FlowTestTx {
318+ tx : BlockifierTransaction :: new_for_sequencing ( StarknetApiTransaction :: Account (
319+ AccountTransaction :: Invoke ( tx) ,
320+ ) ) ,
321+ expected_revert_reason,
322+ } ) ;
307323 }
308324
309- pub ( crate ) fn add_invoke_tx_from_args ( & mut self , args : InvokeTxArgs , chain_id : & ChainId ) {
310- self . add_invoke_tx ( InvokeTransaction :: create ( invoke_tx ( args) , chain_id) . unwrap ( ) ) ;
325+ pub ( crate ) fn add_invoke_tx_from_args (
326+ & mut self ,
327+ args : InvokeTxArgs ,
328+ chain_id : & ChainId ,
329+ revert_reason : Option < String > ,
330+ ) {
331+ self . add_invoke_tx (
332+ InvokeTransaction :: create ( invoke_tx ( args) , chain_id) . unwrap ( ) ,
333+ revert_reason,
334+ ) ;
311335 }
312336
313337 /// Similar to `add_invoke_tx_from_args`, but with the sender address set to the funded account,
@@ -323,6 +347,7 @@ impl<S: FlowTestState> TestManager<S> {
323347 ..additional_args
324348 } ,
325349 & CHAIN_ID_FOR_TESTS ,
350+ None ,
326351 ) ;
327352 }
328353
@@ -334,24 +359,36 @@ impl<S: FlowTestState> TestManager<S> {
334359 let ContractClass :: V0 ( class) = tx. class_info . contract_class . clone ( ) else {
335360 panic ! ( "Expected a V0 contract class" ) ;
336361 } ;
337- self . last_block_txs_mut ( ) . push ( BlockifierTransaction :: new_for_sequencing (
338- StarknetApiTransaction :: Account ( AccountTransaction :: Declare ( tx) ) ,
339- ) ) ;
362+ self . last_block_txs_mut ( ) . push ( FlowTestTx {
363+ tx : BlockifierTransaction :: new_for_sequencing ( StarknetApiTransaction :: Account (
364+ AccountTransaction :: Declare ( tx) ,
365+ ) ) ,
366+ expected_revert_reason : None ,
367+ } ) ;
340368 self . execution_contracts
341369 . executed_contracts
342370 . deprecated_contracts
343371 . insert ( compiled_class_hash, class) ;
344372 }
345373
346374 pub ( crate ) fn add_deploy_account_tx ( & mut self , tx : DeployAccountTransaction ) {
347- self . last_block_txs_mut ( ) . push ( BlockifierTransaction :: new_for_sequencing (
348- StarknetApiTransaction :: Account ( AccountTransaction :: DeployAccount ( tx) ) ,
349- ) ) ;
375+ self . last_block_txs_mut ( ) . push ( FlowTestTx {
376+ tx : BlockifierTransaction :: new_for_sequencing ( StarknetApiTransaction :: Account (
377+ AccountTransaction :: DeployAccount ( tx) ,
378+ ) ) ,
379+ expected_revert_reason : None ,
380+ } ) ;
350381 }
351382
352- pub ( crate ) fn add_l1_handler_tx ( & mut self , tx : L1HandlerTransaction ) {
353- self . last_block_txs_mut ( )
354- . push ( BlockifierTransaction :: new_for_sequencing ( StarknetApiTransaction :: L1Handler ( tx) ) ) ;
383+ pub ( crate ) fn add_l1_handler_tx (
384+ & mut self ,
385+ tx : L1HandlerTransaction ,
386+ expected_revert_reason : Option < String > ,
387+ ) {
388+ self . last_block_txs_mut ( ) . push ( FlowTestTx {
389+ tx : BlockifierTransaction :: new_for_sequencing ( StarknetApiTransaction :: L1Handler ( tx) ) ,
390+ expected_revert_reason,
391+ } ) ;
355392 }
356393
357394 /// Executes the test using default block contexts, starting from the given block number.
@@ -426,6 +463,28 @@ impl<S: FlowTestState> TestManager<S> {
426463 first_use_kzg_da
427464 }
428465
466+ /// Verifies all the execution outputs are as expected w.r.t. revert reasons.
467+ fn verify_execution_outputs (
468+ revert_reasons : & [ Option < String > ] ,
469+ execution_outputs : & [ ( TransactionExecutionInfo , StateMaps ) ] ,
470+ ) {
471+ assert_eq ! ( revert_reasons. len( ) , execution_outputs. len( ) ) ;
472+ for ( revert_reason, ( execution_info, _) ) in
473+ revert_reasons. iter ( ) . zip ( execution_outputs. iter ( ) )
474+ {
475+ if let Some ( revert_reason) = revert_reason {
476+ let actual_revert_reason =
477+ execution_info. revert_error . as_ref ( ) . unwrap ( ) . to_string ( ) ;
478+ assert ! (
479+ actual_revert_reason. contains( revert_reason) ,
480+ "Expected '{revert_reason}' to be in revert string:\n '{actual_revert_reason}'"
481+ ) ;
482+ } else {
483+ assert ! ( execution_info. revert_error. is_none( ) ) ;
484+ }
485+ }
486+ }
487+
429488 /// Decompresses the state diff from the OS output using the given OS output, state and alias
430489 /// keys.
431490 fn get_decompressed_state_diff (
@@ -488,13 +547,19 @@ impl<S: FlowTestState> TestManager<S> {
488547 "use_kzg_da flag in block contexts must match the test parameter."
489548 ) ;
490549 let mut alias_keys = HashSet :: new ( ) ;
491- for ( block_txs, block_context) in per_block_txs. into_iter ( ) . zip ( block_contexts. into_iter ( ) )
550+ for ( block_txs_with_reason, block_context) in
551+ per_block_txs. into_iter ( ) . zip ( block_contexts. into_iter ( ) )
492552 {
553+ let ( block_txs, revert_reasons) : ( Vec < _ > , Vec < _ > ) = block_txs_with_reason
554+ . into_iter ( )
555+ . map ( |flow_test_tx| ( flow_test_tx. tx , flow_test_tx. expected_revert_reason ) )
556+ . unzip ( ) ;
493557 // Clone the block info for later use.
494558 let block_info = block_context. block_info ( ) . clone ( ) ;
495559 // Execute the transactions.
496560 let ExecutionOutput { execution_outputs, block_summary, mut final_state } =
497561 execute_transactions ( state, & block_txs, block_context) ;
562+ Self :: verify_execution_outputs ( & revert_reasons, & execution_outputs) ;
498563 let extended_state_diff = final_state. cache . borrow ( ) . extended_state_diff ( ) ;
499564 // Update the wrapped state.
500565 let state_diff = final_state. to_state_diff ( ) . unwrap ( ) ;
0 commit comments