@@ -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 ;
@@ -86,12 +87,17 @@ pub(crate) struct TestParameters {
8687 pub ( crate ) messages_to_l2 : Vec < MessageToL2 > ,
8788}
8889
90+ pub ( crate ) struct FlowTestTx {
91+ tx : BlockifierTransaction ,
92+ revert_reason : Option < String > ,
93+ }
94+
8995/// Manages the execution of flow tests by maintaining the initial state and transactions.
9096pub ( crate ) struct TestManager < S : FlowTestState > {
9197 pub ( crate ) initial_state : InitialState < S > ,
9298 pub ( crate ) execution_contracts : OsExecutionContracts ,
9399
94- per_block_transactions : Vec < Vec < BlockifierTransaction > > ,
100+ per_block_transactions : Vec < Vec < FlowTestTx > > ,
95101}
96102
97103pub ( crate ) struct OsTestExpectedValues {
@@ -255,7 +261,7 @@ impl<S: FlowTestState> TestManager<S> {
255261 self . per_block_transactions . push ( vec ! [ ] ) ;
256262 }
257263
258- fn last_block_txs_mut ( & mut self ) -> & mut Vec < BlockifierTransaction > {
264+ fn last_block_txs_mut ( & mut self ) -> & mut Vec < FlowTestTx > {
259265 self . per_block_transactions
260266 . last_mut ( )
261267 . expect ( "Always initialized with at least one tx list (at least one block)." )
@@ -271,9 +277,12 @@ impl<S: FlowTestState> TestManager<S> {
271277 else {
272278 panic ! ( "Expected a V1 contract class" ) ;
273279 } ;
274- self . last_block_txs_mut ( ) . push ( BlockifierTransaction :: new_for_sequencing (
275- StarknetApiTransaction :: Account ( AccountTransaction :: Declare ( tx) ) ,
276- ) ) ;
280+ self . last_block_txs_mut ( ) . push ( FlowTestTx {
281+ tx : BlockifierTransaction :: new_for_sequencing ( StarknetApiTransaction :: Account (
282+ AccountTransaction :: Declare ( tx) ,
283+ ) ) ,
284+ revert_reason : None ,
285+ } ) ;
277286
278287 self . execution_contracts
279288 . declared_class_hash_to_component_hashes
@@ -285,14 +294,25 @@ impl<S: FlowTestState> TestManager<S> {
285294 . insert ( compiled_class_hash, casm. clone ( ) ) ;
286295 }
287296
288- pub ( crate ) fn add_invoke_tx ( & mut self , tx : InvokeTransaction ) {
289- self . last_block_txs_mut ( ) . push ( BlockifierTransaction :: new_for_sequencing (
290- StarknetApiTransaction :: Account ( AccountTransaction :: Invoke ( tx) ) ,
291- ) ) ;
297+ pub ( crate ) fn add_invoke_tx ( & mut self , tx : InvokeTransaction , revert_reason : Option < String > ) {
298+ self . last_block_txs_mut ( ) . push ( FlowTestTx {
299+ tx : BlockifierTransaction :: new_for_sequencing ( StarknetApiTransaction :: Account (
300+ AccountTransaction :: Invoke ( tx) ,
301+ ) ) ,
302+ revert_reason,
303+ } ) ;
292304 }
293305
294- pub ( crate ) fn add_invoke_tx_from_args ( & mut self , args : InvokeTxArgs , chain_id : & ChainId ) {
295- self . add_invoke_tx ( InvokeTransaction :: create ( invoke_tx ( args) , chain_id) . unwrap ( ) ) ;
306+ pub ( crate ) fn add_invoke_tx_from_args (
307+ & mut self ,
308+ args : InvokeTxArgs ,
309+ chain_id : & ChainId ,
310+ revert_reason : Option < String > ,
311+ ) {
312+ self . add_invoke_tx (
313+ InvokeTransaction :: create ( invoke_tx ( args) , chain_id) . unwrap ( ) ,
314+ revert_reason,
315+ ) ;
296316 }
297317
298318 pub ( crate ) fn add_cairo0_declare_tx (
@@ -303,24 +323,36 @@ impl<S: FlowTestState> TestManager<S> {
303323 let ContractClass :: V0 ( class) = tx. class_info . contract_class . clone ( ) else {
304324 panic ! ( "Expected a V0 contract class" ) ;
305325 } ;
306- self . last_block_txs_mut ( ) . push ( BlockifierTransaction :: new_for_sequencing (
307- StarknetApiTransaction :: Account ( AccountTransaction :: Declare ( tx) ) ,
308- ) ) ;
326+ self . last_block_txs_mut ( ) . push ( FlowTestTx {
327+ tx : BlockifierTransaction :: new_for_sequencing ( StarknetApiTransaction :: Account (
328+ AccountTransaction :: Declare ( tx) ,
329+ ) ) ,
330+ revert_reason : None ,
331+ } ) ;
309332 self . execution_contracts
310333 . executed_contracts
311334 . deprecated_contracts
312335 . insert ( compiled_class_hash, class) ;
313336 }
314337
315338 pub ( crate ) fn add_deploy_account_tx ( & mut self , tx : DeployAccountTransaction ) {
316- self . last_block_txs_mut ( ) . push ( BlockifierTransaction :: new_for_sequencing (
317- StarknetApiTransaction :: Account ( AccountTransaction :: DeployAccount ( tx) ) ,
318- ) ) ;
339+ self . last_block_txs_mut ( ) . push ( FlowTestTx {
340+ tx : BlockifierTransaction :: new_for_sequencing ( StarknetApiTransaction :: Account (
341+ AccountTransaction :: DeployAccount ( tx) ,
342+ ) ) ,
343+ revert_reason : None ,
344+ } ) ;
319345 }
320346
321- pub ( crate ) fn add_l1_handler_tx ( & mut self , tx : L1HandlerTransaction ) {
322- self . last_block_txs_mut ( )
323- . push ( BlockifierTransaction :: new_for_sequencing ( StarknetApiTransaction :: L1Handler ( tx) ) ) ;
347+ pub ( crate ) fn add_l1_handler_tx (
348+ & mut self ,
349+ tx : L1HandlerTransaction ,
350+ revert_reason : Option < String > ,
351+ ) {
352+ self . last_block_txs_mut ( ) . push ( FlowTestTx {
353+ tx : BlockifierTransaction :: new_for_sequencing ( StarknetApiTransaction :: L1Handler ( tx) ) ,
354+ revert_reason,
355+ } ) ;
324356 }
325357
326358 /// Executes the test using default block contexts, starting from the given block number.
@@ -395,6 +427,27 @@ impl<S: FlowTestState> TestManager<S> {
395427 first_use_kzg_da
396428 }
397429
430+ /// Verifies all the execution outputs are as expected w.r.t. revert reasons.
431+ fn verify_execution_outputs (
432+ revert_reasons : & [ Option < String > ] ,
433+ execution_outputs : & [ ( TransactionExecutionInfo , StateMaps ) ] ,
434+ ) {
435+ for ( revert_reason, ( execution_info, _) ) in
436+ revert_reasons. iter ( ) . zip ( execution_outputs. iter ( ) )
437+ {
438+ if let Some ( revert_reason) = revert_reason {
439+ let actual_revert_reason =
440+ execution_info. revert_error . as_ref ( ) . unwrap ( ) . to_string ( ) ;
441+ assert ! (
442+ actual_revert_reason. contains( revert_reason) ,
443+ "Expected '{revert_reason}' to be in revert string:\n '{actual_revert_reason}'"
444+ ) ;
445+ } else {
446+ assert ! ( execution_info. revert_error. is_none( ) ) ;
447+ }
448+ }
449+ }
450+
398451 /// Decompresses the state diff from the OS output using the given OS output, state and alias
399452 /// keys.
400453 fn get_decompressed_state_diff (
@@ -454,13 +507,19 @@ impl<S: FlowTestState> TestManager<S> {
454507 "use_kzg_da flag in block contexts must match the test parameter."
455508 ) ;
456509 let mut alias_keys = HashSet :: new ( ) ;
457- for ( block_txs, block_context) in per_block_txs. into_iter ( ) . zip ( block_contexts. into_iter ( ) )
510+ for ( block_txs_with_reason, block_context) in
511+ per_block_txs. into_iter ( ) . zip ( block_contexts. into_iter ( ) )
458512 {
459513 // Clone the block info for later use.
514+ let ( block_txs, revert_reasons) : ( Vec < _ > , Vec < _ > ) = block_txs_with_reason
515+ . into_iter ( )
516+ . map ( |flow_test_tx| ( flow_test_tx. tx , flow_test_tx. revert_reason ) )
517+ . unzip ( ) ;
460518 let block_info = block_context. block_info ( ) . clone ( ) ;
461519 // Execute the transactions.
462520 let ExecutionOutput { execution_outputs, block_summary, mut final_state } =
463521 execute_transactions ( state, & block_txs, block_context) ;
522+ Self :: verify_execution_outputs ( & revert_reasons, & execution_outputs) ;
464523 let extended_state_diff = final_state. cache . borrow ( ) . extended_state_diff ( ) ;
465524 // Update the wrapped state.
466525 let state_diff = final_state. to_state_diff ( ) . unwrap ( ) ;
0 commit comments