Skip to content

feat(invariant): decouple handler-side assertions from invariant predicates#14482

Draft
grandizzy wants to merge 2 commits intogdzzy/issue-9727from
gdzzy/issue-14448
Draft

feat(invariant): decouple handler-side assertions from invariant predicates#14482
grandizzy wants to merge 2 commits intogdzzy/issue-9727from
gdzzy/issue-14448

Conversation

@grandizzy
Copy link
Copy Markdown
Collaborator

Summary

Decouple handler-side assertion failures from invariant predicate violations so the campaign can keep running after either class of bug is discovered. Closes #14448.

Motivation

Today, an assert(false) (or vm.assertEq failure) inside a fuzzed handler is attributed to every live invariant and aborts the campaign on the first hit. On real targets like Aave v4 SCFuzzBench, this means a single canary in the handler kills 95+% of the time budget — Foundry finds 3 bugs where Echidna/Medusa find 10+ in the same 24 h window.

What changes

  • Separate failure class. Handler-side assertion bugs are recorded in a new broken_handlers: HashMap<(Address, Selector), HandlerAssertionFailure> map, deduplicated by (reverter, selector) so each unique handler bug is reported once.
  • Dedicated report section. Bugs render under Suite handlers: N assertion bug(s) found with full reason + counterexample, separate from the existing per-invariant [FAIL: ...] blocks.
  • Continuous campaign under assert_all. With the new assert_all = true default, a preflight invariant failure (e.g., a global canary) is recorded and the campaign continues for the full budget. Legacy abort-on-preflight is preserved when assert_all = false.
  • Live telemetry. Progress bar and JSON pulse events surface unique broken_handlers counts alongside invariant failure counts so users see both classes accumulate.

Result on SCFuzzBench (Aave v4)

Single 1 h run, seed 42:

Fuzzer Bugs (24 h) Bugs (1 h)
Echidna 2.3.1 11
Medusa 1.4.1 10
Recon Fuzzer 11
Foundry HEAD 9

Stack

This PR stacks on top of #12587 (rename continuous_runassert_all, default true). Review/merge that first.

TODOs

  • Shrink, persist, and auto-replay handler bugs (currently in-memory only).
  • Expand test coverage for handler-bug interaction modes (multi-handler, post-shrink replay, settings invalidation).

…icates

Handler-side assertion failures are now tracked in a dedicated broken_handlers
map keyed by (reverter, selector) instead of being attributed to every live
invariant. They surface in their own "Suite handlers:" report section,
keeping invariant predicate breaks rendered separately.

Under assert_all = true (the new default), the campaign continues for the
full budget after a preflight invariant failure so handler-side bugs and
still-live invariants can be discovered. The legacy abort-on-preflight
behavior is preserved when assert_all = false.

Live progress (progress bar + JSON pulse events) now surfaces unique
handler bug counts alongside invariant failure counts so both classes are
visible during the campaign.

Tests:

- New regression test assert_all_handler_assertion_routed_to_handler_section
  asserting the "handler bug != invariant break" semantics.
- Existing handler-assert tests (invariant_fail_on_assert_panic,
  invariant_fail_on_vm_assert_*, etc.) updated to expect the new
  "Suite handlers:" rendering while keeping the failure-reason line.
- should_exit_early_on_invariant_failure now sets assert_all = false
  explicitly so it continues to exercise the legacy abort-on-preflight path.

Refs: #14437
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

1 participant