Repository navigation
chore(tests): fuzz the financial arithmetic with proptest - #72
Merged
codemagician1949 merged 1 commit intoAug 25, 2026
Merged
codemagician1949 merged 1 commit into
codemagician1949 merged 1 commit into
Conversation
Adds nine property tests over the repayment waterfall and pool-manager share minting, run by `cargo test --workspace` and so already covered by CI. Waterfall splits ---------------- - the four buckets sum to *exactly* the repayment, for any combination of amount and dues — money falling out of the split would be silently unallocated, which is the invariant the waterfall exists to provide - no bucket is ever negative, and none exceeds what it was due (lender yield excepted, which is deliberately uncapped) - strict priority order: a later bucket is only paid once every earlier one is satisfied in full - non-positive repayments are always rejected Share minting ------------- - a positive deposit always mints positive shares, 1:1 at the initial NAV — a lender who deposited real capital and received zero shares has been diluted to nothing by a rounding bug - share totals accumulate exactly across lenders, with nothing lost between them - non-positive deposits are always rejected Why every call goes through `try_` ---------------------------------- A panic inside a Soroban contract aborts the whole test process rather than unwinding. Driving these through the plain client methods would mean one bad generated case takes the entire suite down and reports nothing useful, so every call uses the `try_` variant, where the same condition surfaces as an `Err` a property can assert on. Two real overflows found — reported, not fixed here --------------------------------------------------- The fuzzing found genuine unchecked-overflow bugs. Above `i128::MAX / NAV_SCALE` (~1.7e32): - `pool-manager::join_pool` computes `amount * NAV_SCALE / nav` with unchecked multiplication - `pool-manager::apply_reserve_delta`, which the waterfall forwards to, accumulates into the reserve the same way Both **trap the host** rather than returning a contract error, which aborts the test process outright — so a property cannot assert on them from inside the same binary, and an unbounded probe simply kills the suite. The properties therefore verify correctness across the entire representable domain *up to* that bound, and the bound is recorded in the test file as a known limit with the reasoning. Making overflow return a contract error instead of trapping means changing the arithmetic in `pool-manager`, which is contract behaviour rather than test coverage — and that file is being edited by the event-emission work for IntensifierLab#4, so changing it here would create exactly the merge conflict this sequence of PRs is meant to avoid. Worth its own issue; happy to open one. Verification ------------ - `cargo test --test properties` — 9 properties passing, ~43s - `cargo test --workspace` — all suites passing `proptest` is pinned to 1.5 so a release of it cannot change the generated cases and turn CI red on a run that touched no contract code. Closes IntensifierLab#9
3 tasks
Contributor
|
Nice job @oluwagbemigaphilip |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds nine property tests over the repayment waterfall and pool-manager share
minting, run by
cargo test --workspaceand so already covered by CI.Waterfall splits
amount and dues — money falling out of the split would be silently
unallocated, which is the invariant the waterfall exists to provide
yield excepted, which is deliberately uncapped)
is satisfied in full
Share minting
a lender who deposited real capital and received zero shares has been
diluted to nothing by a rounding bug
them
Why every call goes through
try_A panic inside a Soroban contract aborts the whole test process rather than
unwinding. Driving these through the plain client methods would mean one bad
generated case takes the entire suite down and reports nothing useful, so
every call uses the
try_variant, where the same condition surfaces as anErra property can assert on.Two real overflows found — reported, not fixed here
The fuzzing found genuine unchecked-overflow bugs. Above
i128::MAX / NAV_SCALE(~1.7e32):pool-manager::join_poolcomputesamount * NAV_SCALE / navwithunchecked multiplication
pool-manager::apply_reserve_delta, which the waterfall forwards to,accumulates into the reserve the same way
Both trap the host rather than returning a contract error, which aborts
the test process outright — so a property cannot assert on them from inside
the same binary, and an unbounded probe simply kills the suite.
The properties therefore verify correctness across the entire representable
domain up to that bound, and the bound is recorded in the test file as a
known limit with the reasoning.
Making overflow return a contract error instead of trapping means changing
the arithmetic in
pool-manager, which is contract behaviour rather thantest coverage — and that file is being edited by the event-emission work for
#4, so changing it here would create exactly the merge conflict this
sequence of PRs is meant to avoid. Worth its own issue; happy to open one.
Verification
cargo test --test properties— 9 properties passing, ~43scargo test --workspace— all suites passingproptestis pinned to 1.5 so a release of it cannot change the generatedcases and turn CI red on a run that touched no contract code.
Closes #9