feat(cashu): take flow escrow request + unblock creation — Track A TA-2 - #830
feat(cashu): take flow escrow request + unblock creation — Track A TA-2#830grunch wants to merge 2 commits into
Conversation
|
Warning Review limit reached
Next review available in: 25 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 (7)
✨ 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.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 6652b7079f
ℹ️ 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".
| order_updated | ||
| .update(pool) | ||
| .await |
There was a problem hiding this comment.
Guard Cashu take updates with a CAS
When two TakeBuy/TakeSell events for the same pending Cashu order run concurrently, both handlers can hold a stale Pending copy and reach this unconditional full-row update. If the first request has already let the seller submit AddCashuEscrow and the CAS has advanced the row to Active with cashu_* fields populated, the later stale update can rewrite the row back to WaitingPayment and clear the persisted escrow while notifications have already gone out. The take transition should be conditional on the current status/empty escrow before enqueueing the request, or re-read and abort once the order has moved on.
Useful? React with 👍 / 👎.
e2f032a to
760467f
Compare
6652b70 to
a16c745
Compare
760467f to
18cc7d6
Compare
Add the Cashu branch to `take_sell_action`/`take_buy_action` and the `show_cashu_escrow_request` helper (docs/cashu/02-track-a-lock.md §5), and unblock `NewOrder`/`TakeBuy`/`TakeSell` in `dispatch_cashu`. A taken order on a cashu node now asks the SELLER to lock a 2-of-3 escrow instead of paying a hold invoice, leaving the order in `WaitingPayment` where the TA-1 CAS expects it. Creatable and takeable ship together so the book never fills with untakeable orders (the orphan-order hazard CF-5 warns about). - `util::show_cashu_escrow_request`: advance to `WaitingPayment`, record both trade pubkeys, publish the order event, and enqueue an escrow request to the seller (`Action::WaitingSellerToPay` + `Payload::Order` carrying `amount`/`buyer_trade_pubkey`/`seller_trade_pubkey`) plus a bare "waiting for the seller" notice to the buyer. The buyer redeems ecash directly, so there is no buyer payout invoice. The escrow locks `order.amount` exactly (the fee is a separate token in TA-1f); the mint URL + locktime floor are node policy the daemon enforces authoritatively at lock validation (TA-1 §5/§7), so they are not carried in the request (the 0.14.0 protocol has no field for them). - `take_sell`/`take_buy`: early-return through `show_cashu_escrow_request` when `is_cashu_enabled()`, skipping the hold-invoice / buyer-invoice path. A supplied buyer invoice is ignored in Cashu mode. Lightning path unchanged. - `dispatch_cashu`: route `NewOrder`/`TakeBuy`/`TakeSell` to `handle_message_action_no_ln` (their real handlers); the gate test drops them from the `InvalidAction` list accordingly. Test: `show_cashu_escrow_request` advances the status, records both pubkeys, and enqueues the seller request (with the trade pubkeys + bare amount) and the buyer notice. Depends on CF-5 (+ TA-1 for the full e2e lock). Base: feat/cashu-ta1-lock-handler Refs: docs/cashu/02-track-a-lock.md (TA-2)
Addresses the P1 review finding on the TA-2 take flow. show_cashu_escrow_request built a full row from an order copy read at the top of the take handler and wrote it unconditionally. Two concurrent TakeBuy/TakeSell for the same pending order both pass the caller's in-memory check_status, so the loser's write lands second — dragging the status back to WaitingPayment with its own trade keys and nulling every column its stale copy does not carry. If the seller had already submitted AddCashuEscrow in the window, that includes the persisted cashu_escrow_token: a funded escrow silently erased, with the lock notifications already sent. The helper now claims the Pending -> WaitingPayment transition through db::claim_order_status before touching anything, so only one taker reaches the write and the loser aborts with CantDo(NotAllowedByStatus) having changed nothing. The claim additionally refuses any order whose escrow is already funded, so a late take can never drag a funded order backwards whatever its current status. Note the Lightning path has the same shape — show_hold_invoice writes a full row just as unconditionally — but its stale write clears an invoice hash/preimage that the invoice-subscription flow reconciles, not an escrow token that nothing can recover. Worth a separate issue; not changed here. Track A §5 is updated to match.
a16c745 to
f76d5fe
Compare
What & why
Completes the Cashu lock flow end-to-end with TA-1 (
docs/cashu/02-track-a-lock.md§5). A taken order on a cashu node now asks the seller to lock a 2-of-3 escrow instead of paying a hold invoice, and unblocks order creation + the take flow indispatch_cashu.Creation and taking ship together: unblocking
NewOrderany earlier would populate the book with orders that can never be taken to completion (no escrow) — the orphan-order hazard CF-5's rationale warns about.Changes
util::show_cashu_escrow_request(new, the Cashu analogue ofshow_hold_invoice): advance the order toWaitingPayment(where the TA-1 CAS expects it), record both trade pubkeys, publish the order event, and enqueue:Action::WaitingSellerToPay+Payload::Order(SmallOrder)carryingamount+buyer_trade_pubkey+seller_trade_pubkey(everything the client needs to build the 2-of-3);take_sell/take_buy: early-return throughshow_cashu_escrow_requestwhenis_cashu_enabled(), skipping the hold-invoice / buyer-invoice path. The buyer redeems ecash directly, so there is no buyer payout invoice (a supplied one is ignored). Lightning path byte-for-byte unchanged.dispatch_cashu:NewOrder/TakeBuy/TakeSell→handle_message_action_no_ln(their real handlers). The CF-5 gate test drops them from theInvalidActionlist.Design notes
order.amountexactly — the Mostro fee is a separate token (Option 2, TA-1f).add_cashu_escrow_action§5/§7), so a client funding against the wrong mint or too short a locktime is simply rejected and retries.Action::WaitingSellerToPay+Payload::Order(with the trade pubkeys set) — on a cashu node the seller's client reads that as "lock the escrow", the same convention the first-attempt branch used.Tests
show_cashu_escrow_requestadvances the status toWaitingPayment, records both trade pubkeys, and enqueues the seller escrow request (asserting theOrderpayload carries the trade pubkeys + bare amount, no buyer invoice) and the buyer notice.Checklist
cargo fmt --checkcargo clippy --all-targets --all-features -- -D warningscargo test— 1027 passedDepends on #829 (TA-1), #828 (CF-5). Refs:
docs/cashu/02-track-a-lock.md(TA-2).🧪 Manual testing — step by step
Prereqs: the Cashu-mode
mostrodsetup from CF-5 (#828) — mint up (docker compose -f docker-compose.cashu.yml up -d),[cashu] enabled = true. This branch is stacked on TA-1, so check it out (it contains CF-5 + TA-1 + TA-2). Run withRUST_LOG=mostro=info.1. Order creation + taking are unblocked in Cashu mode
NewOrder).InvalidActionas in CF-5) and appears in the book asPending. ✔️TakeSell, orTakeBuyfor a buy order).2. The seller gets an escrow request; the order waits for the lock
Action::WaitingSellerToPaycarrying aPayload::Order(SmallOrder) whoseamount= the order amount and whosebuyer_trade_pubkey/seller_trade_pubkeyare set — everything the seller's client needs to build the 2-of-3. The buyer receives a bareWaitingSellerToPay"waiting for the seller" notice. ✔️status = 'waiting-payment', both trade pubkeys recorded. This is exactly where TA-1's lock CAS expects the order. ✔️3. Unit test
cargo test --bin mostrod show_cashu_escrow_requestExpected: the helper advances the order to
WaitingPayment, records both trade pubkeys, and enqueues the seller escrow request (Order payload with the trade pubkeys + bare amount) plus the buyer notice. ✔️4. Lightning regression
[cashu] enabled = false, create + take an order on an LND node.