Skip to content

NUT-20: Signature on Mint Quote

optional

depends on: NUT-04


This NUT defines signature-based authentication for mint quote redemption. When requesting a mint quote, clients provide a public key. The mint will then require a valid signature from the corresponding secret key to process the mint operation.

[!CAUTION]

NUT-04 mint quotes without a public key can be minted by anyone who knows the mint quote id 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. We present an example with the method being bolt11 here.

POST https://mint.host:3338/v1/mint/quote/bolt11

The wallet of Alice includes the following PostMintQuoteBolt11Request data in its request:

{
  "amount": <int>,
  "unit": <str_enum["sat"]>,
  "description": <str>, // Optional
  "pubkey": <str> // Optional <-- New
}

with the requested amount,unit, and description according to NUT-04.

pubkey is the public key that will be required for signature verification during the minting operation. The mint will only mint ecash after receiving a valid signature from the corresponding private key in the subsequent PostMintRequest.

[!IMPORTANT]

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.

The mint Bob then responds with a PostMintQuoteBolt11Response:

{
  "quote": <str>,
  "request": <str>,
  "state": <str_enum[STATE]>,
  "expiry": <int>,
  "pubkey": <str> // Optional <-- New
}

The response is the same as in NUT-04 except for pubkey which has been provided by the wallet in the previous request.

Example

Request of Alice with curl:

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:

{
  "quote": "9d745270-1405-46de-b5c5-e2762b4f5e00",
  "request": "lnbc100n1pj4apw9...",
  "state": "UNPAID",
  "expiry": 1701704757,
  "pubkey": "03d56ce4e446a85bbdaa547b4ec2b073d40ff802831352b8272b7dd7a4de5a7cac"
}

Signing the mint request

Message aggregation

To provide a signature for a mint request, the owner of the signing public keys must concatenate the quote ID quote in PostMintQuoteBolt11Response and the B_ fields of all BlindedMessages in the PostMintBolt11Request (i.e., the outputs, see NUT-00) to a single message string in the order they appear in the PostMintRequest. This concatenated string is then hashed and signed (see Signature scheme).

[!NOTE]

Concatenating the quote ID and the outputs into a single message prevents maliciously replacing the outputs.

If a request has n outputs, the message to sign becomes:

msg_to_sign =  quote || B_0 || ... || B_(n-1)

Where || denotes concatenation, quote is the UTF-8 quote id in PostMintQuoteBolt11Response, and each B_n is a UTF-8 encoded hex string of the outputs in the PostMintBolt11Request.

Signature scheme

To mint a quote where a public key was provided, the wallet includes a signature on msg_to_sign in the PostMintBolt11Request. We use a BIP340 Schnorr 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).

POST https://mint.host:3338/v1/mint/bolt11

The wallet Alice includes the following PostMintBolt11Request data in its request

{
  "quote": <str>,
  "outputs": <Array[BlindedMessage]>,
  "signature": <str|null> <-- New
}

with the quote being the quote ID from the previous step and outputs being BlindedMessages as in NUT-04.

signature is the signature on the msg_to_sign which is the concatenated quote id and the outputs as defined above.

The mint responds with a PostMintBolt11Response as in NUT-04 if all validations are successful.

Example

Request of Alice with curl:

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"
    }
  ],
  "signature": "d9be080b33179387e504bb6991ea41ae0dd715e28b01ce9f63d57198a095bccc776874914288e6989e97ac9d255ac667c205fa8d90a211184b417b4ffdd24092"

}'

Response of Bob:

{
  "signatures": [
    {
      "id": "009a1f293253e41e",
      "amount": 2,
      "C_": "0224f1c4c564230ad3d96c5033efdc425582397a5a7691d600202732edc6d4b1ec"
    },
    {
      "id": "009a1f293253e41e",
      "amount": 8,
      "C_": "0277d1de806ed177007e5b94a8139343b6382e472c752a74e99949d511f7194f6c"
    }
  ]
}

Errors

If the wallet user Alice does not include a signature on the PostMintBolt11Request but did include a pubkey in the PostMintBolt11QuoteRequest then Bob MUST respond with an error. Alice CAN repeat the request with a valid signature.

See Error Codes:

  • 20008: Mint quote with pubkey but no valid signature provided for mint request.
  • 20009: Mint quote requires pubkey but none given or invalid pubkey.

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) which in this case reads

{
  "20": {
    "supported": <bool>,
  }
}