From d50a98b6f2a96a1d595efe63cabd9f469819e899 Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Thu, 12 May 2022 22:52:25 +0000 Subject: [PATCH 1/4] Add an init/node feature for HTLC holding --- 09-features.md | 1 + 1 file changed, 1 insertion(+) diff --git a/09-features.md b/09-features.md index e7a8cbed6..e9e57728c 100644 --- a/09-features.md +++ b/09-features.md @@ -53,6 +53,7 @@ The Context column decodes as follows: | 46/47 | `option_scid_alias` | Supply channel aliases for routing | INT | | [BOLT #2][bolt02-channel-ready] | | 48/49 | `option_payment_metadata` | Payment metadata in tlv record | 9 | | [BOLT #11](11-payment-encoding.md#tagged-fields) | | 50/51 | `option_zeroconf` | Understands zeroconf channel types | INT | `option_scid_alias` | [BOLT #2][bolt02-channel-ready] | +| 52/53 | `option_htlc_hold` | Hold HTLCs and forward on receipt of an onion message | IN | `option_onion_messages` | | 60/61 | `option_simple_close` | Simplified closing negotiation | IN | `option_shutdown_anysegwit` | [BOLT #2][bolt02-simple-close] | ## Requirements From c7aafa336f511ab4ef058c7f96d48ab513e35267 Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Fri, 13 May 2022 00:18:49 +0000 Subject: [PATCH 2/4] Add the ability to hold HTLCs before forwarding This is an incredibly simple first start towards the protocol sketched out at [1]. It adds the ability to have a counteraprty hold an HTLC before forwarding it. Specifically, the HTLC sender sets a required TLV on the `update_add_htlc` message and sends an onion message to the final recipient. From there, the final recipient uses the included `reply_path` to notify the sender's counterparty that they're online and ready to receive the HTLC. In order to fully flesh out the protocol as sketched, we'll need to add an onion message mailbox service (which should be as simple as a new feature bit), add PTLCs, and extensions to BOLT 12 to allow signaling that the final recipient is often-offline. While we could add such signaling to BOLT 11, there's not a whole lot of reason to - if the recipient is able to provide an invoice, they're currently online! [1] https://lists.linuxfoundation.org/pipermail/lightning-dev/2021-October/003307.html --- 02-peer-protocol.md | 52 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/02-peer-protocol.md b/02-peer-protocol.md index 009555d0c..0c1a8a059 100644 --- a/02-peer-protocol.md +++ b/02-peer-protocol.md @@ -2222,6 +2222,17 @@ is destined, is described in [BOLT #4](04-onion-routing.md). 1. type: 0 (`blinded_path`) 2. data: * [`point`:`path_key`] + 3. type: 2 (`hold_htlc`) + 4. data: + +#### TLV fields for `held_htlc_available` + 1. `tlv_stream`: `held_htlc_available` + 2. types: + +#### TLV fields for `release_held_htlc` + + 1. `tlv_stream`: `release_held_htlc` + 2. types: #### Requirements @@ -2260,6 +2271,10 @@ A sending node: - MUST increase the value of `id` by 1 for each successive offer. - if it is relaying a payment inside a blinded route: - MUST set `path_key` (see [Route Blinding](04-onion-routing.md#route-blinding)) + - MUST NOT include a `hold_htlc` TLV unless the sending node expects the + final recipient of the HTLC to be offline at the time the HTLC would arrive + - MUST NOT include a `hold_htlc` TLV unless the sending node expects to be + offline for an extended duration starting soon. `id` MUST NOT be reset to 0 after the update is complete (i.e. after `revoke_and_ack` has been received). It MUST continue incrementing instead. @@ -2291,6 +2306,11 @@ A receiving node: - MUST respond with an error as detailed in [Failure Messages](04-onion-routing.md#failure-messages) - Otherwise: - MUST follow the requirements for the reader of `payload` in [Payload Format](04-onion-routing.md#payload-format) + - if the `hold_htlc` TLV is present: + - MUST NOT forward the HTLC until a corresponding `release_held_htlc` onion + message is received. + - Upon receipt of a corresponding `release_held_htlc` onion message the HTLC SHOULD be treated + as any HTLC without the `hold_htlc` TLV and forwarded as usual. The `onion_routing_packet` contains an obfuscated list of hops and instructions for each hop along the path. It commits to the HTLC by setting the `payment_hash` as associated data, i.e. includes the `payment_hash` in the computation of HMACs. @@ -2326,6 +2346,19 @@ maintaining its channel reserve (because of the increased weight of the commitment transaction), resulting in a degraded channel. See [#728](https://github.com/lightningnetwork/lightning-rfc/issues/728) for more details. +For often-offline recipients, e.g. mobile clients, nodes can use the +`hold_htlc` TLV to prevent further forwarding of an HTLC until the recipient +comes online. As long as the final recipients' counterparty is online and +storing onion messages for the recipient, the recipient can reply to the onion +message when they come online, unblock the HTLC, and expect to receive it +quickly thereafter. + +Note that if the sender expects to be online when the recipient comes online, +they can utilize the `release_held_htlc` onion message without utilizing the +`hold_htlc` TLV - they can simply send a `held_htlc_available` onion message +to the final recipient and wait to send any HTLC at all until they receive a +`release_held_htlc` message back. + ### Removing an HTLC: `update_fulfill_htlc`, `update_fail_htlc`, and `update_fail_malformed_htlc` For simplicity, a node can only remove HTLCs added by the other node. @@ -2511,6 +2544,13 @@ The description of key derivation is in [BOLT #3](03-transactions.md#key-derivat * [`channel_id`:`channel_id`] * [`32*byte`:`per_commitment_secret`] * [`point`:`next_per_commitment_point`] + * [`revoke_and_ack_tlvs`:`tlvs`] + +1. `tlv_stream`: `revoke_and_ack_tlvs` +2. types: + 1. type: 0 (`release_held_htlc_message_paths`) + 2. data: + * [`...blinded_path`:`paths`] #### Requirements @@ -2519,6 +2559,10 @@ A sending node: the previous commitment transaction. - MUST set `next_per_commitment_point` to the values for its next commitment transaction. + - If `hold_htlc` was set in the preceding `update_add_htlc` and `option_htlc_hold` was advertised + by this node: + - MUST set `release_held_htlc_message_paths` to at least 1 blinded path that can be used for + the ultimate payment recipient to eventually send `release_held_htlc` onion messages over A receiving node: - if `per_commitment_secret` is not a valid secret key or does not generate the previous `per_commitment_point`: @@ -2526,6 +2570,14 @@ A receiving node: - if the `per_commitment_secret` was not generated by the protocol in [BOLT #3](03-transactions.md#per-commitment-secret-requirements): - MAY send a `warning` and close the connection, or send an `error` and fail the channel. + - If `release_held_htlc_message_paths` are present and `hold_htlc` was set in the preceding `update_add_htlc`: + - MUST immediately send at least two onion messages across at least two + different paths to the final HTLC recipient. + - Each onion message MUST contain a `held_htlc_available` TLV. + - Each onion message MUST contain a unique `reply_path`s which terminates + at the receiver of the preceding `update_add_htlc` message. + - Each `reply_path` MUST contain a `release_held_htlc` TLV for the + `update_add_htlc` recipient in the `encrypted_data_tlvs` A node: - MUST NOT broadcast old (revoked) commitment transactions, From d706683ad599ff5bccf7ada100ca6e7453522071 Mon Sep 17 00:00:00 2001 From: Valentine Wallace Date: Mon, 4 Mar 2024 10:31:20 -0500 Subject: [PATCH 3/4] Support async payments in BOLT 12. This builds on prior commits by adding the ability to fetch an invoice from an always-online node on behalf of an often-offline recipient, e.g. a mobile node. The idea is that often-offline recipients will supply some always-online node such as their wallet vendor with a static (i.e. `payment_hash`-less) invoice to return on its behalf. The recipient will then publish an offer containing blinded paths that terminate at this always-online node, who payers can request the invoice from if the recipient is offline at the time. After receiving the static invoice, payers will commence the protocol outlined in [1] to send the HTLC asynchronously. [1]: https://lists.linuxfoundation.org/pipermail/lightning-dev/2021-October/003307.html --- 04-onion-routing.md | 16 +++++++++ 12-offer-encoding.md | 77 +++++++++++++++++++++++++++++++++----------- 2 files changed, 74 insertions(+), 19 deletions(-) diff --git a/04-onion-routing.md b/04-onion-routing.md index d61542c6f..c0f400c5d 100644 --- a/04-onion-routing.md +++ b/04-onion-routing.md @@ -214,6 +214,9 @@ This is formatted according to the Type-Length-Value format defined in [BOLT #1] 1. type: 18 (`total_amount_msat`) 2. data: * [`tu64`:`total_msat`] + 1. type: 5482373484 (`sender_provided_payment_preimage`) + 2. data: + * [`32*byte`:`payment_preimage`] `short_channel_id` is the ID of the outgoing channel used to route the message; the receiving peer should operate the other end of this channel. @@ -240,6 +243,9 @@ The requirements ensure consistency in responding to an unexpected `outgoing_cltv_value`, whether it is the final node or not, to avoid leaking its position in the route. +`sender_provided_payment_preimage` is set in the case that the recipient is +often-offline and another node provided a static BOLT 12 invoice on their behalf. + ### Requirements The creator of `encrypted_recipient_data` (usually, the recipient of payment): @@ -273,6 +279,12 @@ The writer of the TLV `payload`: - MUST use the current block height as a baseline value. - if a [random offset](07-routing-gossip.md#recommendations-for-routing) was added to improve privacy: - SHOULD add the offset to the baseline value. + - if paying to a static BOLT 12 invoice: + - MUST set `sender_provided_payment_preimage` to randomly generated unique bytes. + - MUST set `update_add_htlc.payment_hash` to match the SHA256 hash of + `sender_provided_payment_preimage`. + - otherwise: + - MUST NOT set `sender_provided_payment_preimage`. - MUST NOT include any other tlv field. - For every node outside of a blinded route: - MUST include `amt_to_forward` and `outgoing_cltv_value`. @@ -324,6 +336,7 @@ The reader: - MUST return an error if `amt_to_forward` is below what it expects for the payment. - MUST return an error if incoming `cltv_expiry` < `outgoing_cltv_value`. - MUST return an error if incoming `cltv_expiry` < `current_block_height` + `min_final_cltv_expiry_delta`. + - MUST use `sender_provided_payment_preimage` when claiming the HTLC, if present - Otherwise (it is not part of a blinded route): - MUST return an error if `path_key` is set in the incoming `update_add_htlc` or `current_path_key` is present. - MUST return an error if `amt_to_forward` or `outgoing_cltv_value` are not present. @@ -1519,6 +1532,9 @@ even, of course!). 1. type: 68 (`invoice_error`) 2. data: * [`tlv_invoice_error`:`inverr`] + 1. type: 70 (`static_invoice`) + 2. data: + * [`tlv_static_invoice`:`static_inv`] #### Requirements diff --git a/12-offer-encoding.md b/12-offer-encoding.md index db920f5a1..612845a56 100644 --- a/12-offer-encoding.md +++ b/12-offer-encoding.md @@ -42,7 +42,7 @@ Here we use "user" as shorthand for the individual user's lightning node and "merchant" as the shorthand for the node of someone who is selling or has sold something. -There are two basic payment flows supported by BOLT 12: +There are three basic payment flows supported by BOLT 12: The general user-pays-merchant flow is: 1. A merchant publishes an *offer*, such as on a web page or a QR code. @@ -58,6 +58,18 @@ The merchant-pays-user flow (e.g. ATM or refund): 3. The merchant confirms the *invoice_node_id* to ensure it's about to pay the correct person, and makes a payment to the invoice. +The pay-mobile-user flow (e.g. paying a friend back to their mobile node): +1. The mobile user supplies some always-online node with a static (i.e. + `payment_hash`-less) invoice to return on its behalf. This always-online node may + be the mobile user's channel counterparty, wallet vendor, or another node on the + network that it has an out-of-band relationship with. +2. The mobile user publishes an offer that contains blinded paths that terminate + at the always-online node. +3. The payer sends an `invoice_request` to the always-online node, who replies with the static + invoice previously provided by the mobile user and forwards the `invoice_request` to the mobile + user in case they happen to be online. +4. The payer makes a payment to the mobile user as indicated by the invoice. + ## Payment Proofs and Payer Proofs Note that the normal lightning "proof of payment" can only demonstrate that an @@ -70,6 +82,9 @@ to request the invoice. In addition, the Merkle construction of the BOLT 12 invoice signature allows the user to reveal invoice fields in case of a dispute selectively. +Payers will not get proofs in the case that they received a static invoice from the +payee, see the pay-mobile-user flow above. + # Encoding Each of the forms documented here are in @@ -126,7 +141,7 @@ Each form is signed using one or more *signature TLV elements*: TLV types 240 through 1000 (inclusive). For these, the tag is "lightning" || `messagename` || `fieldname`, and `msg` is the Merkle-root; "lightning" is the literal 9-byte ASCII string, -`messagename` is the name of the TLV stream being signed (i.e. "invoice_request" or "invoice") and the `fieldname` is the TLV field containing the +`messagename` is the name of the TLV stream being signed (i.e. "invoice_request", "invoice", or "static_invoice") and the `fieldname` is the TLV field containing the signature (e.g. "signature"). The formulation of the Merkle tree is similar to that proposed in @@ -261,8 +276,9 @@ A writer of an offer: after midnight 1 January 1970, UTC that invoice_request should not be attempted. - if it is connected only by private channels: - - MUST include `offer_paths` containing one or more paths to the node from - publicly reachable nodes. + - MUST include `offer_paths` containing one or more paths to the node + that will reply to the `invoice_request`, using introduction nodes that are + publicly reachable. - otherwise: - MAY include `offer_paths`. - if it includes `offer_paths`: @@ -282,6 +298,8 @@ A writer of an offer: - MUST set `offer_quantity_max` to 0. - otherwise: - MUST NOT set `offer_quantity_max`. + - if it is often-offline and the invoice may be provided by another node on their behalf: + - MUST NOT include more than 1 chain in `offer_chains`. A reader of an offer: - if the offer contains any TLV fields outside the inclusive ranges: 1 to 79 and 1000000000 to 1999999999: @@ -543,6 +561,9 @@ The reader: - if `invreq_bip_353_name` is present: - MUST reject the invoice request if `name` or `domain` contain any bytes which are not `0`-`9`, `a`-`z`, `A`-`Z`, `-`, `_` or `.`. + - if receiving the `invoice_request` on behalf of an often-offline payee: + - SHOULD forward the `invoice_request` to the payee + - SHOULD reply with the static invoice previously provided by the payee ## Rationale @@ -573,10 +594,11 @@ The requirement to use `offer_paths` if present, ensures a node does not reveal # Invoices -Invoices are a payment request, and when the payment is made, -the payment preimage can be combined with the invoice to form a cryptographic receipt. +Invoices are a payment request. If `invoice_payment_hash` is set, then when the +payment is made, the payment preimage can be combined with the invoice to form a +cryptographic receipt. -The recipient sends an `invoice` in response to an `invoice_request` using +The recipient creates an `invoice` for responding to an `invoice_request` using the `onion_message` `invoice` field. 1. `tlv_stream`: `invoice` @@ -671,6 +693,9 @@ the `onion_message` `invoice` field. 1. type: 176 (`invoice_node_id`) 2. data: * [`point`:`node_id`] + 1. type: 178 (`static_invoice_message_paths`) + 2. data: + * [`...*blinded_path`:`paths`] 1. type: 240 (`signature`) 2. data: * [`bip340sig`:`sig`] @@ -709,17 +734,28 @@ may (due to capacity limits on a single channel) require it. A writer of an invoice: - MUST set `invoice_created_at` to the number of seconds since Midnight 1 January 1970, UTC when the invoice was created. - - MUST set `invoice_amount` to the minimum amount it will accept, in units of - the minimal lightning-payable unit (e.g. milli-satoshis for bitcoin) for - `invreq_chain`. - - if the invoice is in response to an `invoice_request`: + - if `invoice_payment_hash` is set and the invoice is in response to an `invoice_request`: - MUST copy all non-signature fields from the invoice request (including unknown fields). - if `invreq_amount` is present: - MUST set `invoice_amount` to `invreq_amount` - otherwise: - MUST set `invoice_amount` to the *expected amount*. - - MUST set `invoice_payment_hash` to the SHA256 hash of the - `payment_preimage` that will be given in return for payment. + - if the expiry for accepting payment is not 7200 seconds after `invoice_created_at`: + - MUST set `invoice_relative_expiry`.`seconds_from_creation` to the number of + seconds after `invoice_created_at` that payment of this invoice should not be attempted. + - if the invoice is intended to be provided by a node other than the recipient (i.e. a static + invoice): + - MUST NOT set `invoice_payment_hash`. + - MUST NOT set `invoice_amount`. + - MUST include `static_invoice_message_paths` containing at least two paths to + the recipient. + - MUST NOT set any `invoice_request` TLV fields + - if the expiry for accepting payment is not 2 weeks after `invoice_created_at`: + - MUST set `invoice_relative_expiry`.`seconds_from_creation` to the number of + seconds after `invoice_created_at` that payment of this invoice should not be attempted. + - otherwise: + - MUST set `invoice_payment_hash` to the SHA256 hash of the + `payment_preimage` that will be given in return for payment. - if `offer_issuer_id` is present: - MUST set `invoice_node_id` to the `offer_issuer_id` - otherwise, if `offer_paths` is present: @@ -730,9 +766,6 @@ A writer of an invoice: - MUST set `invoice_features`.`features` bit `MPP/compulsory` - or if it allows multiple parts to pay the invoice: - MUST set `invoice_features`.`features` bit `MPP/optional` - - if the expiry for accepting payment is not 7200 seconds after `invoice_created_at`: - - MUST set `invoice_relative_expiry`.`seconds_from_creation` to the number of - seconds after `invoice_created_at` that payment of this invoice should not be attempted. - if it accepts onchain payments: - MAY specify `invoice_fallbacks` - SHOULD specify `invoice_fallbacks` in order of most-preferred to least-preferred @@ -745,11 +778,11 @@ A writer of an invoice: - MUST include `invoice_blindedpay` with exactly one `blinded_payinfo` for each `blinded_path` in `paths`, in order. - MUST set `features` in each `blinded_payinfo` to match `encrypted_data_tlv`.`allowed_features` (or empty, if no `allowed_features`). - SHOULD ignore any payment which does not use one of the paths. + - if providing invoices on behalf of an often offline recipient: + - MAY reuse the previous invoice. A reader of an invoice: - - MUST reject the invoice if `invoice_amount` is not present. - MUST reject the invoice if `invoice_created_at` is not present. - - MUST reject the invoice if `invoice_payment_hash` is not present. - MUST reject the invoice if `invoice_node_id` is not present. - if `invreq_chain` is not present: - MUST reject the invoice if bitcoin is not a supported chain. @@ -771,7 +804,9 @@ A reader of an invoice: - MUST NOT use the corresponding `invoice_paths`.`path` if `payinfo`.`features` has any unknown even bits set. - MUST reject the invoice if this leaves no usable paths. - if the invoice is a response to an `invoice_request`: - - MUST reject the invoice if all fields in ranges 0 to 159 and 1000000000 to 2999999999 (inclusive) do not exactly match the invoice request. + - if `invoice_payment_hash` is set: + - MUST reject the invoice if `invoice_amount` is not present. + - MUST reject the invoice if all fields in ranges 0 to 159 and 1000000000 to 2999999999 (inclusive) do not exactly match the invoice request. - if `offer_issuer_id` is present (invoice_request for an offer): - MUST reject the invoice if `invoice_node_id` is not equal to `offer_issuer_id` - otherwise, if `offer_paths` is present (invoice_request for an offer without id): @@ -800,6 +835,10 @@ A reader of an invoice: - MUST reject the invoice if it arrived via a blinded path. - otherwise (derived from an offer): - MUST reject the invoice if it did not arrive via invoice request `onionmsg_tlv` `reply_path`. + - if `invoice_payment_hash` is unset: + - MUST reject the invoice if `static_invoice_message_paths` is not present or is empty. + - MUST pay asynchronously using the `held_htlc_available` onion message + flow, where the onion message is sent over `static_invoice_message_paths`. ## Rationale From 3a7505e0fee9941b0c866b15e42ca4ac375ad6fe Mon Sep 17 00:00:00 2001 From: Valentine Wallace Date: Fri, 15 Mar 2024 13:24:04 -0400 Subject: [PATCH 4/4] Require repeating an invoice_request in async payment onions This field may be useful for often-offline recipients who did not receive the invreq when it was originally sent, due to another node providing a static invoice on their behalf. Recipients may want to verify the invreq or be provided some other relevant data about the payment while remaining stateless until an HTLC is actually received. --- 04-onion-routing.md | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/04-onion-routing.md b/04-onion-routing.md index c0f400c5d..6876513a1 100644 --- a/04-onion-routing.md +++ b/04-onion-routing.md @@ -189,6 +189,9 @@ This is formatted according to the Type-Length-Value format defined in [BOLT #1] 1. `tlv_stream`: `payload` 2. types: + 1. type: 1 (`invoice_request`) + 2. data: + * [`...*byte`:`invoice_request_tlv_stream`] 1. type: 2 (`amt_to_forward`) 2. data: * [`tu64`:`amt_to_forward`] @@ -243,8 +246,10 @@ The requirements ensure consistency in responding to an unexpected `outgoing_cltv_value`, whether it is the final node or not, to avoid leaking its position in the route. -`sender_provided_payment_preimage` is set in the case that the recipient is -often-offline and another node provided a static BOLT 12 invoice on their behalf. +`sender_provided_payment_preimage` and `invoice_request` are set in the case +that the recipient is often-offline and another node provided a static BOLT 12 +invoice on their behalf, where `invoice_request` is the sender's originl +invoice request corresponding to this HTLC. ### Requirements @@ -283,6 +288,8 @@ The writer of the TLV `payload`: - MUST set `sender_provided_payment_preimage` to randomly generated unique bytes. - MUST set `update_add_htlc.payment_hash` to match the SHA256 hash of `sender_provided_payment_preimage`. + - MUST set `invoice_request` to the BOLT 12 invoice request + corresponding to this HTLC. - otherwise: - MUST NOT set `sender_provided_payment_preimage`. - MUST NOT include any other tlv field.