-
Notifications
You must be signed in to change notification settings - Fork 53
blip-0039: BOLT 11 Invoice Blinded Path Tagged Field #39
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,307 @@ | ||
| ``` | ||
| bLIP: 39 | ||
| Title: BOLT 11 Invoice Blinded Path Tagged Field | ||
| Author: Elle Mouton <elle.mouton@gmail.com> | ||
| Status: Draft | ||
| Created: 2024-07-08 | ||
| * Post-History: 2024-06-26: https://delvingbitcoin.org/t/blip-bolt-11-invoice-blinded-path-tagged-field/991 | ||
| License: CC0 | ||
| ``` | ||
|
|
||
| ## Abstract | ||
|
|
||
| This bLIP defines a new [tagged field][tagged-fields] for the payment invoice | ||
| encoding described by [BOLT 11][bolt11] which can be used to communicate | ||
| encoded blinded path information to the payer of the invoice. | ||
|
|
||
| ## Copyright | ||
|
|
||
| This bLIP is licensed under the CC0 license. | ||
|
|
||
| ## Rational | ||
|
|
||
| Blinded paths have been included in the [BOLT 4 specification][blinded-paths] | ||
| and using them in the context of receiving payments is natively supported in | ||
| the [BOLT 12 Offers proposal][offers]. However, the Offers proposal also | ||
| includes various other dependencies such as onion messaging and various new | ||
| protocol message all of which will require a network wide upgrade before full | ||
| advantage can be taken of the new protocol. Blinded paths themselves are a | ||
| useful tool for privacy and potentially more reliable payment delivery due to | ||
| receiver being able to select paths it knows to be more reliable. This document | ||
| proposes a carve-out to the existing [BOLT 11][bolt11] invoice format so that | ||
| testing of blinded paths can be done in implementations that have not yet | ||
| implemented the full Offers specification. This will be done by adding a new | ||
| tagged-field type to the BOLT 11 invoice specification that will encode a | ||
| blinded payment path. With this bLIP, the sender and the receiver of a payment | ||
| will need to be aware of the new tagged-field and the receiver will need to | ||
| support route blinding and ideally have direct channel peers who also support | ||
| route blinding in order to start widening their anonymity set. | ||
|
|
||
| ## Specification | ||
|
|
||
| ### Feature Bit | ||
|
|
||
| A new feature bit, `bolt11_blinded_path`, for the `I` context will be added | ||
| using bits from the experimental range. This is required because the BOLT 11 | ||
| tagged-fields pre-date the TLV format and so nodes parsing the invoice will just | ||
| skip fields they don't know as there is no concept of "It's OK to be odd". This | ||
| feature bit thus allows nodes to fail fast if they do not yet understand the new | ||
| tagged field. | ||
|
|
||
| ### Tagged Field | ||
|
|
||
| The proposal is to add the following new [tagged field][tagged-fields] to the | ||
| set defined in [BOLT 11][bolt11]: | ||
|
|
||
| - `b` (20): `data_length` variable. One or more entries each containing a | ||
| blinded payment path for a private route; there may be more than one `b` | ||
| field. The field uses the `blinded_payinfo` type described below which draws | ||
| heavily on the proposed encoding of the `blinded_payinfo` subtype defined in | ||
| the [Offers proposal][offers]. | ||
|
|
||
| 1. subtype: `blinded_payinfo` | ||
| 2. data: | ||
| * [`u32`:`fee_base_msat`] | ||
| * [`u32`:`fee_proportional_millionths`] | ||
| * [`u16`:`cltv_expiry_delta`] | ||
| * [`u64`:`htlc_minimum_msat`] | ||
| * [`u64`:`htlc_maximum_msat`] | ||
| * [`u16`:`flen`] | ||
| * [`flen*byte`:`features`] | ||
| * [`33*byte`:`first_ephemeral_blinding_point`] | ||
| * [`byte`:`num_hops`] | ||
| * [`num_hops*blinded_hop`:`blinded_hops`] | ||
|
|
||
| 1. subtype: `blinded_hop` | ||
| 2. data: | ||
| * [`33*byte`:`blinded_node_pubkey`] | ||
| * [`bigsize`: `cipher_text_length`] | ||
| * [`cipher_text_length*byte`:`cipher_text`] | ||
|
|
||
| The `blinded_node_pubkey` of the first `blinded_hop` in a `blinded_payinfo` is | ||
| the real public key of the blinded path's introduction node. This encoding was | ||
| chosen so that `num_hops` accurately reflects the true number of hops including | ||
| the introduction node while keeping the number of bytes required for the | ||
| encoding to a minimum. The `cipher_text` is the `encrypted_recipient_data` as | ||
| defined in [BOLT 4 TLV payload][bolt4-payload]. | ||
|
|
||
| The `fee_base_msat`, `fee_proportional_millionths` and `cltv_expiry_delta` are | ||
| the accumulated blinded route policy values as defined in [BOLT 4][bolt4-relay]. | ||
| `features` is the set of features for the path, `first_ephemeral_blinding_point` | ||
| is the blinding point that must be communicated to the introduction node via the | ||
| `blinding` field of the [`payload`][bolt4-payload] type. | ||
|
|
||
| ** Note: see discussion section for question around communicating | ||
| `max_cltv_expiry` ** | ||
|
|
||
| ### Requirements | ||
|
|
||
| An invoice containing the `b` field type: | ||
| - MUST not contain the `r` field type. | ||
| - MUST not contain the `s` field type since a payment address in the context of | ||
| blinded payments does not make sense since the recipient is able to use the | ||
| `path_id` in the `encrypted_recipient_data` for the same purpose. | ||
| - SHOULD sign the invoice with a private key that is not the same as their | ||
| public node ID and should not set the destination node (`n` field). | ||
| - Each `blinded_path` must fit within the `data_length` size limit. This places | ||
| an upper limit of approximately 7 `blinded_hops` on each path. See the | ||
| appendix for the estimation calculation. | ||
| - If the invoice will be displayed in QR form, then this also places an upper | ||
| limit on the number of `blinded_path` fields that can be added to the | ||
| invoice. | ||
| - The existing `c` field (`min_final_cltv_expiry_delta`) is meaningless for an | ||
| invoice containing the `b` field since this value is expected to be accounted | ||
| for in each path's accumulated `cltv_expiry_delta`. | ||
| - The existing invoice `expiry` field along with the `timestamp` field | ||
| should be used to communicate the `max_cltv_expiry` of the blinded paths in | ||
| the invoice. The reader should use the 10-minutes-per-block assumption to | ||
| calculate an estimation of the `max_cltv_exipiry` value. | ||
|
|
||
| ## Universality | ||
|
|
||
| This proposal is a temporary measure that will allow users to start making use | ||
| of blinded paths in the context of payments and thereby take advantage of the | ||
| potential privacy and payment success rate benefits that they will in theory | ||
| provide. Once the Offers protocol along with its new invoice format has been | ||
| widely deployed, then there will be no use for this BOLT 11 carve-out. Due to | ||
| the forcasted temporary use of the new field, it makes sense to be in bLIP form | ||
| rather than adding this in a more temporary way to the spec via a BOLT update | ||
| proposal. The intent is that this will be used mostly for testing of blinded | ||
| paths in implementations that have not yet implemented the full Offers spec. | ||
|
|
||
| ## Backwards Compatibility | ||
|
|
||
| BOLT 11 states that the reader of an invoice "MUST skip over unknown fields". | ||
| This means that an un-updated reader of an invoice that includes the new tagged | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Huh? So the blinded path is informational? Wouldn't the invoice instead be signed by a random key so that you get blinding?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure I understand the question?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In order for blinded paths in a BOLT 11 to be useful it has to be a required feature and we have to not reveal who the recipient node is via the existing BOLT 11 recipient fields (both the pubkey and the route hints). That is what I understood was going on, but here it says that existing senders will just ignore the blinded path fields?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agreed, if you use a blinded path, but it's optional and the invoice contains identifying information for the recipient to fallback when not using blinded paths, then that blinded path doesn't provide any privacy?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Or maybe I got this wrong: there is no routing hint anyway, so a reader that doesn't know how to read the blinded path won't have a complete enough invoice and will simply fail without attempting the payment?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Yeah there would be no routing hint, a required feature bit, and also the pubkey in the invoice would be random (not actually the node ID of the node creating the invoice). So even assuming the parsing node ignores the feature bits, they would see a node ID that appears unconnected, as it isn't found in the main public graph.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yea, okay, that's how I'd assumed it worked, can we clarify this in the bLIP though?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. added some extra info at the end - lemme know if this looks ok |
||
| field would skip it when parsing the invoice. The proposal also adds a new | ||
| feature bit to the invoice feature bit vector and so this gives nodes an | ||
| indication that the invoice includes something they do not yet understand. | ||
| Even if un-upgraded senders did not check the feature bit vector, they would not | ||
| be able to use the invoice as, without knowledge of the blinded path field, it | ||
| does not contain enough information to attempt a payment since no routing hints | ||
| will be included and the invoice will be signed with a random ephemeral key | ||
| meaning that the derived destination node would not correspond to a real node | ||
| in the graph. | ||
|
|
||
| ## Reference Implementations | ||
|
|
||
| The proposed encoding of the new BOLT 11 tagged-field is added to the LND | ||
| implementation in [this PR][impl]. | ||
|
|
||
| ## Appendix | ||
|
|
||
| ### Test Vector | ||
|
|
||
| The following string is an example of a BOLT11 invoice containing 2 blinded | ||
| paths, one with 1 hop and one with 3 hops. | ||
|
|
||
| ``` | ||
| lnbc20m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5xysxxatsyp3k7enxv4js5fdqqqqq2qqqqqpgqyzqqqqqqqqqqqqyqqqqqqqqqqqvsqqqqlnxy0ffrlt2y2jgtzw89kgr3zg4dlwtlfycn3yuek8x5eucnuchqps82xf0m2u6sx5wnjw7xxgnxz5kf09quqsv5zvkgj7d5kpzttp4qz7q5qsyqcyq5pzq2feycsemrh7wvendc4kw3tsmkt25a36ev6kfehrv7ecfkrp5zs9q5zqxqspqtr4avek5quzjn427asptzews5wrczfhychr2sq6ue9phmn35tjqcrspqgpsgpgxquyqjzstpsxsu59zqqqqqpqqqqqqyqq2qqqqqqqqqqqqqqqqqqqqqqqqpgqqqqk8t6endgpc99824amqzk9japgu8synwf3wx4qp4ej2r0h8rghypsqsygpf8ynzr8vwleenxdhzke69wrwed2nk8t9n2e8xudnm8pxcvxs2q5qsyqcyq5y4rdlhtf84f8rgdj34275juwls2ftxtcfh035863q3p9k6s94hpxhdmzfn5gxpsazdznxs56j4vt3fdhe00g9v2l3szher50hp4xlggqkxf77f | ||
| ``` | ||
|
|
||
| Breakdown: | ||
|
|
||
| This invoice was signed with `priv_key`=`e126f68f7eafcc8b74f54d269fe206be715000f94dac067d1c04a8ca3b2db734`. | ||
| It does _not_ contain a payment secret. | ||
|
|
||
| * `lnbc`: prefix, Lightning on Bitcoin mainnet | ||
| * `20m`: amount (20 milli-bitcoin) | ||
| * `1`: Bech32 separator | ||
| * `pvjluez`: timestamp (1496314658) | ||
| * `p`: payment hash (0001020304050607080900010203040506070809000102030405060708090102) | ||
| * `d`: description: '1 cup coffee' | ||
| * `b`: blinded path: | ||
| * `fee_base_msat`: 40 | ||
| * `fee_proportional_millionths`: 20 | ||
| * `cltv_expiry_delta`: 130 | ||
| * `htlc_minimum_msat`: 2 | ||
| * `htlc_maximum_msat`: 100 | ||
| * `flen`: 0 | ||
| * `first_ephemeral_blinding_point`: 03f3311e948feb5115242c4e396c81c448ab7ee5fd24c4e24e66c73533cc4f98b8 | ||
| * `num_hops`: 3 | ||
| * `blinded_hops`: | ||
| * `blinded_node_pubkey`: 03a8c97ed5cd40d474e4ef18c899854b25e5070106504cb225e6d2c112d61a805e | ||
| * `cipher_text`: 0102030405 | ||
| * `blinded_node_pubkey`: 0220293926219d8efe733336e2b674570dd96aa763acb3564e6e367b384d861a0a | ||
| * `cipher_text`: 0504030201 | ||
| * `blinded_node_pubkey`: 02c75eb336a038294eaaf760158b2e851c3c0937262e35401ae64a1bee71a2e40c | ||
| * `cipher_text`: 0102030405060708090a0b0c0d0e | ||
| * `b`: blinded path: | ||
| * `fee_base_msat`: 4 | ||
| * `fee_proportional_millionths`: 2 | ||
| * `cltv_expiry_delta`: 10 | ||
| * `htlc_minimum_msat`: 0 | ||
| * `htlc_maximum_msat`: 10 | ||
| * `flen`: 0 | ||
| * `first_ephemeral_blinding_point`: 02c75eb336a038294eaaf760158b2e851c3c0937262e35401ae64a1bee71a2e40c | ||
| * `num_hops`: 1 | ||
| * `blinded_hops`: | ||
| * `blinded_node_pubkey`: 0220293926219d8efe733336e2b674570dd96aa763acb3564e6e367b384d861a0a | ||
| * `cipher_text`: 0102030405 | ||
|
|
||
| ### Size Restrictions | ||
|
|
||
| #### `data_length` Limit | ||
|
|
||
| In order to conform to any existing BOLT 11 invoice parser, each new tagged | ||
| field must use the `data_length` encoding defined there. This means that the | ||
| maximum size of any _single_ encoded blinded path is 639 bytes. | ||
|
|
||
| What follows is a rough estimation of the maximum number of hops we can include | ||
| in a single blinded path. It assumes that each hop's cipher text is the same | ||
| length. | ||
|
|
||
| ##### Cipher Text Size Estimation | ||
|
|
||
| First, a rough estimation of the average cipher text length is required. A | ||
| forwarding node in a blinded path will receive a cipher text payload containing | ||
| the following data: | ||
|
|
||
| - `padding`: optional | ||
| - `short_channel_id` of 8 bytes | ||
| - `payment_relay`: | ||
| * 2 byte `cltv_expiry_delta` | ||
| * 4 byte `fee_proportional_millionths` | ||
| * 4 byte `fee_base_msat` | ||
| - `payment_constraints`: | ||
| * 4 byte `max_cltv_expiry` | ||
| * 8 byte `htlc_minimum_msat` | ||
| - `allowed_features`: optional | ||
|
|
||
| If we [assume that the `allowed_features` vector is not | ||
| set][empty-allowed-features], then this comes to a total of 30 mandatory bytes. | ||
|
|
||
| For the recipient node, it will receive a cipher text payload containing: | ||
|
|
||
| - `padding`: optional | ||
| - `path_id`: let's assume that this is 32 bytes like the existing payment | ||
| address. | ||
| - `payment_constraints`: | ||
| * 4 byte `max_cltv_expiry` | ||
| * 8 byte `htlc_minimum_msat` | ||
|
|
||
| This comes to a total of 44 bytes for the recipient's cipher text. | ||
|
|
||
| The padding field should be used by recipients to pad cipher text blobs so that | ||
| all are the same size. Since the calculated recipient cipher text blob size (44) | ||
| is larger than that of the forwarding nodes (30), we can assume that all the | ||
| cipher text blobs will have a size of around 44 bytes. | ||
|
|
||
| ##### `blinded_hop` Size Estimation | ||
|
|
||
| The total number of bytes required for a single `blinded_hop` is: | ||
|
|
||
| = 33+bigsize_len(cipher_text)+len(cipher_text) | ||
|
|
||
| If we use the estimated `cipher_text` size of 44 bytes, then | ||
| `bigsize_len(cipher_text)` is 1 and so this comes to 78 bytes for a single | ||
| `blinded_hop`. | ||
|
|
||
| ##### `blinded_payinfo` Size Estimation | ||
|
|
||
| The total number of bytes required for the encoding of a single | ||
| `blinded_payinfo` entry is: | ||
|
|
||
| = 4+4+2+8+8+2+len(features)+33+1+(num_hops*len(blinded_hop)) | ||
| = 68+len(features)+(num_hops*len(blinded_hop)) | ||
|
|
||
| If we take the estimate of 78 bytes per `blinded_hop` and if we assume an empty | ||
| feature vector then this comes to: | ||
|
|
||
| = 68+(num_hops*78) | ||
|
|
||
| The maximum number of hops in a single blinded path can then be calculated to | ||
| be: | ||
|
|
||
| 639 = 68+(num_hops*78) | ||
| num_hops = 7 | ||
|
|
||
| #### QR code limit | ||
|
|
||
| Another soft maximum value to keep in mind is the maximum number of bytes that | ||
| can fit into a [QR code][qr] which is 2,953 bytes. This is a soft maximum | ||
| because this only applies if the invoice is in fact being transmitted via QR | ||
| code. This limit does not apply if the invoice is being transmitted via other | ||
| protocols such as LNURL. In the cases where the limit does apply, then two | ||
| variables will be at play: | ||
|
|
||
| - The number of blinded paths | ||
| - The number of blinded hops within each path (which will always also be | ||
| restricted by the `data_length` maximum). | ||
|
|
||
| The exact limit on the number of blinded paths that can be included depends on | ||
| the size of other fields in the invoice. It is worth noting that an invoice with | ||
| a blinded path should not contain any `r` (route hint) fields. | ||
|
|
||
| [tagged-fields]: https://github.com/lightning/bolts/blob/master/11-payment-encoding.md#tagged-fields | ||
| [bolt11]: https://github.com/lightning/bolts/blob/master/11-payment-encoding.md | ||
| [bolt4-payload]: https://github.com/lightning/bolts/blob/master/04-onion-routing.md#payload-format | ||
| [bolt4-relay]: https://github.com/lightning/bolts/blob/master/04-onion-routing.md#requirements | ||
| [blinded-paths]: https://github.com/lightning/bolts/blob/master/04-onion-routing.md#route-blinding | ||
| [offers]: https://github.com/lightning/bolts/pull/798 | ||
| [impl]: https://github.com/lightningnetwork/lnd/pull/8752 | ||
| [qr]: https://en.wikipedia.org/wiki/QR_code#Information_capacity | ||
| [lnurl]: https://github.com/lnurl/luds | ||
| [rb-proposal]: https://github.com/lightning/bolts/blob/c562d91ace0e95bec3c6f8758969eaf3627f23c8/proposals/route-blinding.md?plain=1#L274 | ||
| [max_cltv_expiry]: https://github.com/lightning/bolts/pull/798/files#r1053000804 | ||
| [empty-allowed-features]: https://github.com/lightning/bolts/blob/c562d91ace0e95bec3c6f8758969eaf3627f23c8/04-onion-routing.md?plain=1#L253 | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You might want to call out that this is really just for testing and won't be broadly adopted - I don't think "temporary" means it shouldn't be in the BOLTs, but rather "not everyone can/will/should adopt this". "Temporary and most people won't adopt this cause it's for testing and everyone else already shipped offers" clearly meets that bar, "it's temporary" doesn't assuming there's some reason why everyone actually needs to adopt it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think "temporary" and "forecasted temporary use of the new field" implies this already no? Feels like writing "this wont be broadly adopted" is a bit to "hard" since we cant dictate via the spec that it wont be adopted broadly right? So here the wording implies: that this wasnt designed to be used long term but without actually making predictions re how it will be used. It does also say later on "Once the Offers protocol along with its new invoice format has been widely deployed, then there will be no use for this BOLT 11 carve-out."
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I understood this was really more for testing (ie it makes sense that its just gonna be an lnd thing cause its for lnd users to test blinded paths or for use in other contexts where everyone involved is running lnd and they know everyone supports this feature via some prenegotiation), is that not the intent?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
added a piece at the end re this being for testing - lemme know if that looks ok?