Skip to content

docs(cashu): specs for Tracks B/C/D (release, coop-cancel, dispute)#833

Open
grunch wants to merge 1 commit into
mainfrom
docs/cashu-tracks-bcd-specs
Open

docs(cashu): specs for Tracks B/C/D (release, coop-cancel, dispute)#833
grunch wants to merge 1 commit into
mainfrom
docs/cashu-tracks-bcd-specs

Conversation

@grunch

@grunch grunch commented Jul 20, 2026

Copy link
Copy Markdown
Member

What & why

Adds the three remaining feature-track specs to the Cashu series, so the whole escrow feature is planned end-to-end before the code lands. Spec only — no implementation. Each mirrors the structure of 01-fundamentals.md / 02-track-a-lock.md (goal & scope, flow + state transitions, what it consumes from Fundamentals + Track A, handler algorithm, PR breakdown, issues table, DoD, cross-track obligations).

# Doc Scope
03 03-track-b-release.md Happy-path settlement: FiatSent (with the §4B remaining-locktime guard + escrow_settlement_margin_days) then Release. The seller's release signature travels P2P; Mostro only advances state and makes the trade rateable.
04 04-track-c-coop-cancel.md Cooperative unwind: both parties Cancel, the buyer signs so the seller reclaims, and Mostro refunds the seller the fee (2 * order.fee) — the first executor of the Track A §4A refund obligation, single-shot.
05 05-track-d-dispute.md Arbitration — the only track where P_M signs. sign_with_pm + CashuPmSignature delivery to the winner (AdminSettle→buyer, AdminCancel→seller), the §4B near-locktime solver alert, and the seller-wins fee refund.

Design continuity

  • No new mostro-core protocol variant. Track D uses the frozen CashuPmSignature / CashuSignatures surface already on main (0.13.0 baseline). The only new daemon-side capability is sign_with_pm in CashuClient.
  • Every blocked dispatch_cashu action gets an owner, closing the CF-5 §6 action-ownership matrix: FiatSent/Release/RateUser (B), Cancel (C), Dispute/AdminTakeDispute/AdminSettle/AdminCancel/AdminAddSolver (D). AddInvoice/AddBondInvoice stay permanently blocked by design.
  • The cross-track obligations Track A defined (§4A fee refund, §4B FiatSent guard + solver alert) are each assigned to the executing track's DoD.

Notes

Refs: docs/cashu/01-fundamentals.md, docs/cashu/02-track-a-lock.md.

Summary by CodeRabbit

  • Documentation
    • Added draft specifications for Cashu escrow release, cooperative cancellation, and dispute resolution workflows.
    • Documented state transitions, security safeguards, signatures, refunds, and near-locktime handling.
    • Updated the Cashu documentation index with links to the new track documents and their draft status.

🧪 Manual testing — step by step

This is a docs-only PR (no code, no runtime), so "testing" here means verifying the specs render and are internally consistent. Nothing to build or run.

1. The three new specs render and are complete

  1. Open each on the branch:
    • docs/cashu/03-track-b-release.md
    • docs/cashu/04-track-c-coop-cancel.md
    • docs/cashu/05-track-d-dispute.md
  2. Expected: each mirrors the 01/02 structure — Status/Depends header, Goal & scope, a mermaid flow + state transitions, a "consumes from Fundamentals + Track A" table, handler algorithm, PR breakdown, issues table, Definition of Done, and a cross-track-obligations table. The mermaid diagrams render on GitHub. ✔️

2. Links and cross-references resolve

  1. From docs/cashu/README.md, click the 03/04/05 rows — they now link to the new files (Status column shows Draft, no longer "Planned"). ✔️
  2. In each spec, the back-links to 01-fundamentals.md, 02-track-a-lock.md, and ../CASHU_ESCROW_ARCHITECTURE.md resolve. ✔️

3. Design continuity holds

  1. Confirm the cross-track obligations Track A defined are each assigned to an executing track: the §4A fee refund → Tracks C & D; the §4B FiatSent guard + solver alert → Tracks B & D. ✔️
  2. Confirm every dispatch_cashu action blocked in CF-5 §6 gets an owner across B/C/D (FiatSent/Release/RateUser → B; Cancel → C; Dispute/AdminTakeDispute/AdminSettle/AdminCancel/AdminAddSolver → D), and that AddInvoice/AddBondInvoice remain permanently blocked. ✔️
  3. Confirm no new mostro-core protocol variant is required — Track D uses the frozen CashuPmSignature / CashuSignatures surface already on main. ✔️

4. Optional lint

# if a markdown linter is available locally
markdownlint docs/cashu/0[345]-*.md

Expected: clean (or only pre-existing repo-wide rules). ✔️

Add the three remaining feature-track specs to the Cashu series, mirroring the
structure of 01-fundamentals.md / 02-track-a-lock.md (goal & scope, flow +
state transitions, what it consumes from Fundamentals + Track A, handler
algorithm, PR breakdown, issues table, Definition of Done, cross-track
obligations). Spec only — no implementation.

- 03-track-b-release.md — happy-path settlement: `FiatSent` (with the §4B
  remaining-locktime guard + `escrow_settlement_margin_days`) then `Release`;
  the seller's release signature travels P2P, Mostro only advances state and
  makes the trade rateable. Executes the §4B `FiatSent` guard obligation.
- 04-track-c-coop-cancel.md — cooperative unwind: both parties `Cancel`, the
  buyer signs so the seller reclaims, and Mostro refunds the seller the fee
  (`2 * order.fee`) — the first executor of the Track A §4A refund obligation
  (single-shot).
- 05-track-d-dispute.md — arbitration: the only track where `P_M` signs.
  `sign_with_pm` + `CashuPmSignature` delivery to the dispute winner
  (`AdminSettle`→buyer, `AdminCancel`→seller), the §4B near-locktime solver
  alert, and the seller-wins fee refund. Uses the frozen
  `CashuPmSignature`/`CashuSignatures` 0.13.0 protocol surface — no new variant.

Each spec is off-by-default and additive; every blocked `dispatch_cashu` action
is given an owner, closing the CF-5 §6 action-ownership matrix. README status
table updated (03/04/05 Planned → Draft).

Refs: docs/cashu/01-fundamentals.md, docs/cashu/02-track-a-lock.md
@coderabbitai

coderabbitai Bot commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

Adds draft documentation for Cashu escrow release, cooperative cancellation, and dispute resolution, including state transitions, signature delivery, locktime protection, refunds, dispatch routing, implementation breakdowns, and completion criteria. The Cashu README now links to Tracks B–D and marks them as drafts.

Changes

Cashu Escrow Lifecycle

Layer / File(s) Summary
Track B release flow
docs/cashu/03-track-b-release.md, docs/cashu/README.md
Documents Cashu settlement from Active through FiatSent to Success, the locktime margin guard, P2P release-signature delivery, rating timing, and draft roadmap status.
Track C cooperative cancellation
docs/cashu/04-track-c-coop-cancel.md
Defines cooperative cancellation, buyer-signature relay, seller escrow recovery, Canceled transition, and idempotent 2 * order.fee refunds.
Track D dispute resolution
docs/cashu/05-track-d-dispute.md
Defines solver-led dispute outcomes, P_M signing, signature delivery, near-locktime alerts, admin settlement/cancellation behavior, and seller refunds.

Estimated code review effort: 2 (Simple) | ~10 minutes

Poem

I’m a rabbit with docs in my burrow tonight,
Release, cancel, dispute—each path tucked in tight.
Tokens know when to wait,
Signatures travel straight,
And draft tracks make the escrow flow right.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the documentation-only specs added for Cashu Tracks B/C/D.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch docs/cashu-tracks-bcd-specs

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 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 `@docs/cashu/03-track-b-release.md`:
- Around line 104-115: Update the CashuSettings implementation and startup
validation to add escrow_settlement_margin_days and require it to be strictly
below escrow_locktime_days. In the Cashu branch of fiat_sent_action, explicitly
reject expired or missing locktimes with the appropriate CantDo reason, and use
checked or saturating subtraction for locktime-versus-now calculations so
underflow cannot disable every FiatSent.
- Around line 163-167: The documented track ordering is inconsistent: update
docs/cashu/03-track-b-release.md lines 163-167 to remove or qualify Track B’s
parallelism with Tracks C/D, update docs/cashu/04-track-c-coop-cancel.md lines
154-161 to state Track B as a dependency if it is required for merge or runtime
behavior, and update docs/cashu/05-track-d-dispute.md lines 176-186 to state
Track C as a dependency if required for implementation.

In `@docs/cashu/04-track-c-coop-cancel.md`:
- Around line 102-109: Define one crash-safe, single-shot seller-refund contract
in docs/cashu/04-track-c-coop-cancel.md lines 102-109: specify atomic refund
claiming, durable retry/idempotency behavior, and explicit fee-state semantics
for minting. In docs/cashu/05-track-d-dispute.md lines 137-143, require
seller-win disputes to call the same refund helper and follow the same contract,
including races between cancel and dispute terminal states.

In `@docs/cashu/05-track-d-dispute.md`:
- Around line 132-135: Make the near-locktime handling in
admin_take_dispute_action enforceable by defining a hard resolution
deadline/guard based on the escrow’s remaining locktime and the resolution SLA,
and prevent finalization that would produce an unusable P_M signature. Specify
the explicit recovery path when the guard is reached, rather than relying only
on logging and priority-flagging.
🪄 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

Run ID: d3555a40-4543-47e2-96e8-2d911d3ac05c

📥 Commits

Reviewing files that changed from the base of the PR and between 57b8ff5 and 1867913.

📒 Files selected for processing (4)
  • docs/cashu/03-track-b-release.md
  • docs/cashu/04-track-c-coop-cancel.md
  • docs/cashu/05-track-d-dispute.md
  • docs/cashu/README.md

Comment on lines +104 to +115
- New `#[serde(default)]` key on `CashuSettings`: **`escrow_settlement_margin_days`,
default 3**. Added by Track B (not needed during foundation).
- In the Cashu branch of `fiat_sent_action`: read the stored escrow token's
`locktime`; reject `FiatSent` with a clear `CantDo` (e.g.
`CashuEscrowNotLocked` if the token/locktime is missing, else a
settlement-window reason) when
`locktime - now < escrow_settlement_margin_days`. Fiat can never be sent inside
the danger window, so the seller cannot weaponise the locktime.

The margin must be comfortably below the locktime floor (`escrow_locktime_days`,
default 15) so a normal trade has ample settlement room; the difference (≈12
days) is the usable fiat-settlement window.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Specify margin validation and expired-locktime handling.

The supplied CashuSettings currently has no escrow_settlement_margin_days; the implementation plan should require startup validation that the margin is below escrow_locktime_days. Also define expired-locktime behavior explicitly and require checked/saturating subtraction, otherwise a configurable margin can disable every FiatSent and unsigned locktime - now arithmetic can underflow.

🤖 Prompt for 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.

In `@docs/cashu/03-track-b-release.md` around lines 104 - 115, Update the
CashuSettings implementation and startup validation to add
escrow_settlement_margin_days and require it to be strictly below
escrow_locktime_days. In the Cashu branch of fiat_sent_action, explicitly reject
expired or missing locktimes with the appropriate CantDo reason, and use checked
or saturating subtraction for locktime-versus-now calculations so underflow
cannot disable every FiatSent.

Comment on lines +163 to +167
| **TB-1** | `escrow_settlement_margin_days` + `FiatSent` §4B guard | CF-1, Track A | Tracks C/D | `config/*`, `fiat_sent.rs` | Low-Medium |
| **TB-2** | `release_action` Cashu branch + unblock `Release`/`RateUser` | CF-5, Track A, TB-1 | Tracks C/D | `release.rs`, `app.rs` | Medium |

All of Track B is parallel with Tracks C/D (disjoint handler files); the shared
touch point is the `dispatch_cashu` allow-list, edited one arm at a time.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Make the documented track ordering consistent.

  • docs/cashu/03-track-b-release.md#L163-L167: remove or qualify the claim that Track B is parallel with Tracks C/D.
  • docs/cashu/04-track-c-coop-cancel.md#L154-L161: reflect the stated dependency on Track B if it is a merge/runtime dependency.
  • docs/cashu/05-track-d-dispute.md#L176-L186: reflect the stated dependency on Track C if it is required for implementation.
📍 Affects 3 files
  • docs/cashu/03-track-b-release.md#L163-L167 (this comment)
  • docs/cashu/04-track-c-coop-cancel.md#L154-L161
  • docs/cashu/05-track-d-dispute.md#L176-L186
🤖 Prompt for 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.

In `@docs/cashu/03-track-b-release.md` around lines 163 - 167, The documented
track ordering is inconsistent: update docs/cashu/03-track-b-release.md lines
163-167 to remove or qualify Track B’s parallelism with Tracks C/D, update
docs/cashu/04-track-c-coop-cancel.md lines 154-161 to state Track B as a
dependency if it is required for merge or runtime behavior, and update
docs/cashu/05-track-d-dispute.md lines 176-186 to state Track C as a dependency
if required for implementation.

Comment on lines +102 to +109
- Refund value = **`2 * order.fee`** (the whole Mostro fee), sent as a fresh
token to the seller's **trade** pubkey.
- Idempotency: gate the refund on the fee state so a replayed/duplicate cancel
never double-refunds — e.g. only refund when `cashu_fee_token IS NOT NULL` and
the order has not already been refunded (stamp a `cashu_fee_refunded_at`, or
reuse `cashu_fee_redeemed_at` semantics: a redeemed fee is refunded by minting
fresh ecash back, a not-yet-redeemed fee is simply not collected). The exact
bookkeeping is a TC decision, but it **must** be single-shot.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Define one crash-safe, idempotent seller-refund contract.

  • docs/cashu/04-track-c-coop-cancel.md#L102-L109: specify atomic claiming, durable retry/idempotency behavior, and fee-state semantics around minting.
  • docs/cashu/05-track-d-dispute.md#L137-L143: require seller-win disputes to use the same refund helper and idempotency contract, including cancel/dispute terminal-state races.
🧰 Tools
🪛 LanguageTool

[style] ~104-~104: The adverb ‘never’ is usually put before the verb ‘cancel’.
Context: ...n the fee state so a replayed/duplicate cancel never double-refunds — e.g. only refund when ...

(ADVERB_WORD_ORDER)

📍 Affects 2 files
  • docs/cashu/04-track-c-coop-cancel.md#L102-L109 (this comment)
  • docs/cashu/05-track-d-dispute.md#L137-L143
🤖 Prompt for 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.

In `@docs/cashu/04-track-c-coop-cancel.md` around lines 102 - 109, Define one
crash-safe, single-shot seller-refund contract in
docs/cashu/04-track-c-coop-cancel.md lines 102-109: specify atomic refund
claiming, durable retry/idempotency behavior, and explicit fee-state semantics
for minting. In docs/cashu/05-track-d-dispute.md lines 137-143, require
seller-win disputes to call the same refund helper and follow the same contract,
including races between cancel and dispute terminal states.

Comment on lines +132 to +135
### 5B · `admin_take_dispute_action` (Cashu branch)
Assign the solver as today; additionally, **alert the solver** when the escrow's
remaining locktime is below the resolution SLA (§4B) — a late `P_M` signature is
worthless once the seller can reclaim via the refund path. Log + priority-flag.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

Make the near-locktime condition enforceable.

The document states that a late P_M signature is worthless once the seller can reclaim, but the proposed behavior only logs and priority-flags the solver. Define a hard deadline/guard or explicit recovery path; otherwise Mostro can finalize a dispute and deliver an unusable signature.

🤖 Prompt for 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.

In `@docs/cashu/05-track-d-dispute.md` around lines 132 - 135, Make the
near-locktime handling in admin_take_dispute_action enforceable by defining a
hard resolution deadline/guard based on the escrow’s remaining locktime and the
resolution SLA, and prevent finalization that would produce an unusable P_M
signature. Specify the explicit recovery path when the guard is reached, rather
than relying only on logging and priority-flagging.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1867913fc6

ℹ️ 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".

Comment on lines +130 to +132
instead advance `FiatSent → Success`, publish the order event, acknowledge the
seller's release signature to the buyer (relay/ack the P2P DM), and make the
trade rateable (`RateUser` becomes valid). The fee was already collected at lock

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Require a verified release signature before success

In this flow the seller's Cashu signature is only described as a P2P DM, while the daemon advances FiatSent to Success on the seller's Release. If a seller sends Release after FiatSent but never sends a valid NUT-11 signature to the buyer, Mostro would make the trade terminal/rateable even though the buyer cannot redeem and may no longer have a usable dispute path. Please make this transition depend on a daemon-verifiable signature delivery or an explicit buyer/redeem acknowledgement, not solely on the seller's release message.

Useful? React with 👍 / 👎.

Comment on lines +107 to +108
reuse `cashu_fee_redeemed_at` semantics: a redeemed fee is refunded by minting
fresh ecash back, a not-yet-redeemed fee is simply not collected). The exact

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Refund unredeemed fee tokens instead of ignoring them

For the Track A fee model, the fee token is locked 1-of-1 to P_M, so the seller cannot spend it. If cooperative cancel happens before cashu_fee_redeemed_at is stamped, treating the fee as “simply not collected” does not refund the seller; it leaves their fee ecash locked to Mostro's key. The cancel path needs to redeem-and-return fresh ecash, provide a usable P_M signature/refund path, or require the self-service-refund refinement before this no-op is safe.

Useful? React with 👍 / 👎.

Comment on lines +127 to +128
- **Do not** touch LND (there is no hold invoice).
- Advance the order to `Canceled`, publish the order event.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve the cooperative-cancel terminal status

The existing active-order cooperative cancel path persists Status::CooperativelyCanceled in cancel_cooperative_execution_step_2; Status::Canceled is used for other cancel flows. Specifying Canceled for the Cashu branch would make the new branch diverge from the current cooperative-cancel semantics and can break code that distinguishes cooperative completion from ordinary cancellation/admin cancellation. Please keep the internal terminal state aligned with the existing handler and only collapse to a public “canceled” status where the current NIP-33 mapping already does so.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant