PR-D: invariant suite sees settlement - #13
Merged
Merged
Conversation
ProtocolInvariants: hWarp action (warp 1..7 epochs + force-settle every post) so all invariants evaluate settled state; settlement-aware ghost ledger (ghostSettleNet from totals deltas, actual-outflow withdrawals, ghostOps); INV-2 rewritten as one-sided conservation with a 1-wei-per-op engine-favoring dust bound. invariant_engineSolvent now runs against the bucketIndexRay/mint-burn/rescale regime where the S-01 class lived. StakeEngineBucket: test_SolventWithActiveBucket's vacuous assertGe(balance, 0) replaced with real solvency — balance >= settled post totals and >= sum of per-staker claimables. Marker: patch_prD_invariant_warp. Pre-req for PR-C (S-01/S-03/S-09).
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.
PR-D: invariant suite sees settlement
Why
The stateful invariant suite (item 253) deliberately had no warp action, so
no epoch settlement ever fired during a campaign:
invariant_engineSolventnever ran against settled state, and the bucket rebase machinery
(
bucketIndexRay) was exercised by 0/26 tests — exactly the blind spot wherethe S-01 class lived (reviewer engagement 2026-08, vs 58971c05). Separately,
StakeEngineBucket.test_SolventWithActiveBucketassertedassertGe(balance, 0)— vacuous for a uint.PR-C (S-01 sentinel/init, S-03 sMax layering, S-09) changes settlement-adjacent
code and must land against a suite that actually sees settlement. PR-D is that
suite change; it needs nothing and blocks nothing.
What
test/ProtocolInvariants.t.sol(markerpatch_prD_invariant_warp)hWarp(daysSeed): warps 1–7 epochs, then force-settlesevery post via
updatePost. Invariants therefore always evaluatesettled state —
getPostTotals/getUserStakereturn stored values,never projections (which also keeps this suite orthogonal to the S-10
projection-vs-settlement surface fixed in PR-A).
ghostSettleNet— settlement's net effect on principal, measured from thetotals delta across
updatePost(mint == applied gains, burn == appliedlosses by construction in
_applyEpoch/_settleBucket).hWithdrawrecords the actual engine outflow (balance delta), becauseunder a settled bucket (
bucketIndexRay != RAY) the transferredremovedcan differ from the requested amount by floor-dust. The old
ghostWithdrawn += amtwas only accidentally exact while no settlementcould fire.
ghostOpscounts mutating ops for the dust bound.mutating op inside the engine, always engine-favoring, so exact conservation
is unprovable post-settlement. The invariant is now one-sided with a bound:
totals + withdrawn ≤ deposited + settleNet, and the gap≤ ghostOpswei.Value can never leak out of the engine, and dust cannot accumulate
faster than 1 wei per op.
meaning — but they now hold across settlement, which is the point.
test/StakeEngineBucket.t.soltest_SolventWithActiveBucket: the vacuous assert is replaced with realsolvency — engine balance ≥ settled post totals, and ≥ the sum of all
151 stakers' claimable values.
Validation
depth=128;
hWarpfuzzed ~1.7k times, 0 reverts; +4s runtime vs baseline).sequence confirms settlement genuinely fires under the handler
(
ghostSettleNet≠ 0, totalSupply moves, engine balance == settled totalsto the wei) — note the epoch-0 bootstrap means the first crossing only
initializes
lastSnapshotEpoch; campaigns cross many times.forge fmt --checkclean; editor is exact-anchor (FATALs on drift),marker-gated idempotent.
Follow-ups (not this PR)
S10ValidationPoC as the V.8 guard when PR-C touches sMax descent.