NUT-20: add deterministic NUT-20 quote locking derivation#373
Conversation
|
As per my comment on the related deterministic P2PK key spec ( #331 ), unless there is an overwhelmingly strong reason to use BIP32, then a HMAC-SHA256 KDF is much faster, unless BIP-32 is carefully handled. |
|
I have proposed a HMAC-SHA256 KDF derived alternative, which would close this PR |
Iirc this has been discussed in the past in Leitos original PR for P2PK locking keys. Using BIP32 might be a tad slower, but it leaves the possibility to build something with the extended public keys. Imagine a watch-only wallet for mint quotes |
Yeah, am not convinced it would not be a huge linkability issue compromising privacy. I mentioned it in my replacement pr |
This is both a bug and a feature. I think we have general consensus about a quote-lookup-by-pubkey API, so this would literally allow watch-only / notification / aggregation services. |
Yeah, unfortunately that was kind of a premature shipping before spec sign off. We moved away from bip32 for keysets v2+. Baking that dep into future stuff means apps will have to carry it a long time more. The performance gain is only marginal if you are batch deriving. If you have to derive sporadically for any reason performance drops off a cliff |
robwoodgate
left a comment
There was a problem hiding this comment.
Following offline discussion, and the fact this is already live, I've closed #384.
|
Cashu-TS support now added: |
…697) # NUTS: - cashubtc/nuts#331 - cashubtc/nuts#373 ## Summary Adds BIP-32 derivation of deterministic signing keys under `m/129373'/{purpose}'/0'/0'/{counter}` (non-hardened counter), as defined for: - [NUT-11](https://github.com/cashubtc/nuts/blob/main/11.md): P2PK keys (purpose index `10`) - [NUT-20](https://github.com/cashubtc/nuts/blob/main/20.md): quote locking keys (purpose index `20`) ## API ```ts export type Bip32KeyPurpose = 'P2PK' | 'QuoteLock'; deriveKeyPair(seed, purpose, counter): { pubkey: string; privkey: string } // one-shot createKeyPairDeriver(seed, purpose): (counter) => { pubkey: string; privkey: string } // cached, for loops ``` `purpose` is passed by name (`'P2PK'` / `'QuoteLock'`). Both return hex `pubkey`/`privkey` that drop straight into the existing lock/quote and `signP2PKProofs` APIs with zero conversion: ```ts const { pubkey, privkey } = deriveKeyPair(seed, 'P2PK', counter); await wallet.send(64, proofs, undefined, { send: { type: 'p2pk', options: { pubkey } } }); await wallet.send(64, lockedProofs, { privkey }); const { pubkey: qp, privkey: qk } = deriveKeyPair(seed, 'QuoteLock', counter); const quote = await wallet.createLockedMintQuote(amount, qp); await wallet.mintProofs('bolt11', amount, quote, { privkey: qk }); ``` `createKeyPairDeriver` caches the shared parent derivation, so restore scans cost a single non-hardened child derivation per counter (~5x faster than re-walking the full path) while still returning the ready-to-use hex keypair: ```ts const derive = createKeyPairDeriver(seed, 'P2PK'); for (let counter = 0; counter < gapLimit; counter++) { if (derive(counter).pubkey === targetPubkey) break; // matched } ``` ## Scope Crypto primitive only. Counter allocation and the quote/proof-to-counter mapping stay with the consumer; they need that mapping regardless, so a wallet-owned counter would add coupling without removing consumer state. No NUT-20 module changes needed: `createLockedMintQuote`/locked mint already accept the pubkey/privkey directly.
Implementations
Summary
Adds deterministic wallet seed derivation guidance for NUT-20 quote locking keys using the Cashu SLIP-0044 coin type and a NUT-20-specific BIP32 path.
Updates the NUT-20 test vectors with the first five derived compressed secp256k1 public keys for the documented mnemonic.
Validation
Egge21M:nut-20-lockingagainstcashubtc/nuts:mainwith the GitHub connector: 1 commit ahead, 0 behind20.mdandtests/20-test.md