feat(ecash): adopt the reference NUT-02 resolver + real/test network switch
Demo images / Build & push demo images (push) Failing after 2m26s
Demo images / Build & push demo images (push) Failing after 2m26s
Executes steps 1-3 of docs/cashu-cdk-migration-plan.md, plus the test-coin
switch needed to exercise these routes without spending real sats.
Protocol layer: depend on `cashu` 0.17.5 (MIT, the crate CDK is built on,
default-features off, `wallet` only). Keyset ids now go through upstream's
`Id::from_short_keyset_id` / `ShortKeysetId` instead of the prefix match
hand-rolled in 2277fc46 — same repair, but implemented by the reference
code that defines the rule, so the next spec turn is a version bump rather
than another incident. `MintClient` feeds it the mint's `/v1/keysets` in
upstream's own `KeySetInfo` shape, parsing entries individually so one
keyset in an unmodelled unit can't block resolving the id we need.
Adding the crate required relaxing `bip39 = "=2.1.0"` to `"2.1"` (resolves
2.2.2): the exact pin held `unicode-normalization` at 0.1.22 and no
resolution existed otherwise. The pin carried no recorded rationale; seed
tests cover the bump.
Network switch: `wallet.ecash-network` / `wallet.ecash-set-network`, with a
Test mode toggle in Wallet Settings → Cashu. Cashu has no testnet, so this
points the wallet at the public `testnut` mint — but crucially each network
gets its OWN wallet and accepted-mints file, because test and real proofs
in one purse would be spendable interchangeably and the balance would be a
lie. Mainnet keeps the original filenames, so existing funds files are
untouched and switching is reversible: tests assert a real balance survives
a round trip through test mode.
Headless coverage: scripts/test-ecash-routes.sh drives every ecash RPC over
the real HTTP path (network get/set, balance, history, mint quote + claim,
send, receive, double-redeem refusal, garbage input, melt quote), restores
the node's original network on exit, and exits non-zero with the failure
count.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
03cf74696d
commit
be2cfb8293
@@ -20,6 +20,13 @@
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use bitcoin::secp256k1::PublicKey;
|
||||
// Protocol types from the reference implementation (`cashu`, the crate CDK
|
||||
// itself is built on). Used for the parts of NUT-00/02 that move with the
|
||||
// spec — token parsing and keyset ids — while the structs below stay ours
|
||||
// because they are also the on-disk format (see docs/cashu-cdk-migration-plan.md).
|
||||
use cashu::nuts::nut02::{
|
||||
Id as CdkId, KeySetInfo as CdkKeySetInfo, ShortKeysetId as CdkShortKeysetId,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// Prefix for V3 (JSON) tokens.
|
||||
@@ -222,6 +229,26 @@ impl CashuToken {
|
||||
}
|
||||
}
|
||||
|
||||
/// Resolve a token's (possibly short) keyset id against the mint's keyset
|
||||
/// list, using the reference implementation's NUT-02 rules.
|
||||
///
|
||||
/// A v1 id is complete at 8 bytes; a v2 id is 33 bytes and may legitimately
|
||||
/// travel in a token as a shorter prefix, which only the mint's keyset list
|
||||
/// can expand. Upstream `Id::from_short_keyset_id` implements exactly that,
|
||||
/// including the "8 bytes but `0x01`-versioned" case that a wallet written
|
||||
/// against the old format produces (framework-pt, 2026-08-17).
|
||||
///
|
||||
/// Returns the full hex id to send to the mint, or `None` when the id is
|
||||
/// already complete or cannot be resolved — the caller passes those through
|
||||
/// untouched so the mint's own error still reaches the operator.
|
||||
pub fn resolve_keyset_id(id_hex: &str, mint_keysets: &[CdkKeySetInfo]) -> Option<String> {
|
||||
let bytes = hex::decode(id_hex).ok()?;
|
||||
let short = CdkShortKeysetId::from_bytes(&bytes).ok()?;
|
||||
let full = CdkId::from_short_keyset_id(&short, mint_keysets).ok()?;
|
||||
let full_hex = hex::encode(full.to_bytes());
|
||||
(full_hex != id_hex).then_some(full_hex)
|
||||
}
|
||||
|
||||
/// NUT-02 keyset ID: hex for either 8 bytes (v1, the `00…` short form) or
|
||||
/// 33 bytes (v2, version-byte + hash).
|
||||
///
|
||||
|
||||
Reference in New Issue
Block a user