@@ -2,6 +2,7 @@ use crate::{
22 executors:: { Executor , RawCallResult } ,
33 inspectors:: Fuzzer ,
44} ;
5+ use alloy_json_abi:: Function ;
56use alloy_primitives:: {
67 Address , Bytes , FixedBytes , Selector , U256 ,
78 map:: { AddressMap , HashMap } ,
@@ -28,7 +29,7 @@ use foundry_evm_traces::{CallTraceArena, SparsedTraceArena};
2829use indicatif:: ProgressBar ;
2930use parking_lot:: RwLock ;
3031use proptest:: { strategy:: Strategy , test_runner:: TestRunner } ;
31- use result:: { assert_after_invariant, assert_invariants , can_continue} ;
32+ use result:: { assert_after_invariant, can_continue} ;
3233use revm:: state:: Account ;
3334use std:: {
3435 collections:: { HashMap as Map , btree_map:: Entry } ,
@@ -41,7 +42,7 @@ pub use error::{InvariantFailures, InvariantFuzzError};
4142use foundry_evm_coverage:: HitMaps ;
4243
4344mod replay;
44- pub use replay:: { replay_error, replay_run} ;
45+ pub use replay:: { generate_counterexample , replay_error, replay_run} ;
4546
4647mod result;
4748use foundry_common:: { TestFunctionExt , sh_println} ;
@@ -52,6 +53,7 @@ use serde_json::json;
5253mod shrink;
5354use crate :: executors:: {
5455 DURATION_BETWEEN_METRICS_REPORT , EarlyExit , EvmError , FuzzTestTimer , corpus:: CorpusManager ,
56+ invariant:: result:: invariant_preflight_check,
5557} ;
5658pub use shrink:: check_sequence;
5759
@@ -130,8 +132,6 @@ struct InvariantTestData {
130132 last_run_inputs : Vec < BasicTxDetails > ,
131133 // Additional traces for gas report.
132134 gas_report_traces : Vec < Vec < CallTraceArena > > ,
133- // Last call results of the invariant test.
134- last_call_results : Option < RawCallResult > ,
135135 // Line coverage information collected from all fuzzed calls.
136136 line_coverage : Option < HitMaps > ,
137137 // Metrics for each fuzzed selector.
@@ -160,19 +160,13 @@ impl InvariantTest {
160160 fuzz_state : EvmFuzzState ,
161161 targeted_contracts : FuzzRunIdentifiedContracts ,
162162 failures : InvariantFailures ,
163- last_call_results : Option < RawCallResult > ,
164163 branch_runner : TestRunner ,
165164 ) -> Self {
166- let mut fuzz_cases = vec ! [ ] ;
167- if last_call_results. is_none ( ) {
168- fuzz_cases. push ( FuzzedCases :: new ( vec ! [ ] ) ) ;
169- }
170165 let test_data = InvariantTestData {
171- fuzz_cases,
166+ fuzz_cases : vec ! [ ] ,
172167 failures,
173168 last_run_inputs : vec ! [ ] ,
174169 gas_report_traces : vec ! [ ] ,
175- last_call_results,
176170 line_coverage : None ,
177171 metrics : Map :: default ( ) ,
178172 branch_runner,
@@ -186,18 +180,13 @@ impl InvariantTest {
186180 }
187181
188182 /// Whether invariant test has errors or not.
189- fn has_errors ( & self ) -> bool {
190- self . test_data . failures . error . is_some ( )
183+ fn has_errors ( & self , invariant : & Function ) -> bool {
184+ self . test_data . failures . has_failure ( invariant )
191185 }
192186
193187 /// Set invariant test error.
194- fn set_error ( & mut self , error : InvariantFuzzError ) {
195- self . test_data . failures . error = Some ( error) ;
196- }
197-
198- /// Set last invariant test call results.
199- fn set_last_call_results ( & mut self , call_result : Option < RawCallResult > ) {
200- self . test_data . last_call_results = call_result;
188+ fn set_error ( & mut self , invariant : & Function , error : InvariantFuzzError ) {
189+ self . test_data . failures . record_failure ( invariant, error) ;
201190 }
202191
203192 /// Set last invariant run call sequence.
@@ -336,7 +325,7 @@ impl<'a> InvariantExecutor<'a> {
336325 early_exit : & EarlyExit ,
337326 ) -> Result < InvariantFuzzTestResult > {
338327 // Throw an error to abort test run if the invariant function accepts input params
339- if !invariant_contract. invariant_function . inputs . is_empty ( ) {
328+ if !invariant_contract. invariant_fn . inputs . is_empty ( ) {
340329 return Err ( eyre ! ( "Invariant test function should have no inputs" ) ) ;
341330 }
342331
@@ -421,9 +410,10 @@ impl<'a> InvariantExecutor<'a> {
421410 current_run. inputs . pop ( ) ;
422411 current_run. rejects += 1 ;
423412 if current_run. rejects > self . config . max_assume_rejects {
424- invariant_test. set_error ( InvariantFuzzError :: MaxAssumeRejects (
425- self . config . max_assume_rejects ,
426- ) ) ;
413+ invariant_test. set_error (
414+ invariant_contract. invariant_fn ,
415+ InvariantFuzzError :: MaxAssumeRejects ( self . config . max_assume_rejects ) ,
416+ ) ;
427417 break ' stop;
428418 }
429419 } else {
@@ -468,7 +458,7 @@ impl<'a> InvariantExecutor<'a> {
468458 } ) ;
469459
470460 // Determine if test can continue or should exit.
471- let result = can_continue (
461+ let can_continue = can_continue (
472462 & invariant_contract,
473463 & mut invariant_test,
474464 & mut current_run,
@@ -477,15 +467,13 @@ impl<'a> InvariantExecutor<'a> {
477467 & state_changeset,
478468 )
479469 . map_err ( |e| eyre ! ( e. to_string( ) ) ) ?;
480- if !result . can_continue || current_run. depth == self . config . depth - 1 {
470+ if !can_continue || current_run. depth == self . config . depth - 1 {
481471 invariant_test. set_last_run_inputs ( & current_run. inputs ) ;
482472 }
483473 // If test cannot continue then stop current run and exit test suite.
484- if !result . can_continue {
474+ if !can_continue {
485475 break ' stop;
486476 }
487-
488- invariant_test. set_last_call_results ( result. call_result ) ;
489477 current_run. depth += 1 ;
490478 }
491479
@@ -501,7 +489,9 @@ impl<'a> InvariantExecutor<'a> {
501489 corpus_manager. process_inputs ( & current_run. inputs , current_run. new_coverage ) ;
502490
503491 // Call `afterInvariant` only if it is declared and test didn't fail already.
504- if invariant_contract. call_after_invariant && !invariant_test. has_errors ( ) {
492+ if invariant_contract. call_after_invariant
493+ && !invariant_test. has_errors ( invariant_contract. invariant_fn )
494+ {
505495 assert_after_invariant (
506496 & invariant_contract,
507497 & mut invariant_test,
@@ -516,10 +506,18 @@ impl<'a> InvariantExecutor<'a> {
516506 if let Some ( progress) = progress {
517507 // If running with progress then increment completed runs.
518508 progress. inc ( 1 ) ;
519- // Display metrics in progress bar.
509+
510+ let failures = & invariant_test. test_data . failures ;
511+ let mut parts = Vec :: new ( ) ;
512+ // Add failures if present
513+ if !failures. errors . is_empty ( ) {
514+ parts. push ( format ! ( "{failures}" ) ) ;
515+ }
516+ // Add edge coverage metrics if enabled
520517 if edge_coverage_enabled {
521- progress . set_message ( format ! ( "{}" , & corpus_manager. metrics) ) ;
518+ parts . push ( format ! ( "{}" , corpus_manager. metrics) ) ;
522519 }
520+ progress. set_message ( parts. join ( "" ) ) ;
523521 } else if edge_coverage_enabled
524522 && last_metrics_report. elapsed ( ) > DURATION_BETWEEN_METRICS_REPORT
525523 {
@@ -528,7 +526,7 @@ impl<'a> InvariantExecutor<'a> {
528526 "timestamp" : SystemTime :: now( )
529527 . duration_since( UNIX_EPOCH ) ?
530528 . as_secs( ) ,
531- "invariant" : invariant_contract. invariant_function . name,
529+ "invariant" : invariant_contract. invariant_fn . name,
532530 "metrics" : & corpus_manager. metrics,
533531 } ) ;
534532 let _ = sh_println ! ( "{}" , serde_json:: to_string( & metrics) ?) ;
@@ -543,7 +541,7 @@ impl<'a> InvariantExecutor<'a> {
543541
544542 let result = invariant_test. test_data ;
545543 Ok ( InvariantFuzzTestResult {
546- error : result. failures . error ,
544+ errors : result. failures . errors ,
547545 cases : result. fuzz_cases ,
548546 reverts : result. failures . reverts ,
549547 last_run_inputs : result. last_run_inputs ,
@@ -627,15 +625,15 @@ impl<'a> InvariantExecutor<'a> {
627625 // already know if we can early exit the invariant run.
628626 // This does not count as a fuzz run. It will just register the revert.
629627 let mut failures = InvariantFailures :: new ( ) ;
630- let last_call_results = assert_invariants (
628+ invariant_preflight_check (
631629 invariant_contract,
632630 & self . config ,
633631 & targeted_contracts,
634632 & self . executor ,
635633 & [ ] ,
636634 & mut failures,
637635 ) ?;
638- if let Some ( error) = failures. error {
636+ if let Some ( error) = failures. get_failure ( invariant_contract . invariant_fn ) {
639637 return Err ( eyre ! ( error. revert_reason( ) . unwrap_or_default( ) ) ) ;
640638 }
641639
@@ -646,13 +644,8 @@ impl<'a> InvariantExecutor<'a> {
646644 None ,
647645 Some ( & targeted_contracts) ,
648646 ) ?;
649- let invariant_test = InvariantTest :: new (
650- fuzz_state,
651- targeted_contracts,
652- failures,
653- last_call_results,
654- self . runner . clone ( ) ,
655- ) ;
647+ let invariant_test =
648+ InvariantTest :: new ( fuzz_state, targeted_contracts, failures, self . runner . clone ( ) ) ;
656649
657650 Ok ( ( invariant_test, corpus_manager) )
658651 }
0 commit comments