@@ -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 ;
@@ -90,13 +91,18 @@ pub(crate) struct TestParameters {
9091 pub ( crate ) private_keys : Option < Vec < Felt > > ,
9192}
9293
94+ pub ( crate ) struct FlowTestTx {
95+ tx : BlockifierTransaction ,
96+ expected_revert_reason : Option < String > ,
97+ }
98+
9399/// Manages the execution of flow tests by maintaining the initial state and transactions.
94100pub ( crate ) struct TestManager < S : FlowTestState > {
95101 pub ( crate ) initial_state : InitialState < S > ,
96102 pub ( crate ) nonce_manager : NonceManager ,
97103 pub ( crate ) execution_contracts : OsExecutionContracts ,
98104
99- per_block_transactions : Vec < Vec < BlockifierTransaction > > ,
105+ per_block_transactions : Vec < Vec < FlowTestTx > > ,
100106}
101107
102108pub ( crate ) struct OsTestExpectedValues {
@@ -273,7 +279,7 @@ impl<S: FlowTestState> TestManager<S> {
273279 self . per_block_transactions . push ( vec ! [ ] ) ;
274280 }
275281
276- fn last_block_txs_mut ( & mut self ) -> & mut Vec < BlockifierTransaction > {
282+ fn last_block_txs_mut ( & mut self ) -> & mut Vec < FlowTestTx > {
277283 self . per_block_transactions
278284 . last_mut ( )
279285 . expect ( "Always initialized with at least one tx list (at least one block)." )
@@ -289,9 +295,12 @@ impl<S: FlowTestState> TestManager<S> {
289295 else {
290296 panic ! ( "Expected a V1 contract class" ) ;
291297 } ;
292- self . last_block_txs_mut ( ) . push ( BlockifierTransaction :: new_for_sequencing (
293- StarknetApiTransaction :: Account ( AccountTransaction :: Declare ( tx) ) ,
294- ) ) ;
298+ self . last_block_txs_mut ( ) . push ( FlowTestTx {
299+ tx : BlockifierTransaction :: new_for_sequencing ( StarknetApiTransaction :: Account (
300+ AccountTransaction :: Declare ( tx) ,
301+ ) ) ,
302+ expected_revert_reason : None ,
303+ } ) ;
295304
296305 self . execution_contracts
297306 . declared_class_hash_to_component_hashes
@@ -303,14 +312,29 @@ impl<S: FlowTestState> TestManager<S> {
303312 . insert ( compiled_class_hash, casm. clone ( ) ) ;
304313 }
305314
306- pub ( crate ) fn add_invoke_tx ( & mut self , tx : InvokeTransaction ) {
307- self . last_block_txs_mut ( ) . push ( BlockifierTransaction :: new_for_sequencing (
308- StarknetApiTransaction :: Account ( AccountTransaction :: Invoke ( tx) ) ,
309- ) ) ;
315+ pub ( crate ) fn add_invoke_tx (
316+ & mut self ,
317+ tx : InvokeTransaction ,
318+ expected_revert_reason : Option < String > ,
319+ ) {
320+ self . last_block_txs_mut ( ) . push ( FlowTestTx {
321+ tx : BlockifierTransaction :: new_for_sequencing ( StarknetApiTransaction :: Account (
322+ AccountTransaction :: Invoke ( tx) ,
323+ ) ) ,
324+ expected_revert_reason,
325+ } ) ;
310326 }
311327
312- pub ( crate ) fn add_invoke_tx_from_args ( & mut self , args : InvokeTxArgs , chain_id : & ChainId ) {
313- self . add_invoke_tx ( InvokeTransaction :: create ( invoke_tx ( args) , chain_id) . unwrap ( ) ) ;
328+ pub ( crate ) fn add_invoke_tx_from_args (
329+ & mut self ,
330+ args : InvokeTxArgs ,
331+ chain_id : & ChainId ,
332+ revert_reason : Option < String > ,
333+ ) {
334+ self . add_invoke_tx (
335+ InvokeTransaction :: create ( invoke_tx ( args) , chain_id) . unwrap ( ) ,
336+ revert_reason,
337+ ) ;
314338 }
315339
316340 /// Similar to `add_invoke_tx_from_args`, but with the sender address set to the funded account,
@@ -326,28 +350,41 @@ impl<S: FlowTestState> TestManager<S> {
326350 ..additional_args
327351 } ,
328352 & CHAIN_ID_FOR_TESTS ,
353+ None ,
329354 ) ;
330355 }
331356
332357 pub ( crate ) fn add_cairo0_declare_tx ( & mut self , tx : DeclareTransaction , class_hash : ClassHash ) {
333358 let ContractClass :: V0 ( class) = tx. class_info . contract_class . clone ( ) else {
334359 panic ! ( "Expected a V0 contract class" ) ;
335360 } ;
336- self . last_block_txs_mut ( ) . push ( BlockifierTransaction :: new_for_sequencing (
337- StarknetApiTransaction :: Account ( AccountTransaction :: Declare ( tx) ) ,
338- ) ) ;
361+ self . last_block_txs_mut ( ) . push ( FlowTestTx {
362+ tx : BlockifierTransaction :: new_for_sequencing ( StarknetApiTransaction :: Account (
363+ AccountTransaction :: Declare ( tx) ,
364+ ) ) ,
365+ expected_revert_reason : None ,
366+ } ) ;
339367 self . execution_contracts . executed_contracts . deprecated_contracts . insert ( class_hash, class) ;
340368 }
341369
342370 pub ( crate ) fn add_deploy_account_tx ( & mut self , tx : DeployAccountTransaction ) {
343- self . last_block_txs_mut ( ) . push ( BlockifierTransaction :: new_for_sequencing (
344- StarknetApiTransaction :: Account ( AccountTransaction :: DeployAccount ( tx) ) ,
345- ) ) ;
371+ self . last_block_txs_mut ( ) . push ( FlowTestTx {
372+ tx : BlockifierTransaction :: new_for_sequencing ( StarknetApiTransaction :: Account (
373+ AccountTransaction :: DeployAccount ( tx) ,
374+ ) ) ,
375+ expected_revert_reason : None ,
376+ } ) ;
346377 }
347378
348- pub ( crate ) fn add_l1_handler_tx ( & mut self , tx : L1HandlerTransaction ) {
349- self . last_block_txs_mut ( )
350- . push ( BlockifierTransaction :: new_for_sequencing ( StarknetApiTransaction :: L1Handler ( tx) ) ) ;
379+ pub ( crate ) fn add_l1_handler_tx (
380+ & mut self ,
381+ tx : L1HandlerTransaction ,
382+ expected_revert_reason : Option < String > ,
383+ ) {
384+ self . last_block_txs_mut ( ) . push ( FlowTestTx {
385+ tx : BlockifierTransaction :: new_for_sequencing ( StarknetApiTransaction :: L1Handler ( tx) ) ,
386+ expected_revert_reason,
387+ } ) ;
351388 }
352389
353390 /// Executes the test using default block contexts, starting from the given block number.
@@ -422,6 +459,28 @@ impl<S: FlowTestState> TestManager<S> {
422459 first_use_kzg_da
423460 }
424461
462+ /// Verifies all the execution outputs are as expected w.r.t. revert reasons.
463+ fn verify_execution_outputs (
464+ revert_reasons : & [ Option < String > ] ,
465+ execution_outputs : & [ ( TransactionExecutionInfo , StateMaps ) ] ,
466+ ) {
467+ assert_eq ! ( revert_reasons. len( ) , execution_outputs. len( ) ) ;
468+ for ( revert_reason, ( execution_info, _) ) in
469+ revert_reasons. iter ( ) . zip ( execution_outputs. iter ( ) )
470+ {
471+ if let Some ( revert_reason) = revert_reason {
472+ let actual_revert_reason =
473+ execution_info. revert_error . as_ref ( ) . unwrap ( ) . to_string ( ) ;
474+ assert ! (
475+ actual_revert_reason. contains( revert_reason) ,
476+ "Expected '{revert_reason}' to be in revert string:\n '{actual_revert_reason}'"
477+ ) ;
478+ } else {
479+ assert ! ( execution_info. revert_error. is_none( ) ) ;
480+ }
481+ }
482+ }
483+
425484 /// Decompresses the state diff from the OS output using the given OS output, state and alias
426485 /// keys.
427486 fn get_decompressed_state_diff (
@@ -485,13 +544,19 @@ impl<S: FlowTestState> TestManager<S> {
485544 "use_kzg_da flag in block contexts must match the test parameter."
486545 ) ;
487546 let mut alias_keys = HashSet :: new ( ) ;
488- for ( block_txs, block_context) in per_block_txs. into_iter ( ) . zip ( block_contexts. into_iter ( ) )
547+ for ( block_txs_with_reason, block_context) in
548+ per_block_txs. into_iter ( ) . zip ( block_contexts. into_iter ( ) )
489549 {
550+ let ( block_txs, revert_reasons) : ( Vec < _ > , Vec < _ > ) = block_txs_with_reason
551+ . into_iter ( )
552+ . map ( |flow_test_tx| ( flow_test_tx. tx , flow_test_tx. expected_revert_reason ) )
553+ . unzip ( ) ;
490554 // Clone the block info for later use.
491555 let block_info = block_context. block_info ( ) . clone ( ) ;
492556 // Execute the transactions.
493557 let ExecutionOutput { execution_outputs, block_summary, mut final_state } =
494558 execute_transactions ( state, & block_txs, block_context) ;
559+ Self :: verify_execution_outputs ( & revert_reasons, & execution_outputs) ;
495560 let extended_state_diff = final_state. cache . borrow ( ) . extended_state_diff ( ) ;
496561 // Update the wrapped state.
497562 let state_diff = final_state. to_state_diff ( ) . unwrap ( ) ;
0 commit comments