test(pm): replay test — leverage/margin math invariants (t17) - #154
Merged
Conversation
Adds tests/pm/replay/t17_leverage_math_invariants.cpp, which LINKS the real
libraries/chain/pm/leverage.cpp margin math (no chain build required) and asserts the
safety properties the leverage subsystem depends on, across adversarial + fuzzed inputs:
I. bounded k on buy: new_reserve_a × new_reserve_b ∈ (k − divisor, k] — the CPMM floor
never lets the product EXCEED the stored k (no phantom constant-product for the pool)
and loses at most one divisor-unit, so tokens_out is not unboundedly inflated. The
stored k is not reassigned on a bet, so this bound holds bet-over-bet.
II. round-trip non-profit: buy `amount`, immediately cancel the tokens at the same
reserves → cancel_value ≤ amount. A self-sandwich cannot mint against the curve.
III. monotonicity: cancel_value_after_opposing is non-increasing in the opposing bet m —
the property that makes worst_opposing_bet the actual worst case for the open-time
solvency check.
IV. solvency: max_leverage_loan only returns a loan L for which the worst-case cancel
value cvw ≥ threshold_safe (= liquidation_threshold(L) × (1 + s%)), i.e. it never
hands out an under-margined loan; returns 0 when nothing qualifies.
V. 400k-case randomized fuzz over the reachable reserve range: no negative outputs, no
128-bit wrap, k bounded, round-trip-safe — 0 violations.
The k bound is the exact floor guarantee: new_out = floor(k / new_in) ⇒
new_in × new_out ∈ (k − new_in, k]. (My first draft asserted the wrong direction / wrong
divisor; the math is correct — characterizing the exact bound is what the exercise
surfaced.) Registered in tests/pm/replay/build.sh; built + run green with g++.
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.
Summary
Companion to the settlement replay test — adds
t17_leverage_math_invariants.cpp, which links the real leverage/margin math (libraries/chain/pm/leverage.cpp) and asserts the safety properties the margin subsystem depends on, across adversarial + fuzzed inputs. No chain build required (seetests/pm/replay/README.md); runs in seconds.Invariants checked
new_reserve_a × new_reserve_b ∈ (k − divisor, k]. The CPMM floor never lets the product exceed the storedk(no phantom constant-product accruing to the pool) and loses at most one divisor-unit, sotokens_outis not unboundedly inflated in the bettor's favour. The storedkis not reassigned on a bet, so the bound holds bet-over-bet.amount, immediately cancel the tokens at the same reserves →cancel_value ≤ amount. A self-sandwich cannot mint against the curve.cancel_value_after_opposingis non-increasing in the opposing betm— the property that makesworst_opposing_betthe actual worst case for the open-time solvency check.max_leverage_loanonly returns a loanLfor which the worst-case cancel valuecvw ≥ threshold_safe(=liquidation_threshold(L) × (1 + s%)); it never hands out an under-margined loan, and returns 0 when nothing qualifies.Note
The k bound is the exact floor guarantee:
new_out = floor(k / new_in)⇒new_in × new_out ∈ (k − new_in, k]. My first draft asserted the wrong direction (and used the wrong reserve as the divisor); the math is correct — pinning the exact bound is what the exercise surfaced, and it's now documented in the test.Verification
Built and run green with
g++:Registered in
tests/pm/replay/build.sh; existing linked cases (t10/t15) still pass — no regression.