feat(stake): two-phase timelock on cooldown_slots increases (#242) - #246
Conversation
Implements the N-2 timelock that #243 prepared (TIMELOCK_SLOTS). The capital-LOCK vector in #242 is a cooldown_slots INCREASE: a compromised admin could set cooldown = MAX_COOLDOWN_SLOTS (~1 year) in a single UpdateConfig tx and lock LP withdrawals instantly. That increase now requires a two-phase, time-delayed commit. - ProposeCooldownIncrease (tag 7, reclaimed) — admin records the new (larger) value + proposal slot. Rejects non-increases (those use UpdateConfig). - CommitCooldownIncrease (tag 8) — applies the pending value ONLY after TIMELOCK_SLOTS (~48h) have elapsed, giving LP holders a guaranteed exit window. - CancelCooldownIncrease (tag 9) — admin withdraws a pending proposal. - UpdateConfig now REJECTS a cooldown INCREASE (CooldownIncreaseRequiresTimelock); decreases (LP-friendly) and deposit_cap stay immediate. State: pending_cooldown_slots + cooldown_proposed_at_slot stored in the previously-free _reserved[10..26] — no struct-size change, no version bump (fresh-start cutover). 3 new errors (25/26/27). FlushToInsurance / SetMarketResolved intentionally stay immediate (PDA-custody + #227/#229 guards / one-way operational — see N-2 comment). Verification: - timelock_window_elapsed extracted as a pure helper; 3 unit tests + 2 accessor tests + collision-with-neighbors test; instruction round-trip tests; tombstone tests updated. - Kani proof kani_timelock_window_elapsed_matches_spec: VERIFIED (293 properties, 2/2 cover properties satisfied — non-vacuous, both elapsed/not-elapsed reachable, overflow is Err not panic). - 138 lib tests + full integration suite (incl. LiteSVM e2e) pass, 0 failures; cargo build-sbf clean. NOT deployed (cutover-gated). Closes #242. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (7)
📝 WalkthroughWalkthroughImplements a two-phase timelock for admin cooldown increases. Three new ChangesCooldown Increase Timelock
Sequence Diagram(s)sequenceDiagram
participant Admin
participant Processor
participant StakePool
participant Clock
rect rgba(255, 165, 0, 0.5)
note over Admin,Clock: Phase 1 — Propose
Admin->>Processor: ProposeCooldownIncrease(new_cooldown_slots)
Processor->>StakePool: set_pending_cooldown_slots(new_value)
Processor->>Clock: read slot
Processor->>StakePool: set_cooldown_proposed_at_slot(slot)
end
rect rgba(255, 0, 0, 0.5)
note over Admin,Processor: Blocked direct path
Admin->>Processor: UpdateConfig(cooldown_increase)
Processor-->>Admin: Err(CooldownIncreaseRequiresTimelock)
end
rect rgba(0, 128, 0, 0.5)
note over Admin,Clock: Phase 2 — Commit (after timelock)
Admin->>Processor: CommitCooldownIncrease
Processor->>StakePool: cooldown_proposed_at_slot()
Processor->>Clock: read slot
Processor->>Processor: timelock_window_elapsed(proposed_at, TIMELOCK_SLOTS, now)
Processor->>StakePool: pool.cooldown_slots = pending_cooldown_slots
Processor->>StakePool: set_cooldown_proposed_at_slot(0)
end
rect rgba(100, 100, 200, 0.5)
note over Admin,StakePool: Cancel path
Admin->>Processor: CancelCooldownIncrease
Processor->>StakePool: set_cooldown_proposed_at_slot(0)
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Poem
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Implements the N-2 timelock #243 prepared. A compromised admin could set
cooldown_slots = MAX(~1yr) in oneUpdateConfigtx and instantly lock LP withdrawals. Now a cooldown increase requires a two-phase, ~48h-delayed commit, giving LPs a guaranteed exit window.TIMELOCK_SLOTS) → CancelCooldownIncrease (tag 9).UpdateConfignow rejects an increase (CooldownIncreaseRequiresTimelock); decreases +deposit_capstay immediate._reserved[10..26]— no struct-size change / version bump (fresh cutover). 3 new errors (25/26/27).FlushToInsurance/SetMarketResolvedintentionally stay immediate (PDA-custody + fix(stake): verify wrapper_vault SPL Token ownership before raw mint read in FlushToInsurance #227/fix(stake): block FlushToInsurance after SetMarketResolved #229 guards / one-way operational — documented in the N-2 comment).Verification:
timelock_window_elapsedpure helper + Kani proof (293 properties, 2/2 covers satisfied — non-vacuous); unit tests for helper/accessors/neighbor-collision; instruction round-trip + tombstone tests updated; 138 lib + full integration suite (incl. LiteSVM e2e) pass, 0 failures;build-sbfclean. NOT deployed (cutover-gated).Closes #242.
🤖 Generated with Claude Code
Summary by CodeRabbit