feat(#197): defer session removal so a trailing bond-slashed still decrypts - #258
feat(#197): defer session removal so a trailing bond-slashed still decrypts#258AndreaDiazCorreia wants to merge 7 commits into
Conversation
…ssion loss When retaking an order within the grace window, explicitly resolve any pending deferred removal from the canceled take so the fresh session created for the retake survives the old timer. create_session now clears the deferral after inserting the new session, and take_order calls resolve_deferred_removal before creating the session to ensure cleanup.
…ashed decryption Adds Session lifecycle on cancel section explaining the 60-second deferral that keeps the trade key in subscriptions so bond-slashed can be received and decrypted. Documents the status-based removal rules (immediate for Pending/Dispute, deferred otherwise), retake clearing the deferral, and the deliberate divergence from v1's user-initiated/bond-policy approach.
|
Warning Review limit reached
Next review available in: 37 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?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 reviews. How do review 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 refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
WalkthroughCancellation now validates trade-key generations and applies status-based session cleanup. ChangesSession cleanup lifecycle
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant CancellationHandler
participant SessionManager
participant DeferredRemovalScheduler
participant BondSlashedHandler
CancellationHandler->>SessionManager: validate trade-key generation
CancellationHandler->>SessionManager: apply cleanup policy
DeferredRemovalScheduler->>SessionManager: reconcile expired removal
BondSlashedHandler->>SessionManager: resolve deferred removal
CancellationHandler->>SessionManager: create retaken session
Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ 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 |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 1e87ed32f6
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
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 `@rust/src/mostro/session.rs`:
- Around line 127-134: Replace the separately guarded sessions and
deferred_removals maps with a shared SessionState protected by one RwLock, so
create_session atomically checks for stale sessions, inserts the new session,
and clears any deferred removal. Update resolve_deferred_removal and
reconcile_deferred_removals to atomically coordinate deadline removal with
session deletion, preserving existing behavior for due and unresolved entries.
Add a tokio::time::pause() test that interleaves reconciliation or resolution
with a concurrent retry and verifies no stale SessionAlreadyExists result.
🪄 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: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 715b2598-8f5d-4d92-b3df-e12212b1b481
📒 Files selected for processing (3)
rust/src/api/orders.rsrust/src/mostro/session.rsspecs/004-mostro-p2p-client/contracts/orders.md
…k to prevent retake races
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: aa7441189a
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
Blocking changes required
I reviewed the current head (aa7441189a) and revalidated the existing unresolved generation-binding thread. The shared SessionState lock fixes the previous two-map race, but cancellation cleanup is still correlated only by order_id.
On this exact head I reproduced the following sequence with a focused regression test:
- the old take has a deferred session;
- a retake creates a fresh session with a new
trade_key_index; - a delayed
Canceledfrom the old trade key arms a new order-level deferral; - the old
BondSlashedresolves that deferral and removes the fresh session.
The added test fails at the assertion that the retaken session must survive. The same missing guard is also applied too late in the dispatcher: the Canceled arm removes the order-book entry and writes Canceled to the current DB trade before calling apply_cancel_cleanup. Therefore an old-generation delivery can corrupt the fresh retake's persisted/UI state even before its session is removed.
Please bind Canceled, BondSlashed, and deferred-removal state to the recipient trade_index (or another explicit session generation), reject generation mismatches before any order-book, DB, or session mutation, and add a dispatcher-level regression test covering delayed old-key Canceled/BondSlashed deliveries after a retake. This should extend the existing unresolved thread rather than merely adding another lock around order-ID state.
Verification on the current head:
cargo test: 249 passed, 8 ignored, 0 failed- Focused session/cancel tests: green
- Temporary stale-generation regression probe: fails, because the fresh session is deleted
- GitHub CI: Rust, Flutter, and web checks are green, but none exercises this event-ordering case
…ent retake collision
There was a problem hiding this comment.
Blocking race still remains
I reviewed the latest commit (0aa219486d) as a delta from the previously reviewed head. The new generation-tagged deferral correctly prevents an already superseded old-key delivery from deleting the replacement session, and it fixes the prior timer/resolution behavior. However, it does not cover an old Canceled handler that passes the new check and is then overtaken by a retake while suspended in the following awaits.
That remaining interleaving can still corrupt the fresh order-book/DB state and can leave an accepted retake without any session; see the inline blocker. The new tests call generation helpers sequentially, so they cannot exercise this dispatcher/caller ordering.
Required before approval: make generation validation and the affected order-book/DB/session transition one coordinated per-order operation (or make every persisted/cache mutation generation-conditional), handle create_session failure instead of discarding it, and add a real interleaving test that pauses the old Canceled after validation, completes the retake, then resumes it.
Verification on the current head:
cargo test: 254 passed, 8 ignored, 0 failed- GitHub Rust, Flutter, and web checks: all green
- Current branch and live base merge cleanly
…ted takes Introduces install_session that unconditionally installs a session for a daemon-confirmed take, replacing any stale session or clearing pending deferrals. Unlike create_session which fails on collision, install_session assumes the new take owns the order_id and logs a warning when replacing. Refactors session construction into build_session helper shared by both methods.
|
Thanks — all four findings were the same defect seen at successively narrower
What is not in this PR: serializing the gate together with the order-book and DB Verified: |
Closes #197. Part of the anti-abuse bond epic #145.
Problem
On a timeout slash the daemon sends
canceledfirst andbond-slashed~150 ms later. Deleting the session oncanceleddrops the trade key from the subscription filter and discards its decryption key, so the notice can never be received or decrypted.v2 turned out never to delete sessions at all —
remove_sessionhad no production caller. This defines the policy before #191 and #208 introduce that deletion, and closes the leak with the deferral already in place.Fix
SessionManagertracks deferred removals (defer / resolve / reconcile).cancel_cleanupdecides from the status held before the cancel:Pendingremoves at once (the cancel returns the bond), dispute and admin states keep the session (the admin chat still needs its keys), anything else — including an unknown status — defers 60 s.Action::Canceledreads that status before syncingCanceled;Action::BondSlashedsettles a pending deferral and is a no-op without one.Divergence from v1
v1 keys off
!userInitiated && hadBond; this keys off the pre-cancel status, needing neither an outbound cancel marker nor a bond-policy lookup. It defers in a few cases v1 would not — the cost is a session held 60 s longer. Sessions are in-memory only, so v1's restart reconciliation does not apply. Documented inspecs/004-mostro-p2p-client/contracts/orders.md.Tests
9 pure session tests plus 4 through the dispatcher path.
cargo test247 passing,cargo clippyclean,frb-generate --checkunchanged.Summary by CodeRabbit
Bug Fixes
Documentation