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

Include BIP 353 name info in invoice_requests #1180

Open
wants to merge 20 commits into
base: master
Choose a base branch
from

Conversation

TheBlueMatt
Copy link
Collaborator

Based on #798,

BIP 353 defines a method for resolving Human Readable Names (in the
form ₿`name`@`domain`) into bitcoin: URIs. In order to use them with
BOLT 12 with a wildcard DNS entry which resolves to a single
BOLT 12 offer for all `name`s in a domain, we need a way to
disambiguate the `name` being paid in an `invoice_request` which we
provide here by simply copying the HRN into the `invoice_request`.

rustyrussell and others added 20 commits August 12, 2023 01:00
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
It's far easier to validate these on parsing than to hand-validate them
elsewhere.

I didn't turn `alias` or `error` into this, though they're similar
(`alias` can have a nul terminator).

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
A BOLT11 "invoice" has proven too low-level for human use in many
scenarios.  Efforts like lnurl have covered the gap, but integrating
some of such higher layers into the lightning protocol itself has many
advantages.

This draft defines three new things:

1. A new invoice format.  I know, this is painful, but it maps almost
   1:1 to the current format (though signatures are very different),
   is easier to implement, and easier to send via the lightning
   network itself.

2. Formats for an "offer", which for all intents and purposes serves
   as the new, persistent invoice for users.

3. Format for an "invoice_request": this is a message sent via the
   lightning network itself to receive the real invoice, or can
   be used directly in a send-money scenario (e.g. ATM).

The offer (for accepting payments) or invoice_request (for sending
payments) are usually presented via a QR code or similar, the replies
are sent using onion messages.  Each copies fields from the prior so
it stands alone, to allow statelessness.

Features which have been deliberately omitted for the initial version:
- Recurrence.
- Invoice replacement ("don't accept that old payment!")
- Payer proof for refunds.

I need to thank everyone who gave detailed feedback, particularly:

1. Thomas H of ACINQ (https://github.com/thomash-acinq)
2. Joost Jager (https://github.com/joostjager)
3. Aditya Sharma (https://github.com/adi2011)
4. Rene Pickhardt (https://github.com/renepickhardt)
5. Bastien Teinturier (https://github.com/t-bast)
6. Valentine Wallace of LDK (https://github.com/valentinewallace)
7. Matt Corallo of LDK (https://github.com/BlueMatt)
8. Jeffrey Czyz of Square Crypto (https://github.com/jkczyz)

Also @bjarnemagnussen, @ellemouton, @animatedbarber, @617a7a,
@instagibbs, and @eupn.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Regenerated bolt12/signature-test.json; for some reason jq reordered a
few fields, but it now shows the complete signature.
bolt12/format-string-test.json is now a valid offer.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Offers may contain blinded paths to allow for greater recipient privacy.
However, they come at a cost of increased QR code size as each hop
requires a 33-byte `point` for the `next_node_id`. Allow using
`short_channel_id` instead, which only requires 8 bytes.

Still allow for use of `next_node_id` for cases where the blinded path
may not involve channel counterparties or for long-lived offers, which
may outlive the given channels.
Offers may contain blinded paths to allow for greater recipient privacy.
However, they come at a cost of increased QR code size as the
introduction node requires a 33-byte `point`.

Define a new `sciddir_or_pubkey` fundamental type such that either a point or a
reference to one in a `channel_announcement` can be used. This is
backwards compatible with `point`.

Use this new type for the `blinded_path` subtype's `first_node_id`.
Add example of scid first-id for blinded path (thanks jkczyz!)
Remove leftover reference to direct invoices (thanks jkczyz!)
describe the offer_amount/currency/invreq_amount/invoice_amount purpose
make offer_node_id optional, if they specify offer_paths.

1. We now tell them explicitly where to send the onion message.
2. We use this as shorthand later when they're supposed to check the reply.
3. Raw invoice_requests cannot set `offer_paths`, so we add `invreq_paths` for this.

Thanks to thomash-acinq and jkczyz!
Make offer_description optional if there's no amount; this is the common "tip" case.

Note that this implies a minimal offer is simply a bech32 encoding of the offer_node_id field, or "lno1" + bech32(0x2242<node_id>).  This might become normalized as the "default offer" to which you can throw sats to a node directly?
Since we say you should forward via short_channel_id:
1. Allow setting it!
2. Make it clear that it must be a public scid or an alias (no probing!).
3. Make it clear what to do if both are set!
Recent spec meeting brought up various things:

1. Resending the same invoice: we key of invreq_payer_id, but ACINQ is considering reuse for some cases. We should key off invreq_metadata which is required, actually unique and is supposed to be unguessable.
2. Move this caching description from the invoice section to the "invoice_request reader" section: I couldn't find it!
3. Renamed offer_node_id to offer_issuer_id (Matt points out, it may be a node_id, but it's basically the key used to sign the invoice)
4. The spec says you must send a message to offer_node_id if it's set: that's backwards, you must use blinded paths if offered, and only fall back to this as a node id if there are none.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Make the ranges big (1BN), so people can self-assign, but still ensure
they don't overlap.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
BIP 353 defines a method for resolving Human Readable Names (in the
form ₿`name`@`domain`) into bitcoin: URIs. In order to use them with
BOLT 12 with a wildcard DNS entry which resolves to a single
BOLT 12 offer for all `name`s in a domain, we need a way to
disambiguate the `name` being paid in an `invoice_request` which we
provide here by simply copying the HRN into the `invoice_request`.
@t-bast
Copy link
Collaborator

t-bast commented Jul 11, 2024

Thanks for sharing this. I'm not sure how the node receiving an invoice_request containing a BIP 353 address should behave though. It shouldn't be specified here (probably rather in a bLIP), but it's useful to have the high-level design discussed here to help reviewers.

Let's assume that Bob is the domain owner and Alice the end user (₿alice@bob.com). Carol is trying to pay Alice: she resolves *@bob.com, receives an offer created by Bob, and sends an invoice_request containing ₿alice@bob.com to Bob.

What does Bob do with this invoice_request, which contains its own offer instead of an offer generated by Alice? It cannot simply forward it to Alice, because Alice won't issue an invoice for an offer owned by Bob?

Does Bob instead reply to Carol with an offer from Alice and Carol then follows up with another invoice_request for that offer? Or is there a mechanism on Alice's side to create an invoice for her own offer, even though it doesn't match Carol's invoice_request (and thus also a mechanism for Carol to accept the returned invoice)?

@TheBlueMatt
Copy link
Collaborator Author

TheBlueMatt commented Jul 11, 2024

I'm not sure how the node receiving an invoice_request containing a BIP 353 address should behave though.
Let's assume that Bob is the domain owner and Alice the end user

Obviously this isn't an issue for custodial services, so I assume you're asking about non-custodial. In this case Bob would have to issue an invoice using a payment_hash and blinded path from Alice. This would require some proprietary (bLIP?) protocol where Bob can ask Alice for a fresh payment_hash and blinded path (replacing the final onion message hop, so its no new latency).

For async payments its a bit more complicated, but Alice can simply register her payment parameters with Bob rather than a full static invoice, which Bob can turn into his own static async invoice for Alice.

Its a chunk of additional protocol work to implement, so IMO people should just put a few 100k DNS entries in a zone (which is super trivial, just dont use some hosted DNS garbage that charges you $$$$$ to store 5MiB of data), but someone could build this if they want. I think this will likely be used much more for custodial services where its trivial.

It shouldn't be specified here (probably rather in a bLIP)

Why? You could reasonably argue that many wallets won't implement BIP 353 sending and thus it should be a bLIP, but it seems like all of the major lightning implementations will need to support this field (as at least some of their users will implement BIP 353 sending), which implies it should be here.

@t-bast
Copy link
Collaborator

t-bast commented Jul 11, 2024

In this case Bob would have to issue an invoice using a payment_hash and blinded path from Alice. This would require some proprietary (bLIP?) protocol where Bob can ask Alice for a fresh payment_hash and blinded path (replacing the final onion message hop, so its no new latency).

That means Alice will create an invoice for an offer created by Bob, not her own offer...I'm not sure how messy it will be to implement: since Alice doesn't have the private key for Bob's offer, she can't sign that invoice. So that's probably the wrong approach, and you're instead proposing that Bob is creating and signing the invoice, but the blinded path leads to Alice? So when receiving the payment, Alice just needs to be ok with the fact that the offer wasn't generated by her.

The issue with that scheme is that it's not TOFU: for every payment, Bob has an opportunity to cheat and generate invoices that are actually for him. Wouldn't it be a better idea to use the DNS resolution to (somehow) obtain an offer from Alice? This would be TOFU, but then the offer can be verified IRL, and afterwards the payer is guaranteed that they'll keep paying Alice whenever they reuse this offer. This way payers store the offer they received, and don't need to make DNS requests the next time they want to pay Alice.

so IMO people should just put a few 100k DNS entries in a zone

That is indeed my preferred option as well, this is a simpler flow!

Why? You could reasonably argue that many wallets won't implement BIP 353 sending and thus it should be a bLIP, but it seems like all of the major lightning implementations will need to support this field (as at least some of their users will implement BIP 353 sending), which implies it should be here.

I wasn't saying that this PR shouldn't be in the BOLT, but rather that the additional protocol we're mentioning should be a bLIP. I agree that this BIP 353 field in invoice_requests should probably be in the BOLTs once we've sketched out how to use it properly.

@TheBlueMatt
Copy link
Collaborator Author

That means Alice will create an invoice for an offer created by Bob, not her own offer...I'm not sure how messy it will be to implement: since Alice doesn't have the private key for Bob's offer, she can't sign that invoice. So that's probably the wrong approach, and you're instead proposing that Bob is creating and signing the invoice, but the blinded path leads to Alice? So when receiving the payment, Alice just needs to be ok with the fact that the offer wasn't generated by her.

Right, I think we're on the same page. Having Alice create the offer is probably a non-starter. Luckily if Alice builds the blinded path she doesn't know that she didn't create the offer. She just sees the blinded path she generated in an HTLC :)

The issue with that scheme is that it's not TOFU: for every payment, Bob has an opportunity to cheat and generate invoices that are actually for him. Wouldn't it be a better idea to use the DNS resolution to (somehow) obtain an offer from Alice?

Yea, I agree, you could have some scheme where you fetch an offer instead (which is how the original bLIP was specified), but I wanted to avoid the extra round-trip during normal payment flows. Sadly, BIP 353 is somewhat limited in TOFU-ness as you do need to revalidate the DNS records occasionally (often on the order of a day, depending on DNS settings, both to let people rotate addresses if they want but also to make sure the domain hasn't expired). A wallet can (and maybe should) of course warn the sender if the recipient info has changed, but its never perfect TOFU.

This would be TOFU, but then the offer can be verified IRL, and afterwards the payer is guaranteed that they'll keep paying Alice whenever they reuse this offer. This way payers store the offer they received, and don't need to make DNS requests the next time they want to pay Alice.

If you're able to "verify IRL" you shouldn't be using BIP 353 :).

I wasn't saying that this PR shouldn't be in the BOLT, but rather that the additional protocol we're mentioning should be a bLIP. I agree that this BIP 353 field in invoice_requests should probably be in the BOLTs once we've sketched out how to use it properly.

Ah, okay! I have no intention of specifying that protocol. If people want to offer BIP 353 names for non-custodial wallets they'll have to either build it or just deal with running BIND/KNOT/etc themselves 🤷‍♂️.

@t-bast
Copy link
Collaborator

t-bast commented Jul 11, 2024

Thanks, that's clear enough! I agree on all points 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants