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

LNURL-pay: Drop metadata description hash validation #234

Open
wants to merge 3 commits into
base: luds
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 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
24 changes: 6 additions & 18 deletions 06.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ LUD-06: `payRequest` base spec.

The idea here is that a wallet can scan a static QR code or click on a static LNURL address and get back details about a payment that is expected. The details may include much more extensive metadata than a normal Lightning invoice. And the amounts may be fixed or within a range.

Then, once the user accepts the terms (and choose an amount, if that is not fixed), the wallet will call the service and get a Lightning invoice specific for that payment, containing a hash of the metadata as its [`h` tag (`description_hash`)](https://github.com/lightningnetwork/lightning-rfc/blob/master/11-payment-encoding.md#requirements-3) and proceed to pay the invoice if it matches the expected amount and hash.
Then, once the user accepts the terms (and choose an amount, if that is not fixed), the wallet will call the service and get a Lightning invoice specific for that payment and proceed to pay the invoice if it matches the expected amount and hash.

## Pay to static QR/NFC/link

Expand All @@ -22,7 +22,7 @@ Then, once the user accepts the terms (and choose an amount, if that is not fixe
"callback": string, // The URL from LN SERVICE which will accept the pay request parameters
"maxSendable": number, // Max millisatoshi amount LN SERVICE is willing to receive
"minSendable": number, // Min millisatoshi amount LN SERVICE is willing to receive, can not be less than 1 or more than `maxSendable`
"metadata": string, // Metadata json which must be presented as raw string here, this is required to pass signature verification at a later step
"metadata": string, // Metadata json which must be presented as raw string here
"tag": "payRequest" // Type of LNURL
}
```
Expand Down Expand Up @@ -69,7 +69,7 @@ Then, once the user accepts the terms (and choose an amount, if that is not fixe
"[[\"text/plain\", \"lorem ipsum blah blah\"]]"
```

3. `LN WALLET` displays a payment dialog where user can specify an exact sum to be sent which would be bounded by:
4. `LN WALLET` displays a payment dialog where user can specify an exact sum to be sent which would be bounded by:

```
max can send = min(maxSendable, local estimation of how much can be sent from wallet)
Expand All @@ -82,15 +82,15 @@ Then, once the user accepts the terms (and choose an amount, if that is not fixe
And it may include:
- An image element with the contents of `image/png` or `image/jpeg`.

4. `LN WALLET` makes a GET request using
5. `LN WALLET` makes a GET request using

```
<callback><?|&>amount=<milliSatoshi>
```

`amount` being the amount specified by the user in millisatoshis.

5. `LN Service` takes the GET request and returns JSON response of form:
6. `LN Service` takes the GET request and returns JSON response of form:

```Typescript
{
Expand All @@ -105,17 +105,5 @@ Then, once the user accepts the terms (and choose an amount, if that is not fixe
{"status":"ERROR", "reason":"error details..."}
```

6. `LN WALLET` Verifies that `h` tag in provided invoice is a hash of `metadata` string converted to byte array in UTF-8 encoding.
7. `LN WALLET` Verifies that amount in provided invoice equals the amount previously specified by user.
10. `LN WALLET` pays the invoice, no additional user confirmation is required at this point.

## Notes on metadata for server-side LNURL-PAY

### When client makes a first call:

Construct a metadata object, turn it into json, then include it into parent json as a string.

### When client makes a second call

1. Make a hash as follows: `sha256(utf8ByteArray(unescaped_metadata_string))`.
2. Generate a payment request using an obtained hash.
8. `LN WALLET` pays the invoice, no additional user confirmation is required at this point.
32 changes: 0 additions & 32 deletions 18.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ LUD-18: Payer identity in `payRequest` protocol.

The idea here is that the payer can identify itself when paying. The scope varies from free-form names (or [LUD-16](16.md) internet identifiers) useful for loose identification in comments or similar things, to cryptographic keys for proof of payment, to authentication keys for future login into a `SERVICE`'s website after a payment and other use cases.

Aside from that, the payer ids are also committed to the invoice by means of the `descriptionHash` property, which ensures strong cryptographic guarantees for proof of payment.

## 1. `payerData` record

If `SERVICE` wants to get one or more types of payer identities from `WALLET` then it MUST alter its JSON response to the first callback to include a `payerData` field, as follows (notice that the `payerData` record below has a bunch of fields only for completion, an actual response will likely contain just a subset of these):
Expand Down Expand Up @@ -67,33 +65,3 @@ Each key in this JSON object should correspond to a requested payerdata from the
`WALLET` CAN send any of the payer id kinds if they are listed in the `payerData` record. But if any is marked as `"mandatory": true` then `WALLET` MUST send or otherwise do not proceed with the payment flow.

`WALLET` SHOULD NOT send payer identity types omitted in `payerData` record, none at all if record is not present.

## 3. Committing payer to the invoice

If `SERVICE` requests (section 1) and `WALLET` sends a `payerdata` record in the callback (section 2), the payer ids MUST be committed to the metadata before the invoice is created.

`SERVICE` MUST append it to the metadata as it was received (after url-decoding), as in the following example:

```diff
[["text/plain", "description"], ["image/png;base64", "AAA=="]]
+[["text/plain", "description"], ["image/png;base64", "AAA=="]]{<identity records>}
```

After doing that, `SERVICE` proceeds to hash the metadata and include that hash in the generated BOLT11 invoice as the `descriptionHash` field.

On its own side, `WALLET` MUST do the same thing before hashing the metadata and checking its hashed value against the `descriptionHash` field of the invoice received from `SERVICE`.

Even if `WALLET` sends more `payerdata` fields than requested by `SERVICE`, `SERVICE` MUST still append everything as received.

`WALLET` MUST NOT expect `SERVICE` to append the identity records to the metadata if `SERVICE` hadn't included the `payerData` record in the first response, as that would mean `SERVICE` doesn't understand LUD-18 at all.

### Pseudocode example for calculating `descriptionHash` with `payerdata`

```
originalServiceMetadata = '[["text/plain", "description"], ["image/png;base64", "AAA=="]]'
urlEncodedPayerIds = '%7B%22name%22%3A%22bob%22%2C%22auth%22%3A%7B%22key%22%3A%2202c9323d02fc164f89c8f688dbfba8aad69a96fa8f6253ba8cce2c6f1546073fa3%22%2C%22sig%22%3A%222afd21794e2a801d0d516584ceebe1a24ed8991dd5ec708259aeaee5c0d2d1437542b689ee5d39e619a01a257142d49c18a4af3088c46ce87e2d941a1bcc7210%22%7D%2C%22identifier%22%3A%22bob%40bob.com%22%2C%22pubkey%22%3A%2203ee58475055820fbfa52e356a8920f62f8316129c39369dbdde3e5d0198a9e315%22%7D'
payerData = urldecode(urlEncodedPayerIds)
descriptionToBeHashed = metadata + payerData
descriptionToBeHashed == '[["text/plain", "description"], ["image/png;base64", "AAA=="]]{"name":"bob","auth":{"key":"02c9323d02fc164f89c8f688dbfba8aad69a96fa8f6253ba8cce2c6f1546073fa3","sig":"2afd21794e2a801d0d516584ceebe1a24ed8991dd5ec708259aeaee5c0d2d1437542b689ee5d39e619a01a257142d49c18a4af3088c46ce87e2d941a1bcc7210"},"identifier":"[email protected]","pubkey":"03ee58475055820fbfa52e356a8920f62f8316129c39369dbdde3e5d0198a9e315"}'
descriptionHash = sha256(utf8(descriptionToBeHashed))
```