Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NUT-XX and NUT-XX+1: Mint / Melt Bitcoin On-Chain #107

Open
wants to merge 17 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
208 changes: 208 additions & 0 deletions 18.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
NUT-18: Mint tokens Bitcoin On-Chain
==========================

`optional`

---

This NUT describes the process of minting tokens on Bitcoin On-Chain analog to NUT-04 for Bitcoin Lightning.

# 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 `btconchain`).

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

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

```json
{
"amount": <int>,
"unit": <str_enum["sat"]>
}
```
with the requested `amount` and the `unit`.

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

```json
{
"quote": <str>,
"address": <str>,
"state": <str_enum[STATE]>,
"expiry": <int>
}
```
Comment on lines +20 to +37
Copy link
Collaborator

@thesimplekid thesimplekid Sep 5, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If unit is included here don't we need to add an amount in sats to the quote response so the mint can signal the exchange rate. Otherwise if it is non bitcoin unit the wallet will not know how much bitcoin to send to the address to fill the quote.

That being said because of the imprecise nature of sending a bitcoin onchain transaction I would be for removing the unit and limiting it to sat denominated quotes.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree. I support keeping it simple to start and only supporting sats as the unit.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Feels simplest to say

  1. You can only mint BTC on-chain (sats only, no other units)
  2. You can then swap sats ecash for ecash in a different unit with the mint

So if you wanted to get e.g. "USD eCash by making an onchain tx", it's a two-step process. Has the advantage of a swap being atomic, much faster and less flaky than a mint depending on N confirmations


Where `quote` is the quote ID and `address` is the payment request to fulfill.

`state` is an enum string field with possible values `"UNPAID"`, `"PAID"`, `"PENDING"`, `"ISSUED"`:
- `"UNPAID"` means that the quote's request has not been paid yet.
- `"PAID"` means that the request has been paid.
- `"PENDING"` means that the quote is currently being issued.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this when the transaction is still confirming?

- `"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 https://mint.host:3338/v1/mint/quote/btconchain -d '{"amount": 10, "unit": "sat"}' -H "Content-Type: application/json"
```

Response of `Bob`:

```json
{
"quote": "DSGLX9kevM...",
"address": "bc1qkyfgd7mus7ykfd7qkwakq75qsf7rtm...",
"state": "UNPAID",
"expiry": 1701704757
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it worth adding the txid here?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might though this question made me think what if a a quote is payed with multiple transaction, I think that should still be valid? So to cover that it would need to be a list of txid? Unless we explicitly say it MUST be paid in one tx.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it should be explicit only one transaction is supported.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is always going to be possible to make multiple transactions paying the same address, so someone will do that... so how to handle? Multiple transactions also make reorg risk more complex and add consolidation cost for the Mint.

Feels resonable that the requirement should be for a single transaction with one output paying the mint, anything else and the Mint can 'fail' the mint request and refund whatever it got somehow -- would need the user to provide a refund address, or the mint would have to issue an ecash claim on it (but then, consolidation cost etc., would be a 'mint with on-chain dust and then drain the mint's lightning channels' attackable behaviour)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we return the number of confirmations the transaction currently has?

```

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.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be worth adding a note here that wallets SHOULD display the quote as a BIP21 URI or BIP21 URI encoded as a QR code to the user. This would help reduce errors of underpaying or overpaying accidentally.

NOTE: There is a draft to replace BIP21 bitcoin/bips#1555 but as its backwards compatible i suggest we say bip21 for now and can update later.

## Check mint quote state

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

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

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

Example request of `Alice` with curl:

```bash
curl -X GET https://mint.host:3338/v1/mint/quote/btconchain/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/{method}` endpoint where `method` is the payment method requested (here `btconchain`).

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

The wallet `Alice` includes the following `PostMintBtcOnchainRequest` 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 `PostMintBtcOnchainResponse`:

```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/btconchain -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"
}
]
}
```

## 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 supported or not. They are part of the info response of the mint ([NUT-06][06]) which in this case reads
```json
{
"18": {
"supported": true,
"methods": [
{
"method": "btconchain",
"unit": "sat",
"min_amount": 10000,
"max_amount": 1000000,
"min_confirmations": 3
}
]
}
}
```

[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