fix(stake): promote cooldown-timelock fields out of colliding _reserved bytes - #261
fix(stake): promote cooldown-timelock fields out of colliding _reserved bytes#261Morenikeoa wants to merge 2 commits into
Conversation
…ed bytes (dcccrypto#250, dcccrypto#258) dcccrypto#242's two-phase cooldown-increase timelock packed pending_cooldown_slots and cooldown_proposed_at_slot into _reserved[10..26], believing that range was free. PERC-313's high-water-mark feature already owns the entire _reserved[10..32] range (hwm_enabled, hwm_floor_bps, epoch_high_water_tvl, hwm_last_epoch). Every cooldown-timelock write silently corrupted HWM state and vice versa: proposing a cooldown increase could disable HWM or wedge the withdrawal floor, and routine HWM refreshes on deposit/withdraw could corrupt or fabricate a pending cooldown proposal, voiding the timelock's 48h LP exit-window guarantee. Promote both fields to real StakePool struct fields (mirroring how pending_admin was added for the v1->v2 bump): STAKE_POOL_SIZE 384 -> 400, CURRENT_VERSION 2 -> 3. Fresh-start cutover, no v2 pools exist, so no migration is needed. Updates the two stale hardcoded-384 size assertions, strengthens the existing collision regression test to also assert against every HWM field (the exact check the original test was missing), and adds a dedicated regression file reproducing all three corruption directions from the bug reports. Verified the negative: temporarily swapping back the old _reserved-packed state.rs makes all three new regression tests fail with the exact corruption described in the issues; restoring the fix makes them pass.
|
Warning Review limit reached
More reviews will be available in 34 minutes and 45 seconds. Learn how PR review limits work. Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file). ⌛ How to resolve this issue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits. 🚦 How do rate limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please see our Fair Usage Limits Policy for further information. 📝 WalkthroughWalkthrough
ChangesStakePool cooldown layout fix
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 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 |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/state.rs`:
- Around line 132-134: The ownership comment on the `_reserved` layout in
`state.rs` is misleading because it says bytes [10..26] are “genuinely free
again” even though the HWM accessors still own `_reserved[10..32]`. Update the
comment near the `_reserved` field to match the actual ownership contract used
by the HWM accessor methods and avoid implying that [10..26] is free for reuse.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: e960983e-f4b1-4596-b40a-a4304ab762e7
📒 Files selected for processing (4)
src/processor.rssrc/state.rstests/poc_cooldown_timelock_hwm_overlap.rstests/struct_layout.rs
Per CodeRabbit review: bytes [10..32] are still owned by the PERC-313 HWM fields, unchanged by this fix. What changed is that the cooldown timelock no longer also writes into that range (it's a real field now). The previous comment said [10..26] was "genuinely free again," which is wrong -- it overlaps [10..32], which HWM still owns.
Problem
#242's two-phase cooldown-increase timelock packedpending_cooldown_slotsandcooldown_proposed_at_slotintoStakePool::_reserved[10..26], believing that range was free ("the previously-free_reserved[10..26]region"). It wasn't: PERC-313's high-water-mark feature already owns the entire_reserved[10..32]range (hwm_enabledat [10],hwm_floor_bpsat [11..13],epoch_high_water_tvlat [16..24],hwm_last_epochat [24..32]).Both features are reachable on the same pool with no guard preventing co-use:
hwm_enabled/hwm_floor_bps/part ofepoch_high_water_tvl, silently disabling HWM or wedging the withdrawal floor so it can never be satisfied.refresh_hwm()— called on every deposit/withdraw/junior-deposit once HWM is enabled — overwritescooldown_proposed_at_slotand part ofpending_cooldown_slots, corrupting or fabricating a pending cooldown proposal and voiding the timelock's 48h LP exit-window guarantee.The existing collision regression test (
pending_cooldown_bytes_do_not_collide_with_neighbors) checked against the discriminator, version,market_resolved, the tranche flag, andrealized_junior_loss— every neighbor except the HWM region it actually overlaps, which is why this shipped unnoticed.Fix
Promoted both fields to real
StakePoolstruct fields, mirroring exactly howpending_adminwas added for the v1→v2 bump (same rationale:_reserveddidn't have enough contiguous free bytes, so this is a real field, not more_reservedpacking):STAKE_POOL_SIZE384 → 400,CURRENT_VERSION2 → 3. Fresh-start cutover per existing project convention — no v2 pools exist, so no migration is needed.384size assertions (tests/struct_layout.rs,src/state.rs) and the field-offset test's stale_reservedoffset (320 → 336, accounting for the two new 8-byte fields at 320/328).tests/poc_cooldown_timelock_hwm_overlap.rs, a dedicated regression covering all three corruption directions from the bug reports (cooldown-proposal-corrupts-HWM, HWM-refresh-corrupts-cooldown-proposal, HWM-activity-fabricates-a-phantom-cooldown-proposal).Proof of Fix
_reserved-packedstate.rsand confirmed all three new regression tests fail with exactly the corruption described in the issues (e.g.hwm_enabledflips after a cooldown proposal; a phantomcooldown_proposed_at_slotof562949953497605appears from HWM activity alone with no cooldown instruction ever issued). Restoring the fix makes all three pass.cargo test --lib(142 tests) and every non-SBF-dependent integration/proptest file (197 more tests, 339 total) pass with no regressions.target/deploy/percolator_stake.soviacargo build-sbf, which isn't available in this sandbox (cargo-build-sbf not found) — this is a pre-existing environment limitation, not something this change causes; the same suites fail identically against unpatchedmainhere for the same reason.Related
Closes #250
Closes #258
Summary by CodeRabbit
New Features
Bug Fixes