Skip to content

feat: add optional fee_token to CashuLockProof#157

Merged
grunch merged 1 commit into
mainfrom
feat/cashu-fee-token
Jun 26, 2026
Merged

feat: add optional fee_token to CashuLockProof#157
grunch merged 1 commit into
mainfrom
feat/cashu-fee-token

Conversation

@grunch

@grunch grunch commented Jun 26, 2026

Copy link
Copy Markdown
Member

What

Adds an optional fee_token field to CashuLockProof.

In the Cashu 2-of-3 escrow flow, Mostro is removed from the money path, so it
cannot skim its fee from the escrow token the way it does with Lightning hold
invoices. The agreed model (Track A spec, "funder-pays-at-lock") has the seller
fund the Mostro fee as a separate Cashu token locked 1-of-1 to P_M,
submitted alongside the 2-of-3 escrow token in the same AddCashuEscrow
message. This field carries that token.

Changes (src/message.rs)

  • CashuLockProof.fee_token: Option<String> — the serialized fee token.
    None when the node charges no fee.
    • #[serde(default, skip_serializing_if = "Option::is_none")] so the wire
      form is backward- and forward-compatible: an old client's proof (no
      fee_token key) deserializes to None, and a fee-less proof serializes
      byte-identically to before (key omitted, not null).
  • new() unchanged — still 5 args, now sets fee_token: None; no existing
    caller breaks.
  • with_fee_token(self, String) -> Self — immutable builder to attach the
    token opt-in.
  • verify() intentionally untouched — it is a protocol-shape check with no
    access to Settings, so it cannot know whether the node charges a fee.
    Presence/amount of fee_token is validated daemon-side (config-dependent).

Tests

  • test_cashu_lock_proof_fee_token_round_tripwith_fee_token → JSON
    contains fee_token → round-trips preserving Some.
  • test_cashu_lock_proof_without_fee_token_is_omitted_and_defaults_to_none
    fee-less proof omits the key and parses back to None.
  • Existing CashuLockProof / verify() tests pass unmodified.

cargo fmt --check, clippy --all-targets --all-features -D warnings, and the
full test suite (83 lib + 4 doc-tests) are green.

Notes

  • No version bump — left for the maintainer's cargo release flow.
  • Additive and backward-compatible; the daemon-side handler and the wider Cashu
    escrow spec live in the mostro repo (docs/cashu/02-track-a-lock.md).

Summary by CodeRabbit

  • New Features

    • Added support for attaching an optional fee token to lock proofs.
    • Fee token data now persists through save/load flows when present.
    • Empty fee token values are omitted automatically, keeping the output cleaner.
  • Tests

    • Added coverage for round-trip behavior with and without a fee token.

Carries the seller-funded Mostro fee token (locked to P_M) alongside the
2-of-3 escrow token, for the Cashu escrow funder-pays-at-lock fee model.

The field is Option<String>, defaults to None, and is omitted from the wire
form when absent (serde default + skip_serializing_if), so proofs without a
fee serialize exactly as before. new() is unchanged (sets None); a
with_fee_token builder attaches it. verify() is intentionally untouched —
fee presence/amount is a daemon-side, config-dependent check.
@coderabbitai

coderabbitai Bot commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: d7483955-8b15-47c9-85fe-6dfe2e79fa47

📥 Commits

Reviewing files that changed from the base of the PR and between c041891 and fb02078.

📒 Files selected for processing (1)
  • src/message.rs

Walkthrough

CashuLockProof now serializes an optional fee_token field, initializes it to None, provides a setter-style builder method, and includes tests for JSON round-trip behavior when the field is present or absent.

Changes

CashuLockProof fee token support

Layer / File(s) Summary
Field, builder, and serialization
src/message.rs
CashuLockProof adds an optional fee_token field, initializes it in new(...), exposes with_fee_token(...), and verifies JSON round-trips for both Some and None values.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related PRs

  • MostroP2P/mostro-core#150: Updates the same CashuLockProof message surface with an optional fee_token field and matching JSON tests.

Poem

A rabbit hopped through JSON light,
With fee tokens tucked in just right.
Some or None, both sound and neat,
The proof now lands with softer feet.
🐇

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: adding an optional fee_token to CashuLockProof.
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.
✨ 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 feat/cashu-fee-token

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.

@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: fb02078b5d

ℹ️ 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 thread src/message.rs
/// fee. Omitted from the wire form when absent, so proofs without a fee
/// serialize exactly as before.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub fee_token: Option<String>,

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 struct-literal compatibility

Because CashuLockProof is a public struct re-exported through the crate prelude, adding this public field is not just a wire-compatible extension: any downstream crate that currently constructs CashuLockProof { token, mint_url, buyer_pubkey, seller_pubkey, mostro_pubkey } with a struct literal will fail to compile with a missing fee_token field, despite new() staying unchanged. If this is meant to be an additive patch-compatible change, avoid changing the public struct shape or coordinate it with a breaking release.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Good catch, and correct: adding a public field to CashuLockProof (re-exported in the prelude) is source-breaking for struct-literal construction, even though new() and the wire format stay compatible.

This is intentional and coordinated with the release, not a patch-compatible change: it will ship as a breaking 0.14.0 minor (pre-1.0, so the minor bump is the breaking-allowed one), never as 0.13.x. In practice nothing in the org breaks today — the struct is only built via new() here, and the daemon does not construct it yet — but you are right that external struct-literal users would need the new field, which the 0.14.0 bump accounts for.

If we want future field additions to be non-breaking we could mark the struct #[non_exhaustive], but that is itself breaking and would land in the same 0.14.0; leaving it out for now while the crate is young.

@grunch grunch merged commit 439ca12 into main Jun 26, 2026
11 checks passed
@grunch grunch deleted the feat/cashu-fee-token branch June 26, 2026 16:05
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