Skip to content

restore does not persist recovered orders, so recovered trades cannot be continued #188

Description

@grunch

Summary

mostro-cli restore asks Mostro for the user's active orders and disputes and prints them, but it never writes them into the local orders table. Every command that acts on a specific order looks it up there first, so after a mnemonic-only recovery the user can see their in-flight trades but cannot act on any of them.

This makes the documented "restore on a new machine" path incomplete: it recovers your identity, but not your ability to finish a trade that was already open.

Found while auditing the README against the code (#186), which now documents the limitation. This issue tracks closing the gap in the code.

Reproduction

On a machine whose ~/.mcli/mcli.db has been recreated from a backed-up mnemonic, with an order in progress:

$ mostro-cli restore
🔄 Restore Session Response
📋 Found 1 pending order(s):
  1. Order ID: <uuid>
     Trade Index: 7
     Status: FiatSent

✅ Session restore completed successfully!

$ mostro-cli release -o <uuid>
no rows returned by a query that expected to return at least one row

The restore reports success, and the very next step fails.

Root cause

Action::RestoreSession in src/parser/dms.rs:826 iterates restore_data.restore_orders and restore_data.restore_disputes purely to println! them:

for (i, order_info) in restore_data.restore_orders.iter().enumerate() {
    println!("  {}. Order ID: {}", i + 1, order_info.order_id);
    println!("     Trade Index: {}", order_info.trade_index);
    println!("     Status: {:?}", order_info.status);
    println!();
}

No Order is constructed and Order::save is never called, so the orders table is untouched. src/cli/restore.rs has a &Context (and therefore the pool) available — it is passed down to print_commands_results — so nothing structural prevents persisting here.

Impact

Every command that resolves an order through Order::get_by_id fails on a restored order. All of these hard-error, none degrade gracefully:

Command Call site
release, cancel, fiatsent, dispute src/cli/send_msg.rs:89, :159
addinvoice src/cli/add_invoice.rs:15
addbondinvoice src/cli/add_bond_invoice.rs:20
rate src/cli/rate_user.rs:39
senddm src/cli/send_dm.rs:45
dmtouser src/cli/dm_to_user.rs:21
getdmuser src/cli/get_dm_user.rs:31
sendadmindmattach src/cli/send_admin_dm_attach.rs:212

Worth noting the failure is opaque: release surfaces sqlx's no rows returned by a query that expected to return at least one row, which gives the user no clue that the order is simply absent from the local cache.

Why this should be tractable

What those commands actually need from the row is order.trade_keys — the keypair the order was opened with. That is derivable, because RestoredOrdersInfo (mostro-core 0.14.1, src/message.rs:484) carries exactly the missing piece:

pub struct RestoredOrdersInfo {
    pub order_id: Uuid,
    pub trade_index: i64,
    pub status: String,
}

User::get_trade_keys(pool, trade_index) already derives a keypair from an index, so trade_index → trade keys → a usable local row is a short path. RestoredDisputesInfo likewise carries order_id, trade_index, status, initiator and solver_pubkey.

Suggested approach

  1. In the RestoreSession handler, for each restored order: derive the trade keys from trade_index, and upsert an orders row keyed on order_id (upsert rather than insert, so re-running restore is idempotent and does not clobber a row that is already richer).

  2. Decide what to do about the columns the payload does not carry. orders declares kind, status, amount, fiat_code, fiat_amount, payment_method and premium as NOT NULL (src/db.rs:29-46), so a row cannot be written from RestoredOrdersInfo alone. Two options:

    • fetch the public kind-38383 event for each order id and fill the real values — the ordersinfo path already does this kind of lookup, so it could be reused;
    • or relax those columns to nullable and treat a restored row as a stub that later messages backfill.

    The first keeps the local cache faithful; the second is smaller but spreads Option handling into the printing code.

  3. Consider persisting disputes too, so a restored user can keep talking to their solver.

  4. Whatever is chosen, restore should stop printing ✅ Session restore completed successfully! when the orders were only displayed — the message currently overstates what happened.

Acceptance

After mostro-cli restore on a database rebuilt from the mnemonic, an order that Mostro reports as active can be carried to completion — at minimum release / fiatsent / senddm work against it without further manual steps.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions