Skip to content

Restore session loses pending bond payouts on terminal-status orders #784

Description

@Catrya

When a dispute is resolved, the loser's bond is slashed and the winning counterparty must claim their share via Action::AddBondInvoice. But the order can reach a terminal status (success, canceledbyadmin, …) while that bond is still in PendingPayout. If the winner restores their session at that point, the restore response contains neither the order (excluded by its terminal status), nor the dispute (excluded because it is no longer active), nor the pending bond payout (restore never queries the bonds table).

The client ends up with no context for the order, so it ignores the add-bond-invoice messages the daemon keeps re-sending every tick. The winner can never submit an invoice and the payout is eventually lost to Forfeited when the claim window elapses.

This is reproducible from two different routes, both of which slash a bond and then drop the order/dispute into
a terminal/inactive status:

  • admin-settle → order ends as success
  • admin-cancel → order ends as canceledbyadmin, dispute as SellerRefunded

Steps to reproduce

  1. Complete a sell order up to active, with both maker and taker bonds locked.
  2. Open a dispute; admin takes it and resolves it (settle or cancel). Loser's bond → PendingPayout, winner is asked for a payout invoice (add-bond-invoice).
  3. Let the order reach its terminal status:
    • settle path: hold invoice settled + release paid → order becomes success
    • cancel path: admin_cancel_action sets order to CanceledByAdmin, dispute to SellerRefunded
  4. As the winner, send a RestoreSession.

Observed: restore response is empty for this trade — the order/dispute are not listed and no pending bond payout
is signaled. The daemon keeps emitting add-bond-invoice for the order, but the client no longer tracks it and
discards them. The user can never claim; the bond eventually forfeits.

Expected: restore surfaces the pending bond-payout claim so the winner can submit their invoice and receive the
slashed-bond share.

Root cause

Restore-session is order-status-centric, while the bond payout has its own state machine in the bonds table, decoupled from order/dispute status:

  • find_user_orders_by_master_key (src/db.rs) filters out EXCLUDED_ORDER_STATUSES, which includes 'success', 'canceledbyadmin', 'settledbyadmin', 'completedbyadmin'.
  • find_user_disputes_by_master_key only restores 'initiated'/'in-progress' disputes; a resolved dispute (settled, SellerRefunded, …) is excluded.
  • RestoreSessionInfo only carries restore_orders + restore_disputes. It has no notion of pending bond payouts, so a PendingPayout bond whose recipient is the restoring user is invisible to recovery.

Both resolution paths reach this through bond::apply_bond_resolution(... BondSlashReason::LostDispute) (called from admin_settle and admin_cancel), which transitions the loser's bond to PendingPayout and triggers add-bond-invoice. The order/dispute then land in statuses the restore queries exclude. The trade is "done" from the order's point of view, yet the winner is still owed a slashed-bond share, and that outstanding claim is exactly what restore drops.

Possible solutions

  1. (Preferred) Surface pending bond payouts in restore. Add to RestoreSessionInfo a list of PendingPayout bonds where the restoring master key is the resolved recipient (resolve_payout_recipient), regardless of order/dispute status. The client rebuilds context and can answer add-bond-invoice.
  2. Re-include terminal orders that have a pending payout. Join bonds in the restore order query so terminal-status orders are returned when a PendingPayout bond targets the user. Cheaper, but the client still has to reconstruct the claim from the order.
  3. Re-trigger the request on restore. On restore, immediately re-emit add-bond-invoice (or call request_payout_invoice) for the user's pending payouts instead of waiting for the next scheduler tick.

Because the bug reaches this state via multiple routes (admin-settle and admin-cancel) and affects every terminal status that can coexist with a PendingPayout bond, partial fixes based only on tweaking the excluded-status list are insufficient. Option 1 addresses the root cause: restore must surface pending bond-payout obligations, not just order/dispute status.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions