Skip to content

Commit

Permalink
feat: bolt12
Browse files Browse the repository at this point in the history
  • Loading branch information
thesimplekid committed Oct 25, 2024
1 parent ea8d7d6 commit 5565d0b
Show file tree
Hide file tree
Showing 3 changed files with 434 additions and 0 deletions.
237 changes: 237 additions & 0 deletions 19.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,237 @@
# NUT-18: Mint tokens via BOLT12

`optional`

---

Similar to [NUT-04][04], which covers melting via BOLT11 invoices, melting via [BOLT12](https://github.com/lightning/bolts/blob/master/12-offer-encoding.md) is also a two-step process: requesting a melt quote and melting tokens. This document describes both steps, focusing on BOLT12-specific considerations.

# Mint quote

To request a mint quote, the wallet of `Alice` makes a `POST /v1/mint/quote/bolt12`.

```http
POST https://mint.host:3338/v1/mint/quote/bolt12
```

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

```json
{
"amount": <int>,
"unit": <str_enum["sat"]>,
"description": <str|null>
}
```

with the requested `amount` and the `unit`. An optional `description` can be passed if the mint signals support for it in `MintMethodSetting`.

The mint `Bob` then responds with a `PostMintQuoteBolt12Response`:

```json
{
"quote": <str>,
"request": <str>,
"state": <str_enum[STATE]>,
"expiry": <int>
}
```

Where `quote` is the quote ID and `request` is the bolt12 offer. The bolt12 offer **MUST** be `single_use` and have the `absolute_expiry` and `amount` set to match the quote. `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.

Note: `quote` is a **unique and random** id generated by the mint to internally look up the payment state. `quote` **MUST** remain a secret between user and mint and **MUST NOT** be derivable from the payment request. A third party who knows the `quote` ID can front-run and steal the tokens that this operation mints.

## Example

Request of `Alice` with curl:

```bash
curl -X POST http://localhost:3338/v1/mint/quote/bolt12 -d '{"amount": 10, "unit": "sat"}' -H "Content-Type: application/json"
```

Response of `Bob`:

```json
{
"quote": "DSGLX9kevM...",
"request": "lnbc100n1pj4apw9...",
"state": "UNPAID",
"expiry": 1701704757
}
```

The wallet **MUST** store the `amount` in the request and the `quote` id in the response in its database so it can later request the tokens after paying the request. After payment, the wallet continues with the next section.

## Check mint quote state

To check whether a mint quote has been paid, `Alice` makes a `GET /v1/mint/quote/bolt12/{quote_id}`.

```http
GET https://mint.host:3338/v1/mint/quote/bolt12/{quote_id}
```

Like before, the mint `Bob` responds with a `PostMintQuoteBolt12Response`.

Example request of `Alice` with curl:

```bash
curl -X GET http://localhost:3338/v1/mint/quote/bolt12/DSGLX9kevM...
```

# Minting tokens

After requesting a mint quote and paying the request, the wallet proceeds with minting new tokens by calling the `POST /v1/mint/bolt12`.

```http
POST https://mint.host:3338/v1/mint/bolt12
```

The wallet `Alice` includes the following `PostMintBolt12Request` data in its request

```json
{
"quote": <str>,
"outputs": <Array[BlindedMessage]>
}
```

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.

The mint `Bob` then responds with a `PostMintBolt12Response`:

```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/bolt12 -H "Content-Type: application/json" -d \
'{
"quote": "DSGLX9kevM...",
"outputs": [
{
"amount": 8,
"id": "009a1f293253e41e",
"B_": "035015e6d7ade60ba8426cefaf1832bbd27257636e44a76b922d78e79b47cb689d"
},
{
"amount": 2,
"id": "009a1f293253e41e",
"B_": "0288d7649652d0a83fc9c966c969fb217f15904431e61a44b14999fabc1b5d9ac6"
}
]
}'
```

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` **CAN** repeat the same request until the Lightning invoice is settled.

## Unblinding signatures

Upon receiving the `BlindSignatures` from the mint `Bob`, the wallet of `Alice` unblinds them to generate `Proofs` (using the blinding factor `r` and the mint's public key `K`, see BDHKE [NUT-00][00]). The wallet then stores these `Proofs` in its database:

```json
[
{
"id": "009a1f293253e41e",
"amount": 2,
"secret": "407915bc212be61a77e3e6d2aeb4c727980bda51cd06a6afc29e2861768a7837",
"C": "02bc9097997d81afb2cc7346b5e4345a9346bd2a506eb7958598a72f0cf85163ea"
},
{
"id": "009a1f293253e41e",
"amount": 8,
"secret": "fe15109314e61d7756b0f8ee0f23a624acaa3f4e042f61433c728c7057b931be",
"C": "029e8e5050b890a7d6c0968db16bc1d5d5fa040ea1de284f6ec69d61299f671059"
}
]
```

## Settings

The settings for this nut indicate the supported method-unit pairs for minting and whether minting is disabled or not. They are part of the info response of the mint ([NUT-06][06]) which in this case reads

```json
{
"18": {
"methods": [
<MintMethodSetting>,
...
],
"disabled": <bool>
}
}
```

`MintMethodSetting` indicates supported `method` and `unit` pairs and additional settings of the mint. `disabled` indicates whether this minting is disabled.

`MintMethodSetting` is of the form:

```json
{
"method": <str>,
"unit": <str>,
"min_amount": <int|null>,
"max_amount": <int|null>,
"description": <bool|null>
}
```

`min_amount` and `max_amount` indicate the minimum and maximum amount for an operation of this method-unit pair.

Example `MintMethodSetting`:

```json
{
"method": "bolt12",
"unit": "sat",
"min_amount": 0,
"max_amount": 10000,
"description": true
}
```

[00]: 00.md
[01]: 01.md
[02]: 02.md
[03]: 03.md
[04]: 04.md
[05]: 05.md
[06]: 06.md
[07]: 07.md
[08]: 08.md
[09]: 09.md
[10]: 10.md
[11]: 11.md
[12]: 12.md
Loading

0 comments on commit 5565d0b

Please sign in to comment.