diff --git a/20.md b/20.md index 4bf1b598..1d01739d 100644 --- a/20.md +++ b/20.md @@ -39,6 +39,26 @@ with the requested `amount`,`unit`, and `description` according to [NUT-04][04]. > > **Privacy:** To prevent the mint from being able to link multiple mint quotes, wallets **SHOULD** generate a unique public key for each mint quote request. +### Deterministic quote locking key derivation + +Wallets can deterministically generate private keys for NUT-20 quote locking from their wallet seed. + +The following BIP32 derivation path is used to derive the private key: + +``` +m/129373'/20'/0'/0'/{counter} +``` + +Where: + +- `129373'` is the Cashu wallet derivation namespace. +- `20'` is the wallet derivation index for NUT-20 quote locking keys. +- `{counter}` is an incrementing, non-hardened BIP32 child index. + +The wallet **SHOULD** use a new counter value for each mint quote request. The NUT-20 quote locking counter is independent from NUT-13 keyset counters and other wallet derivation counters. + +The wallet uses the compressed secp256k1 public key derived from the private key as the quote `pubkey`. This allows a wallet to restore the private key needed to sign for unpaid or paid NUT-20 locked mint quotes during a recovery process. + The mint `Bob` then responds with a `PostMintQuoteBolt11Response`: ```json diff --git a/tests/20-test.md b/tests/20-test.md index e534f948..f3344b55 100644 --- a/tests/20-test.md +++ b/tests/20-test.md @@ -1,5 +1,27 @@ # NUT-20 Test Vectors +## Deterministic quote locking key derivation + +Using [NUT-20](../20.md) quote locking key derivation, we derive values starting from the following BIP39 mnemonic: + +```json +{ + "mnemonic": "half depart obvious quality work element tank gorilla view sugar picture humble" +} +``` + +The public keys derived for the first five counters from `counter=0` to `counter=4` are: + +```json +{ + "m/129373'/20'/0'/0'/0": "03062837166e56114b59a4d1fd3a5a812bf7aadc1dde758428cf943d80acd41539", + "m/129373'/20'/0'/0'/1": "02b47d9d41725f5ce6f08c874835cef25376cb1e95f6cb073fef52ca8fd986cf15", + "m/129373'/20'/0'/0'/2": "029acbd3a46fd75bc05ba0226d0b4d909b2fb6e96c80544a094a1a3567737e44d3", + "m/129373'/20'/0'/0'/3": "0373e4a42fbe0a4e18aadb57cf500b655f2446b4071ee579121d2ed8905bcc49c2", + "m/129373'/20'/0'/0'/4": "02b8709bfce17c10f1864f5218844533ae60930d52089669b317d8b5f474eec071" +} +``` + The following is a `PostMintBolt11Request` with a valid signature. Where the `pubkey` in the `PostMintQuoteBolt11Request` is `03d56ce4e446a85bbdaa547b4ec2b073d40ff802831352b8272b7dd7a4de5a7cac`. ```json