a624d11b6a803fbd83ac1782369d3766d20006a3
3
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
ee40880ce5 |
feat(ecash): import a backup phrase from another NUT-13 wallet
Demo images / Build & push demo images (push) Failing after 2m2s
Bring-your-own, the open question the migration plan left. Point this wallet at a phrase you already hold — Minibits, Nutstash, cdk-cli — and its coins become restorable here, which is the other half of "these words are portable". Replacing an established phrase is the one genuinely lossy thing this module can do, so it is treated that way. The coins already held stay spendable: they are proofs, not derivations, and nothing here touches `ecash.json`. But they were minted under the *old* phrase, so a restore will no longer find them. Hence an explicit confirm, a prompt to reveal and write down the current phrase first, and — most importantly — the replaced phrase is archived beside the wallet, never overwritten. It may be the last copy of the words a balance was minted under, and quietly destroying that is precisely what this module exists to prevent. Re-importing the phrase already in use is a no-op rather than a replacement, so it archives nothing. Counters are deliberately left alone. They are per-keyset and seed-relative, so under a new seed they merely start high, which costs nothing because a restore scans from zero regardless. Resetting them would be the dangerous choice on the day someone imports the phrase they were already using. `imported` is its own provenance rather than reusing `independent`: both mean the node's recovery phrase does not cover the wallet, but only one of them means the operator already knows where else the words live. 15 NUT-13 tests green, 1000 frontend tests green. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
e30516316b |
fix(ecash): give every node a backup phrase, and prove restore works
Demo images / Build & push demo images (push) Failing after 2m11s
Running the route suite on this box surfaced that the backup was unreachable here: `identity/master_seed.enc` is written during onboarding, and any node onboarded before that step existed simply does not have one. Reveal bailed with "this node has no encrypted seed backup", and restore followed it down. But the choice on such a node was never "derived phrase or independent phrase" — it was "independent phrase or no backup at all", and a wallet whose coins can be restored from words the operator holds beats one whose coins die with a single file. So it now generates one, recorded as `independent`, and every surface that shows it says plainly that restoring the node will not bring the ecash back — only these words will. `derivable_from_node_seed` lets the card say which kind you are about to get *before* you write anything down. Also: a mint that never implemented NUT-09 answered restore with a bare 404, which surfaced as "mint returned 404 with no further detail" — true, and useless to someone trying to get their coins back. It now names the limitation. The route suite was reading `result.amount_sats` from mint-claim, which answers with `minted_sats`. A working claim had been reporting as a failure; that was one of the two reds carried over from yesterday. The real gap, though, was that "recovered 0 sats" passes on a wallet with nothing to find — exactly the shape of a backup that looks fine until the day you need it. test-ecash-restore.sh does the test that settles it: mint, **delete the wallet file**, restore, check the coins came back. On this box: 87 sats before the wipe, 0 after, 61 recovered from the phrase alone — every coin minted since the phrase existed, and none of the 26 sats minted before it, which used random secrets and never could come back. Testnet only, and it refuses to run otherwise. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
59fffc809f |
feat(ecash): the wallet can now be restored from a phrase (NUT-13)
Demo images / Build & push demo images (push) Failing after 2m15s
Until now every Cashu proof this node held was backed by a secret drawn from OsRng and written to exactly one file. Losing wallet/ecash.json lost the coins outright — no phrase to write down, and nothing the mint could do about it. Ecash is a bearer instrument, so "one file, no backup" was the sharpest edge in the wallet. NUT-13 derives each proof's secret and blinding factor from (seed, keyset id, counter) instead. The wallet becomes a phrase, and the coins can be re-derived and re-claimed — here or in any other NUT-13 wallet. The phrase is its own 24 words, derived from the node master seed over a fixed HKDF path. Both halves matter: it is still covered by the node's recovery phrase, so there is nothing extra to write down; but it is portable, so restoring ecash into Minibits or cdk-cli does not mean handing over the key to the entire node. It sits on disk unencrypted, deliberately. The master seed needs the operator's password to open, which no background mint or swap can ask for; and this file lives beside wallet/ecash.json, which already holds spendable bearer secrets in plaintext. It regenerates exactly those secrets, so it is the same sensitivity class as the file next to it. 0600, like identity/nostr_secret, which is derived and persisted the same way. Counters are reserved *before* the mint call and never rolled back. A gap costs a restore scan a few extra probes; a reused counter costs a coin, because two proofs with the same secret can only be spent once. Restore is the half that cannot be done offline: a re-derived secret is not money until the mint's signature over it exists. /v1/restore returns those signatures; unblinding reconstitutes the proofs. It is additive and idempotent — coins already held are skipped by secret, spent ones are counted but not added — so it is safe to press on a working wallet, which is when someone is most likely to reach for it. Existing nodes activate on the first visit to Settings → Ecash backup phrase: that password prompt is the only moment the master seed can legitimately be opened. New nodes get it at onboarding. Until then the behaviour is exactly as before — valid proofs, no backup — and the card says so rather than implying a backup already exists. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |