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
@@ -36,6 +36,51 @@ impl RpcHandler {
|
||||
}))
|
||||
}
|
||||
|
||||
/// `wallet.ecash-network` — which ecash network this node is on, and the
|
||||
/// balance sitting in the *other* one so the UI can say what switching
|
||||
/// would reveal rather than appearing to lose money.
|
||||
pub(super) async fn handle_wallet_ecash_network(&self) -> Result<serde_json::Value> {
|
||||
let current = ecash::load_network(&self.config.data_dir).await;
|
||||
let wallet = ecash::load_wallet(&self.config.data_dir).await?;
|
||||
Ok(serde_json::json!({
|
||||
"network": current,
|
||||
"is_test": current.is_test(),
|
||||
"mint_url": wallet.mint_url,
|
||||
"balance_sats": wallet.balance(),
|
||||
}))
|
||||
}
|
||||
|
||||
/// `wallet.ecash-set-network` — switch between real and test ecash.
|
||||
///
|
||||
/// Each network keeps its own wallet file, so this never moves, merges or
|
||||
/// deletes coins: switching away parks the current balance and switching
|
||||
/// back finds it exactly as it was.
|
||||
pub(super) async fn handle_wallet_ecash_set_network(
|
||||
&self,
|
||||
params: Option<serde_json::Value>,
|
||||
) -> Result<serde_json::Value> {
|
||||
let params = params.ok_or_else(|| anyhow::anyhow!("Missing params"))?;
|
||||
let requested = params
|
||||
.get("network")
|
||||
.and_then(|v| v.as_str())
|
||||
.ok_or_else(|| anyhow::anyhow!("Missing network ('mainnet' or 'testnet')"))?;
|
||||
let network = match requested {
|
||||
"mainnet" => ecash::EcashNetwork::Mainnet,
|
||||
"testnet" => ecash::EcashNetwork::Testnet,
|
||||
other => anyhow::bail!("Unknown ecash network '{other}' — use 'mainnet' or 'testnet'"),
|
||||
};
|
||||
|
||||
ecash::save_network(&self.config.data_dir, network).await?;
|
||||
let wallet = ecash::load_wallet(&self.config.data_dir).await?;
|
||||
tracing::info!(?network, "ecash network switched by operator");
|
||||
Ok(serde_json::json!({
|
||||
"network": network,
|
||||
"is_test": network.is_test(),
|
||||
"mint_url": wallet.mint_url,
|
||||
"balance_sats": wallet.balance(),
|
||||
}))
|
||||
}
|
||||
|
||||
pub(super) async fn handle_wallet_ecash_mint(
|
||||
&self,
|
||||
params: Option<serde_json::Value>,
|
||||
|
||||
Reference in New Issue
Block a user