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 3 commits 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).
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
WalkthroughAdds the ChangesCashu escrow client
Estimated code review effort: 4 (Complex) | ~60 minutes Possibly related issues
Possibly related PRs
Suggested reviewers: Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 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".
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).
There was a problem hiding this comment.
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
⛔ Files ignored due to path filters (1)
Cargo.lockis excluded by!**/*.lock
📒 Files selected for processing (3)
Cargo.tomlsrc/cashu/mod.rssrc/main.rs
|
@codex review |
There was a problem hiding this comment.
💡 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".
…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.
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).
Summary by CodeRabbit
New Features
Bug Fixes