feat: CashuClient mint library (cdk 0.17.2) — Cashu foundation CF-2#798
feat: CashuClient mint library (cdk 0.17.2) — Cashu foundation CF-2#798grunch wants to merge 1 commit into
Conversation
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).
|
Warning Review limit reached
Next review available in: 38 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 Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (3)
✨ 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: 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".
| let conditions = conditions | ||
| .ok_or_else(|| Error::Condition("P2PK condition carries no NUT-11 tags".into()))?; |
There was a problem hiding this comment.
Reject non-SIG_INPUTS escrow tokens
When the seller includes sigflag = SIG_ALL, this parser still accepts the token because the parsed conditions.sig_flag is never checked. The Cashu architecture docs explicitly base the happy path and fee policy on SIG_INPUTS so the seller can sign once and the buyer can later choose their own swap outputs; accepting a SIG_ALL escrow can lock an order that the documented release flow cannot redeem without a different pre-constructed-output signing round. Please reject any parsed condition whose sig flag is not SigInputs during lock validation.
Useful? React with 👍 / 👎.
| let spending_conditions = SpendingConditions::try_from(secret) | ||
| .map_err(|e| Error::Condition(e.to_string()))?; |
There was a problem hiding this comment.
Reject duplicate NUT-11 tags before parsing
SpendingConditions::try_from in the pinned CDK parser collapses duplicate standard tags to the first value, so checks below can pass for a raw secret that still contains a second refund, locktime, n_sigs, or sigflag tag. NUT-11 marks duplicate tags malformed, and with a non-CDK configured mint that rejects or interprets duplicates differently, Mostro can accept and persist an escrow token that later cannot be settled or has different spend conditions than the daemon verified. Please inspect the raw NUT-10 tag list and reject duplicates before converting it to Conditions.
Useful? React with 👍 / 👎.
Summary
CF-2 of the Cashu foundation (
docs/cashu/01-fundamentals.md§6): a self-containedcdk 0.17.2wrapper insrc/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 activesatkeyset (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 = 2over exactly{P_B, P_S, P_M}(set-based; duplicates faking a 3-key set rejected), and the Track A §4B seller-recovery pathway:locktimetag 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 →satunit + exact amount → DLEQ → unspent (fail-closed if checkstate returns fewer states than proofs).verify_token_dleq— NUT-12 mint authentication + per-proofsatkeyset 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.n_sigs_refundtests 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 forgedn_sigs_refund=0(cdk has a known refund-path zero-sigs bypass hazard) and the escrow.MissingDleqProofpin, 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 --allcargo clippy --all-targets --all-features -- -D warningscargo test— 515 passed (503 pre-existing unmodified + 12 new, all offline)connect,check_state,verify_token_dleq, fullverify_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).