Skip to content

Commit af97b2c

Browse files
authored
perf(invariant): do not include reverts in counterexample (#8459)
1 parent 65b3cb0 commit af97b2c

File tree

3 files changed

+47
-0
lines changed

3 files changed

+47
-0
lines changed

crates/evm/evm/src/executors/invariant/result.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,10 @@ pub(crate) fn can_continue(
147147
invariant_data.failures.error = Some(InvariantFuzzError::Revert(case_data));
148148

149149
return Ok(RichInvariantResults::new(false, None));
150+
} else if call_result.reverted {
151+
// If we don't fail test on revert then remove last reverted call from inputs.
152+
// This improves shrinking performance as irrelevant calls won't be checked again.
153+
invariant_run.inputs.pop();
150154
}
151155
}
152156
Ok(RichInvariantResults::new(true, call_results))

crates/forge/tests/it/invariant.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -676,3 +676,21 @@ async fn test_invariant_selectors_weight() {
676676
)]),
677677
)
678678
}
679+
680+
#[tokio::test(flavor = "multi_thread")]
681+
async fn test_no_reverts_in_counterexample() {
682+
let filter =
683+
Filter::new(".*", ".*", ".*fuzz/invariant/common/InvariantSequenceNoReverts.t.sol");
684+
let mut runner = TEST_DATA_DEFAULT.runner();
685+
runner.test_options.invariant.fail_on_revert = false;
686+
// Use original counterexample to test sequence len.
687+
runner.test_options.invariant.shrink_run_limit = 0;
688+
689+
match get_counterexample!(runner, &filter) {
690+
CounterExample::Single(_) => panic!("CounterExample should be a sequence."),
691+
CounterExample::Sequence(sequence) => {
692+
// ensure original counterexample len is 10 (even without shrinking)
693+
assert_eq!(sequence.len(), 10);
694+
}
695+
};
696+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// SPDX-License-Identifier: UNLICENSED
2+
pragma solidity ^0.8.13;
3+
4+
import "ds-test/test.sol";
5+
6+
contract SequenceNoReverts {
7+
uint256 public count;
8+
9+
function work(uint256 x) public {
10+
require(x % 2 != 0);
11+
count++;
12+
}
13+
}
14+
15+
contract SequenceNoRevertsTest is DSTest {
16+
SequenceNoReverts target;
17+
18+
function setUp() public {
19+
target = new SequenceNoReverts();
20+
}
21+
22+
function invariant_no_reverts() public view {
23+
require(target.count() < 10, "condition met");
24+
}
25+
}

0 commit comments

Comments
 (0)