feat: add comprehensive invariant tests for Zenith contracts#94
feat: add comprehensive invariant tests for Zenith contracts#94init4samwise wants to merge 7 commits intomainfrom
Conversation
Adds 35 invariant tests covering fund safety, liveness, and sequencing integrity across the contract suite: - ZenithInvariant.t.sol: sequencing contract invariants - PassageInvariant.t.sol: host-side passage (ETH/token entry) - RollupPassageInvariant.t.sol: rollup-side passage (exits/burns) - OrdersInvariant.t.sol: order handling (RollupOrders, HostOrders) - TransactorInvariant.t.sol: L1→L2 transaction handling All tests pass with 50 runs × 20 call depth per invariant. Closes ENG-1533
|
[Claude Code] CI Performance InvestigationThe
TransactorInvariant alone consumed ~2h 57m of the total run. All other suites finished within ~2 minutes each. Run Configuration MismatchThe PR description states "50 runs × 20 call depth" but CI actually ran with 256 runs × 500 call depth (128,000 calls per invariant). With 9 tests in TransactorInvariant, that's 1,152,000 total fuzz calls for this suite. The handler is likely much more expensive per call than the others. Consider setting a lighter CI profile (matching the intended 50 × 20) and reserving deep fuzzing for nightly/manual runs. Failing InvariantsTwo invariants failed: 1.
The fuzzer found a sequence where total withdrawals exceeded total entries. The counterexample shows a 2. This invariant asserts HostOrders holds no funds (pass-through design). The fuzzer found that after a Both failures look like test handler bugs (unbounded inputs, incorrect pass-through modeling) rather than contract bugs — but worth verifying. |
- PassageInvariant: withdraw test ETH in invariant_canReceiveEth so untracked balance doesn't break invariant_withdrawalBounded - OrdersInvariant: guard fill handlers against recipient being the orders contracts, preventing tokens/ETH from getting stuck - TransactorInvariant: replace O(n²) nested-loop invariant with O(1) incremental validation, reducing runtime from ~3 hours to seconds - foundry.toml: add explicit [invariant] config (runs=256, depth=500) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
[Claude Code] Pushed fixes in 89ef71b:
All 35 invariant tests pass locally. |
Invariant functions that use vm.prank and mutate contract state cause two problems: (1) vm.prank conflicts with the handler's active startPrank, and (2) deposits/withdrawals inside invariant checks corrupt ghost variable accounting. Convert all liveness invariants (invariant_canReceiveEth, invariant_canFillOrder, invariant_canInitiateOrder, invariant_canTransactInNewBlock, invariant_canExitEth, invariant_canExitTokens) to view functions that verify contract liveness without mutating state. Actual liveness is already tested through the handler's fuzz calls. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
[Claude Code] Pushed additional fix in 00720a7 addressing CI failures from the previous push: 4 failing tests in CI:
Root cause: All liveness invariants ( Fix: Converted all state-mutating liveness invariants to 15k+ lines of CI output: The All 34 invariant tests pass locally at 64 runs × 256 depth in ~3.6s. |
Replace bound() with _bound() in all invariant test handlers. bound() calls console2_log on every invocation, producing ~15k log lines across 128k fuzz calls. _bound() performs the same clamping without logging. Also remove unused console2 imports. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When the fuzzer generates recipient == address(passage), withdrawals transfer tokens/ETH from passage to itself (no balance change) while ghost variables still increment totalWithdrawn, breaking accounting invariants. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- OrdersFuzz: widen precompile exclusion from >0x09 to >0xff to cover Cancun's point evaluation precompile at 0x0A, and exclude the Foundry Vm cheatcode address from token fuzz inputs - PassageInvariant: guard withdrawals against recipient==passage (self-transfer doesn't change balance but increments ghost tracking) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
closing as not a priority |
Summary
Adds comprehensive invariant tests covering fund safety, liveness, and sequencing integrity across the Signet/Zenith contract suite.
Changes
Created 5 new test files in
test/invariant/:ZenithInvariant.t.solPassageInvariant.t.solRollupPassageInvariant.t.solOrdersInvariant.t.solTransactorInvariant.t.solInvariants Tested (35 total)
Fund Safety:
Liveness:
Sequencing & Settlement:
Access Control:
Testing
All 35 invariants pass with 50 runs × 20 call depth per invariant (5,000 fuzzing calls each).
Closes ENG-1533