Skip to content

PR-D: invariant suite sees settlement - #13

Merged
VSP-0 merged 1 commit into
mainfrom
pr-d-invariant-warp
Aug 30, 2026
Merged

PR-D: invariant suite sees settlement#13
VSP-0 merged 1 commit into
mainfrom
pr-d-invariant-warp

Conversation

@VSP-0

@VSP-0 VSP-0 commented Aug 30, 2026

Copy link
Copy Markdown
Contributor

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_engineSolvent
never ran against settled state, and the bucket rebase machinery
(bucketIndexRay) was exercised by 0/26 tests — exactly the blind spot where
the S-01 class lived (reviewer engagement 2026-08, vs 58971c05). Separately,
StakeEngineBucket.test_SolventWithActiveBucket asserted
assertGe(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 (marker patch_prD_invariant_warp)

  • New handler action hWarp(daysSeed): warps 1–7 epochs, then force-settles
    every post via updatePost. Invariants therefore always evaluate
    settled state — getPostTotals/getUserStake return stored values,
    never projections (which also keeps this suite orthogonal to the S-10
    projection-vs-settlement surface fixed in PR-A).
  • Settlement-aware ghost ledger:
    • ghostSettleNet — settlement's net effect on principal, measured from the
      totals delta across updatePost (mint == applied gains, burn == applied
      losses by construction in _applyEpoch/_settleBucket).
    • hWithdraw records the actual engine outflow (balance delta), because
      under a settled bucket (bucketIndexRay != RAY) the transferred removed
      can differ from the requested amount by floor-dust. The old
      ghostWithdrawn += amt was only accidentally exact while no settlement
      could fire.
    • ghostOps counts mutating ops for the dust bound.
  • INV-2 rewritten: bucket share↔index floor division strands ≤1 wei per
    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 ≤ ghostOps wei.
    Value can never leak out of the engine, and dust cannot accumulate
    faster than 1 wei per op.
  • INV-1 (solvency), INV-3 (VS bounds), INV-4 (single-sided) unchanged in
    meaning — but they now hold across settlement, which is the point.

test/StakeEngineBucket.t.sol

  • test_SolventWithActiveBucket: the vacuous assert is replaced with real
    solvency — engine balance ≥ settled post totals, and ≥ the sum of all
    151 stakers' claimable values.

Validation

  • Full suite: 232 passed / 0 failed (invariant campaign runs=64,
    depth=128; hWarp fuzzed ~1.7k times, 0 reverts; +4s runtime vs baseline).
  • Teeth check (sandbox, not shipped): scripted asymmetric-stake → warp → warp
    sequence confirms settlement genuinely fires under the handler
    (ghostSettleNet ≠ 0, totalSupply moves, engine balance == settled totals
    to the wei) — note the epoch-0 bootstrap means the first crossing only
    initializes lastSnapshotEpoch; campaigns cross many times.
  • forge fmt --check clean; editor is exact-anchor (FATALs on drift),
    marker-gated idempotent.

Follow-ups (not this PR)

  • PR-C lands the S-01/S-03/S-09 rulings against this suite; keep
    S10ValidationPoC as the V.8 guard when PR-C touches sMax descent.

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).
@VSP-0
VSP-0 merged commit aa7d477 into main Aug 30, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant