Skip to content

feat: CashuClient mint library (cdk 0.17.2) — Cashu foundation CF-2#798

Open
grunch wants to merge 3 commits into
mainfrom
feat/cashu-cf2-client
Open

feat: CashuClient mint library (cdk 0.17.2) — Cashu foundation CF-2#798
grunch wants to merge 3 commits into
mainfrom
feat/cashu-cf2-client

Conversation

@grunch

@grunch grunch commented Jul 2, 2026

Copy link
Copy Markdown
Member

Summary

CF-2 of the Cashu foundation (docs/cashu/01-fundamentals.md §6): a self-contained cdk 0.17.2 wrapper in src/cashu/mod.rs. Pure library — registered as a module (pub mod cashu;) but called by nothing; CF-5 does the boot wiring and Track A adds the first caller. Adapted from the reviewed first-attempt module (PR #765), updated to the re-planned spec.

Surface (frozen contract, spec §10)

  • connect(mint_url) — mint reachable, supports NUTs 07 (checkstate), 11 (P2PK), 12 (DLEQ), and exposes ≥ 1 active sat keyset (M-3). Fails fast instead of stranding orders later.
  • verify_2of3_condition(token, p_b, p_s, p_m) — per proof: P2PK only (HTLC rejected), n_sigs = 2 over exactly {P_B, P_S, P_M} (set-based; duplicates faking a 3-key set rejected), and the Track A §4B seller-recovery pathway: locktime tag present, refund = [P_S] exactly, n_sigs_refund = 1 (absent = 1 per NUT-11). This is inverted vs. the first attempt, which rejected locktime/refund — the spec now mandates them (a locktime'd token with no refund is anyone-can-spend after expiry).
  • verify_escrow_conditions(…, min_locktime) — offline half (condition + locktime floor), split out so it's unit-testable without a mint.
  • verify_escrow_token(…, expected_amount, min_locktime) — composes: condition + floor → mint binding → sat unit + exact amount → DLEQ → unspent (fail-closed if checkstate returns fewer states than proofs).
  • verify_token_dleq — NUT-12 mint authentication + per-proof sat keyset check (M-3: tokens may mix keysets, so the connect check alone doesn't cover it).
  • check_state (NUT-07), cashu_pubkey_from_xonly_hex (x-only → compressed even-Y).

Notes for reviewers

  • cdk = { version = "0.17.2", default-features = false, features = ["wallet"] } — wallet-side client only, no mint-server code.
  • Connect-time unit check is "≥ 1 active sat keyset", not "all keysets sat": multi-unit mints are legitimate, and the per-proof keyset check is the security floor keeping foreign-unit tokens out. The spec's wording was ambiguous here — flagging for confirmation (docs can be amended either way).
  • The n_sigs_refund tests forge raw NUT-10 secrets (0 and 2): cdk's own constructor refuses both shapes, but an attacker hands us raw secrets, not constructor output — the daemon-side check is what stands between a forged n_sigs_refund=0 (cdk has a known refund-path zero-sigs bypass hazard) and the escrow.
  • Kept the first attempt's hard-won regression guards: DLEQ-before-unspent rationale (fabricated-token hole), MissingDleqProof pin, odd-Y parity sign/verify roundtrip.
  • TODO(track-b) documented: active-keyset-only DLEQ lookup needs a /v1/keys/{id} fallback before Track B verifies aged tokens.

Test plan

  • cargo fmt --all
  • cargo clippy --all-targets --all-features -- -D warnings
  • cargo test — 515 passed (503 pre-existing unmodified + 12 new, all offline)
  • Mint-backed paths (connect, check_state, verify_token_dleq, full verify_escrow_token) → env-gated CF-3 integration suite (next PR)

Wave-1 PR of the CF-0…CF-5 series (CF-0 #795, CF-1 #796, CF-4 #797). Remaining: CF-3 (mint harness), CF-5 (integration, last).

Summary by CodeRabbit

  • New Features

    • Added Cashu wallet support for validating escrow tokens and mint-backed conditions.
    • Implemented mint URL connection and async mint capability checks before verification.
    • Added escrow condition validation and x-only public key conversion helpers.
  • Bug Fixes

    • Strengthened escrow/token verification against malformed and forged tokens.
    • Added fail-closed mint unspent state checks and rejected mismatched state results.
    • Improved authenticity validation, including per-proof verification, missing DLEQ rejection, and duplicate-proof prevention.

Cashu foundation CF-2 (docs/cashu/01-fundamentals.md, section 6): a
self-contained cdk 0.17.2 wrapper, registered as a module but not wired
into the daemon (CF-5 does the boot wiring). Adapted from the reviewed
first-attempt module (PR #765) with the re-planned spec's changes:

- connect(): mint reachable + NUTs 07/11/12 + at least one active sat
  keyset (M-3) — refuses to boot against an unusable mint.
- verify_2of3_condition(): P2PK-only (HTLC rejected), exactly 2 sigs
  over exactly {P_B, P_S, P_M} (set-based, duplicates rejected), and
  NOW REQUIRES the seller-recovery pathway (Track A 4B): locktime tag
  present, refund = [P_S] exactly, n_sigs_refund = 1.
- verify_escrow_conditions(): offline half (condition + locktime floor
  >= min_locktime), unit-testable without a mint.
- verify_escrow_token(): composes condition + floor + mint binding +
  sat unit + exact amount + DLEQ + unspent (fail-closed on checkstate
  count mismatch).
- verify_token_dleq(): NUT-12 mint authentication + per-proof sat
  keyset check (M-3: tokens may mix keysets).
- cashu_pubkey_from_xonly_hex(): x-only -> compressed (even-Y) with a
  sign/verify roundtrip test for odd-Y source keys.

Tests (12, all offline): every spec-required verify_2of3_condition
case (valid shapes, wrong sig count, missing locktime, missing/wrong/
extra refund key, forged n_sigs_refund 0/2 via raw NUT-10 secrets,
missing/wrong/extra/duplicated pubkey), locktime floor, x-only
conversion x3, odd-Y roundtrip, and the no-DLEQ regression guard.
Mint-backed paths (connect/check_state/verify_token_dleq) are covered
by the env-gated CF-3 integration suite.

515 tests pass (503 pre-existing unmodified + 12 new).
@coderabbitai

coderabbitai Bot commented Jul 2, 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: f6807a20-c88f-4d65-b08b-e01837496a1c

📥 Commits

Reviewing files that changed from the base of the PR and between 6505b1f and 51110bf.

📒 Files selected for processing (1)
  • src/cashu/mod.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/cashu/mod.rs

Walkthrough

Adds the cdk dependency and a new cashu module that implements CashuClient for mint connection checks, escrow-condition validation, token verification, module wiring, and tests.

Changes

Cashu escrow client

Layer / File(s) Summary
Dependency and module wiring
Cargo.toml, src/main.rs, src/cashu/mod.rs
Adds cdk 0.17.2 with wallet support, registers pub mod cashu;, and defines module docs plus the shared Error type.
Mint connection checks
src/cashu/mod.rs
Introduces CashuClient, binds it to a mint URL, and verifies required NUT support plus an active sat keyset.
Escrow condition validation
src/cashu/mod.rs
Implements offline 2-of-3 P2PK escrow validation, duplicate-tag rejection, locktime-floor checks, and x-only pubkey conversion.
Token and mint-state verification
src/cashu/mod.rs
Implements end-to-end escrow token verification with mint binding, amount and unit checks, duplicate-proof rejection, DLEQ verification, and /v1/checkstate validation.
Test coverage
src/cashu/mod.rs
Adds builders and tests for escrow shapes, locktime floors, duplicate proofs, pubkey conversion, parity handling, and missing-DLEQ rejection.

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related issues

Possibly related PRs

  • MostroP2P/mostro#758: Shares the same Cashu wiring effort and overlaps on the cdk dependency in Cargo.toml.
  • MostroP2P/mostro#765: Relies on the same CashuClient escrow verification flow and token checks introduced here.
  • MostroP2P/mostro#794: Matches the same Cashu client connection and validation contract implemented in src/cashu/mod.rs.

Suggested reviewers: AndreaDiazCorreia, Catrya

Poem

I’m a rabbit with a minty grin,
Two-of-three locks keep trouble thin.
DLEQ hums, checkstate glows,
Cashu hops where escrow flows. 🐇

🚥 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 describes the main change: adding a CashuClient mint library for CF-2 with cdk 0.17.2.
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-cf2-client

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: fc8ccbe93a

ℹ️ 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/cashu/mod.rs
Comment thread src/cashu/mod.rs Outdated
Two hardening fixes on the CF-2 escrow-token validator, from PR #798
review:

- verify_2of3_condition never checked conditions.sig_flag, so a
  SIG_ALL escrow was accepted. The documented release/refund flow
  signs inputs only (SIG_INPUTS) — the seller signs once and the buyer
  chooses their own swap outputs at redeem time — and cannot redeem a
  SIG_ALL token, which would strand the locked order. Reject any escrow
  whose sig flag is not SIG_INPUTS.

- cdk's Conditions parser silently keeps only the first occurrence of
  each standard NUT-11 tag and drops duplicates, so a forged secret
  with a second refund/locktime/n_sigs/sigflag/... tag passed every
  check while a non-cdk mint could settle it under different spend
  conditions than the daemon verified. NUT-11 marks duplicate tags
  malformed; inspect the raw NUT-10 tag list and reject duplicates
  before the lossy conversion.

Adds rejects_sig_all_escrow_token and rejects_duplicate_standard_tags
tests (via a new raw_secret_token forge helper, also reused by the
n_sigs_refund test).

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

🤖 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 `@src/cashu/mod.rs`:
- Around line 88-113: Add a bounded timeout to mint discovery in the cashu
client initialization path, since `HttpClient::new(url, None)` leaves
`get_mint_info()` and `get_mint_keysets()` unbounded and they can hang. Update
the logic around the `cashu_client.client.get_mint_info()` and
`cashu_client.client.get_mint_keysets()` awaits to use `tokio::time::timeout`,
or otherwise configure a finite client timeout, and map timeout failures into
the existing `Error::MintConnection` flow.
🪄 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: 0c314e21-5c9d-433e-ac17-c57623c5452f

📥 Commits

Reviewing files that changed from the base of the PR and between 53e0086 and 6505b1f.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (3)
  • Cargo.toml
  • src/cashu/mod.rs
  • src/main.rs

Comment thread src/cashu/mod.rs Outdated
@grunch

grunch commented Jul 7, 2026

Copy link
Copy Markdown
Member Author

@codex review

@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: 6505b1fae6

ℹ️ 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/cashu/mod.rs
…meout

Two more hardening fixes from PR #798 review:

- verify_escrow_token trusted token.value() over proofs that could
  repeat. A duplicated proof is counted toward the escrow amount and
  reported Unspent once per copy by NUT-07 checkstate (answers in
  request order), so a token backed by a single 50-sat proof repeated
  twice could satisfy a 100-sat escrow even though the mint spends that
  proof only once. Add reject_duplicate_proofs (dedup by secret, which
  fixes the checkstate Y) and call it before the amount/unspent checks.
  (codex P1)

- HttpClient wraps a reqwest client with no default timeout, so
  get_mint_info/get_mint_keysets (connect), post_check_state and
  get_mint_keys (DLEQ) could hang on a slow or unreachable mint. Bound
  every mint HTTP call with a 10s tokio::time::timeout mapped into
  Error::MintConnection. (CodeRabbit)

Adds rejects_duplicate_proofs_in_token test.
@grunch grunch requested a review from arkanoider July 7, 2026 15:11

@Catrya Catrya left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

tACK

@AndreaDiazCorreia AndreaDiazCorreia left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

tACK

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.

3 participants