Skip to content
Closed
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
93 changes: 93 additions & 0 deletions 28.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
# NUT-28: Mint Quote Lookup by Public Key

`optional`

`depends on: NUT-04, NUT-20`

---

This NUT adds an endpoint for users to query mint quotes associated with their public key. This enables third-party mint quotes where one user creates and pays a quote for another user to redeem.

> [!NOTE]
>
> Quote creation with a public key and minting with a signature are defined in [NUT-20][20]. This NUT only adds the ability to discover quotes by public key.

## Use cases

- **Mining pools**: A pool creates paid quotes for miners' public keys when shares are validated
- **Gift payments**: `Alice` pays for `Bob` to receive ecash without `Bob` initiating a request

## Querying quotes by public key

To query quotes assigned to a public key, the wallet makes a `POST /v1/mint/quote/{method}/pubkey` request.

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

The wallet includes the following `PostMintQuotesByPubkeyRequest` data:

```json
{
"pubkey": <str>,
"timestamp": <int>,
"signature": <str>,
"unit": <str|null>,
"state": <str|null>
}
```

| Field | Type | Description |
| --- | --- | --- |
| `pubkey` | `str` | Compressed secp256k1 public key (33 bytes, hex-encoded) |
| `timestamp` | `int` | Unix timestamp (provides entropy for signature) |
| `signature` | `str` | BIP340 Schnorr signature (see below) |
| `unit` | `str\|null` | Optional filter by currency unit |
| `state` | `str\|null` | Optional filter by quote state |

### Signature scheme

The message to sign is:

```
msg_to_sign = "quote_lookup" || pubkey || timestamp || unit || state
```

Where `||` denotes concatenation. All fields are UTF-8 strings. If `unit` or `state` is null, use an empty string.

The signature is a [BIP340](https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki) Schnorr signature on `SHA256(msg_to_sign)`.

### Response

The mint responds with a `PostMintQuotesByPubkeyResponse`:

```json
{
"quotes": <Array[MintQuoteResponse]>
}
```

Where `MintQuoteResponse` is the quote response type defined in [NUT-04][04].

## Errors

See [Error Codes][errors]:

- `20010`: Signature for quote lookup invalid

## Settings

The settings for this NUT are part of the mint info response ([NUT-06][06]):

```json
{
"28": {
"supported": <bool>
}
}
```

[04]: 04.md
[06]: 06.md
[20]: 20.md
[errors]: error_codes.md
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ Wallets and mints `MUST` implement all mandatory specs and `CAN` implement optio
| [25][25] | Payment Method: BOLT12 | [cdk], [cashu-ts][ts] | [cdk-mintd] |
| [26][26] | Payment Request Bech32m Encoding | [cdk] | - |
| [27][27] | Nostr Mint Backup | [Cashu.me][cashume], [cdk] | - |
| [28][28] | Third-Party Mint Quotes | - | - |

#### Wallets:

Expand Down Expand Up @@ -102,3 +103,4 @@ Wallets and mints `MUST` implement all mandatory specs and `CAN` implement optio
[25]: 25.md
[26]: 26.md
[27]: 27.md
[28]: 28.md
2 changes: 2 additions & 0 deletions error_codes.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
| 20007 | Quote is expired | [NUT-04][04], [NUT-05][05] |
| 20008 | Signature for mint request invalid | [NUT-20][20] |
| 20009 | Pubkey required for mint quote | [NUT-20][20] |
| 20010 | Signature for quote lookup invalid | [NUT-28][28] |
| 30001 | Endpoint requires clear auth | [NUT-21][21] |
| 30002 | Clear authentication failed | [NUT-21][21] |
| 31001 | Endpoint requires blind auth | [NUT-22][22] |
Expand All @@ -50,3 +51,4 @@
[20]: 20.md
[21]: 21.md
[22]: 22.md
[28]: 28.md
33 changes: 33 additions & 0 deletions tests/28-test.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# NUT-28 Test Vectors

## Quote Lookup Signature

### Message Construction

For a request with:
- `pubkey` = `03d56ce4e446a85bbdaa547b4ec2b073d40ff802831352b8272b7dd7a4de5a7cac`
- `timestamp` = `1701704800`
- `unit` = `sat`
- `state` = `PAID`

```
msg_to_sign = "quote_lookup" || pubkey || timestamp || unit || state
= "quote_lookup03d56ce4e446a85bbdaa547b4ec2b073d40ff802831352b8272b7dd7a4de5a7cac1701704800satPAID"
```

For a request with null filters:
- `pubkey` = `03d56ce4e446a85bbdaa547b4ec2b073d40ff802831352b8272b7dd7a4de5a7cac`
- `timestamp` = `1701704800`
- `unit` = null
- `state` = null

```
msg_to_sign = "quote_lookup" || pubkey || timestamp || "" || ""
= "quote_lookup03d56ce4e446a85bbdaa547b4ec2b073d40ff802831352b8272b7dd7a4de5a7cac1701704800"
```

The signature is BIP340 Schnorr on `SHA256(msg_to_sign)`.

### Invalid Signature

A request with an invalid signature should return error code `20010`.