-
Notifications
You must be signed in to change notification settings - Fork 54
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: nut19 signature on mint request
- Loading branch information
1 parent
96891ac
commit a4349b5
Showing
3 changed files
with
264 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,182 @@ | ||
# NUT-19: Signature on Mint Quote | ||
|
||
`optional` | ||
|
||
This NUT defines a protocol extension that enables signature-based authentication for mint quote redemption. When requesting a mint quote, clients can provide a public key. The mint will then require a valid signature from the corresponding secret key before processing the mint. | ||
Caution: If the mint does not support this NUT, anyone with the mint quote id will be able to mint even without providing a signature. | ||
|
||
# Mint quote | ||
|
||
To request a mint quote, the wallet of `Alice` makes a `POST /v1/mint/quote/{method}` request where `method` is the payment method requested (here `bolt11`). | ||
|
||
```http | ||
POST https://mint.host:3338/v1/mint/quote/bolt11 | ||
``` | ||
|
||
The wallet of `Alice` includes the following `PostMintQuoteBolt11Request` data in its request: | ||
|
||
```json | ||
{ | ||
"amount": <int>, | ||
"unit": <str_enum["sat"]>, | ||
"description": <str|null>, | ||
"pubkey": <str|null> <-- New | ||
} | ||
``` | ||
|
||
with the requested `amount` and the `unit`. An optional `description` can be passed if the mint signals support for it in `MintMethodSetting`. `pubkey` is the public key that will be required for signature verification during the minting process. The mint will only mint ecash after receiving a valid signature from the corresponding private key in the `PostMintRequest`. | ||
|
||
> **Privacy:** To prevent linking multiple mint quotes together, wallets **SHOULD** generate a unique public key for each mint quote request | ||
The mint `Bob` then responds with a `PostMintQuoteBolt11Response`: | ||
|
||
```json | ||
{ | ||
"quote": <str>, | ||
"request": <str>, | ||
"state": <str_enum[STATE]>, | ||
"expiry": <int>, | ||
"pubkey": <str|null> <-- New | ||
} | ||
``` | ||
|
||
Where `quote` is the quote ID and `request` is the payment request to fulfill. `expiry` is the Unix timestamp until which the mint quote is valid. | ||
`state` is an enum string field with possible values `"UNPAID"`, `"PAID"`, `"ISSUED"`: | ||
|
||
- `"UNPAID"` means that the quote's request has not been paid yet. | ||
- `"PAID"` means that the request has been paid. | ||
- `"ISSUED"` means that the quote has already been issued. | ||
|
||
## Example | ||
|
||
Request of `Alice` with curl: | ||
|
||
```bash | ||
curl -X POST http://localhost:3338/v1/mint/quote/bolt11 -d '{"amount": 10, "unit": "sat", "pubkey": "03d56ce4e446a85bbdaa547b4ec2b073d40ff802831352b8272b7dd7a4de5a7cac"}' -H "Content-Type: application/json" | ||
``` | ||
|
||
Response of `Bob`: | ||
|
||
```json | ||
{ | ||
"quote": "9d745270-1405-46de-b5c5-e2762b4f5e00", | ||
"request": "lnbc100n1pj4apw9...", | ||
"state": "UNPAID", | ||
"expiry": 1701704757, | ||
"pubkey": "03d56ce4e446a85bbdaa547b4ec2b073d40ff802831352b8272b7dd7a4de5a7cac" | ||
} | ||
``` | ||
|
||
#### Message aggregation | ||
|
||
To provide a signature for a mint request, the owner of one of the signing public keys must concatenate the `PostMintQuoteBolt11Response.quote` and the `B_` fields of all `BlindedMessages` (outputs, see [NUT-00][00]) to a single message string in the order they appear in the `PostMintRequest`. This string concatenated is then hashed and signed (see [Signature scheme](#signature-scheme)). | ||
|
||
If a request has `n` outputs the message to sign becomes: | ||
|
||
``` | ||
msg = quote_id || B_0 || ... || B_n | ||
``` | ||
|
||
Where || denotes concatenation, `quote_id` is a UTF-8 string, and each `B_` is a hex string, with all components encoded to UTF-8 bytes | ||
|
||
#### Signature scheme | ||
|
||
To mint a quote where a public key was provided, the minter needs to include signatures in the `PostMintBolt11Request`. We use a [BIP340](https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki) signature on the SHA-256 hash of the message to sign as defined above. | ||
|
||
# Minting tokens | ||
|
||
After requesting a mint quote and paying the request, the wallet proceeds with minting new tokens by calling the `POST /v1/mint/{method}` endpoint where `method` is the payment method requested (here `bolt11`). | ||
|
||
```http | ||
POST https://mint.host:3338/v1/mint/bolt11 | ||
``` | ||
|
||
The wallet `Alice` includes the following `PostMintBolt11Request` data in its request | ||
|
||
```json | ||
{ | ||
"quote": <str>, | ||
"outputs": <Array[BlindedMessage]>, | ||
"witness": <str|null> <-- New | ||
} | ||
``` | ||
|
||
with the `quote` being the quote ID from the previous step and `outputs` being `BlindedMessages` (see [NUT-00][00]) that the wallet requests signatures on whose sum is `amount` as requested in the quote. `witness` is the signature on the mint quote id as defined above. | ||
The mint `Bob` then responds with a `PostMintBolt11Response`: | ||
|
||
```json | ||
{ | ||
"signatures": <Array[BlindSignature]> | ||
} | ||
``` | ||
|
||
where `signatures` is an array of blind signatures on the outputs. | ||
|
||
## Example | ||
|
||
Request of `Alice` with curl: | ||
|
||
```bash | ||
curl -X POST https://mint.host:3338/v1/mint/bolt11 -H "Content-Type: application/json" -d \ | ||
'{ | ||
"quote": "9d745270-1405-46de-b5c5-e2762b4f5e00", | ||
"outputs": [ | ||
{ | ||
"amount": 8, | ||
"id": "009a1f293253e41e", | ||
"B_": "035015e6d7ade60ba8426cefaf1832bbd27257636e44a76b922d78e79b47cb689d" | ||
}, | ||
{ | ||
"amount": 2, | ||
"id": "009a1f293253e41e", | ||
"B_": "0288d7649652d0a83fc9c966c969fb217f15904431e61a44b14999fabc1b5d9ac6" | ||
} | ||
], | ||
"witness": "d9be080b33179387e504bb6991ea41ae0dd715e28b01ce9f63d57198a095bccc776874914288e6989e97ac9d255ac667c205fa8d90a211184b417b4ffdd24092" | ||
}' | ||
``` | ||
|
||
Response of `Bob`: | ||
|
||
```json | ||
{ | ||
"signatures": [ | ||
{ | ||
"id": "009a1f293253e41e", | ||
"amount": 2, | ||
"C_": "0224f1c4c564230ad3d96c5033efdc425582397a5a7691d600202732edc6d4b1ec" | ||
}, | ||
{ | ||
"id": "009a1f293253e41e", | ||
"amount": 8, | ||
"C_": "0277d1de806ed177007e5b94a8139343b6382e472c752a74e99949d511f7194f6c" | ||
} | ||
] | ||
} | ||
``` | ||
|
||
If the invoice was not paid yet, `Bob` responds with an error. In that case, `Alice` **MAY** repeat the same request until the Lightning invoice is settled, as in NUT04. If `Alice` does not include a witness on the `PostMintBolt11Request` but did include a `pubkey` in the `PostMintBolt11QuoteRequest` then `Bob` **MUST** respond with an error, `Alice` **SHOULD** repeat the request with a witness in order to mint the ecash. | ||
|
||
## Settings | ||
|
||
The settings for this NUT indicate the support for requiring a signature before minting. They are part of the info response of the mint ([NUT-06][06]) which in this case reads | ||
|
||
```json | ||
{ | ||
"19": { | ||
"supported": <bool>, | ||
"required": <bool> | ||
} | ||
} | ||
``` | ||
|
||
In addition to signaling optional support, the mint can require a public key for mint quotes and a corresponding witness during minting. If `required` is set to `true` in the NUT-19 settings: | ||
|
||
1. The wallet **MUST** provide a public key when requesting a mint quote | ||
2. The wallet **MUST** provide a valid witness signature when submitting the mint request | ||
|
||
> **Note:** Enabling this setting makes the mint incompatible with wallets that do not support NUT-19 | ||
[00]: 00.md | ||
[06]: 06.md |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
# NUT-19 Test Vectors | ||
|
||
The following is a `PostMintBolt11Request` with a valid signature. Where the `pubkey` in the `PostMintQuoteBolt11Request` is `03d56ce4e446a85bbdaa547b4ec2b073d40ff802831352b8272b7dd7a4de5a7cac`. | ||
|
||
```json | ||
{ | ||
"quote": "9d745270-1405-46de-b5c5-e2762b4f5e00", | ||
"outputs": [ | ||
{ | ||
"amount": 1, | ||
"id": "00456a94ab4e1c46", | ||
"B_": "0342e5bcc77f5b2a3c2afb40bb591a1e27da83cddc968abdc0ec4904201a201834" | ||
}, | ||
{ | ||
"amount": 1, | ||
"id": "00456a94ab4e1c46", | ||
"B_": "032fd3c4dc49a2844a89998d5e9d5b0f0b00dde9310063acb8a92e2fdafa4126d4" | ||
}, | ||
{ | ||
"amount": 1, | ||
"id": "00456a94ab4e1c46", | ||
"B_": "033b6fde50b6a0dfe61ad148fff167ad9cf8308ded5f6f6b2fe000a036c464c311" | ||
}, | ||
{ | ||
"amount": 1, | ||
"id": "00456a94ab4e1c46", | ||
"B_": "02be5a55f03e5c0aaea77595d574bce92c6d57a2a0fb2b5955c0b87e4520e06b53" | ||
}, | ||
{ | ||
"amount": 1, | ||
"id": "00456a94ab4e1c46", | ||
"B_": "02209fc2873f28521cbdde7f7b3bb1521002463f5979686fd156f23fe6a8aa2b79" | ||
} | ||
], | ||
"witness": "d4b386f21f7aa7172f0994ee6e4dd966539484247ea71c99b81b8e09b1bb2acbc0026a43c221fd773471dc30d6a32b04692e6837ddaccf0830a63128308e4ee0" | ||
} | ||
``` | ||
|
||
The following is the expected message to sign on the above `PostMintBolt11Request`. | ||
|
||
``` | ||
[57, 100, 55, 52, 53, 50, 55, 48, 45, 49, 52, 48, 53, 45, 52, 54, 100, 101, 45, 98, 53, 99, 53, 45, 101, 50, 55, 54, 50, 98, 52, 102, 53, 101, 48, 48, 48, 51, 52, 50, 101, 53, 98, 99, 99, 55, 55, 102, 53, 98, 50, 97, 51, 99, 50, 97, 102, 98, 52, 48, 98, 98, 53, 57, 49, 97, 49, 101, 50, 55, 100, 97, 56, 51, 99, 100, 100, 99, 57, 54, 56, 97, 98, 100, 99, 48, 101, 99, 52, 57, 48, 52, 50, 48, 49, 97, 50, 48, 49, 56, 51, 52, 48, 51, 50, 102, 100, 51, 99, 52, 100, 99, 52, 57, 97, 50, 56, 52, 52, 97, 56, 57, 57, 57, 56, 100, 53, 101, 57, 100, 53, 98, 48, 102, 48, 98, 48, 48, 100, 100, 101, 57, 51, 49, 48, 48, 54, 51, 97, 99, 98, 56, 97, 57, 50, 101, 50, 102, 100, 97, 102, 97, 52, 49, 50, 54, 100, 52, 48, 51, 51, 98, 54, 102, 100, 101, 53, 48, 98, 54, 97, 48, 100, 102, 101, 54, 49, 97, 100, 49, 52, 56, 102, 102, 102, 49, 54, 55, 97, 100, 57, 99, 102, 56, 51, 48, 56, 100, 101, 100, 53, 102, 54, 102, 54, 98, 50, 102, 101, 48, 48, 48, 97, 48, 51, 54, 99, 52, 54, 52, 99, 51, 49, 49, 48, 50, 98, 101, 53, 97, 53, 53, 102, 48, 51, 101, 53, 99, 48, 97, 97, 101, 97, 55, 55, 53, 57, 53, 100, 53, 55, 52, 98, 99, 101, 57, 50, 99, 54, 100, 53, 55, 97, 50, 97, 48, 102, 98, 50, 98, 53, 57, 53, 53, 99, 48, 98, 56, 55, 101, 52, 53, 50, 48, 101, 48, 54, 98, 53, 51, 48, 50, 50, 48, 57, 102, 99, 50, 56, 55, 51, 102, 50, 56, 53, 50, 49, 99, 98, 100, 100, 101, 55, 102, 55, 98, 51, 98, 98, 49, 53, 50, 49, 48, 48, 50, 52, 54, 51, 102, 53, 57, 55, 57, 54, 56, 54, 102, 100, 49, 53, 54, 102, 50, 51, 102, 101, 54, 97, 56, 97, 97, 50, 98, 55, 57] | ||
``` | ||
|
||
The following is a `PostMintBolt11Request` with an invalid signature. Where the `pubkey` in the `PostMintQuoteBolt11Request` is `03d56ce4e446a85bbdaa547b4ec2b073d40ff802831352b8272b7dd7a4de5a7cac`. | ||
|
||
```json | ||
{ | ||
"quote": "9d745270-1405-46de-b5c5-e2762b4f5e00", | ||
"outputs": [ | ||
{ | ||
"amount": 1, | ||
"id": "00456a94ab4e1c46", | ||
"B_": "0342e5bcc77f5b2a3c2afb40bb591a1e27da83cddc968abdc0ec4904201a201834" | ||
}, | ||
{ | ||
"amount": 1, | ||
"id": "00456a94ab4e1c46", | ||
"B_": "032fd3c4dc49a2844a89998d5e9d5b0f0b00dde9310063acb8a92e2fdafa4126d4" | ||
}, | ||
{ | ||
"amount": 1, | ||
"id": "00456a94ab4e1c46", | ||
"B_": "033b6fde50b6a0dfe61ad148fff167ad9cf8308ded5f6f6b2fe000a036c464c311" | ||
}, | ||
{ | ||
"amount": 1, | ||
"id": "00456a94ab4e1c46", | ||
"B_": "02be5a55f03e5c0aaea77595d574bce92c6d57a2a0fb2b5955c0b87e4520e06b53" | ||
}, | ||
{ | ||
"amount": 1, | ||
"id": "00456a94ab4e1c46", | ||
"B_": "02209fc2873f28521cbdde7f7b3bb1521002463f5979686fd156f23fe6a8aa2b79" | ||
} | ||
], | ||
"witness": "cb2b8e7ea69362dfe2a07093f2bbc319226db33db2ef686c940b5ec976bcbfc78df0cd35b3e998adf437b09ee2c950bd66dfe9eb64abd706e43ebc7c669c36c3" | ||
} | ||
``` |