diff --git a/04-onion-routing.md b/04-onion-routing.md index 19864ea72..671016456 100644 --- a/04-onion-routing.md +++ b/04-onion-routing.md @@ -213,6 +213,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. @@ -239,6 +242,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): @@ -269,6 +275,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`. @@ -319,6 +331,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 `blinding_point` is set in the incoming `update_add_htlc` or `current_blinding_point` is present. - MUST return an error if `amt_to_forward` or `outgoing_cltv_value` are not present. diff --git a/09-features.md b/09-features.md index 7fdabe068..8ed7ff2b1 100644 --- a/09-features.md +++ b/09-features.md @@ -27,6 +27,7 @@ The Context column decodes as follows: * `C+`: presented in the `channel_announcement` message, but always even (required). * `9`: presented in [BOLT 11](11-payment-encoding.md) invoices. * `B`: presented in the `allowed_features` field of a blinded path. +* `R`: presented in [BOLT 12](12-offers.md) invoice requests. | Bits | Name | Description | Context | Dependencies | Link | |-------|-----------------------------------|-----------------------------------------------------------|----------|---------------------------|-----------------------------------------------------------------------| @@ -51,11 +52,17 @@ The Context column decodes as follows: | 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 | IN | `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` | +| 56/57 | `option_om_mailbox` | Store-and-forward onion messages for often-offline peers | IN | `option_onion_messages` | [BOLT #12](bolt12-offers.md) | +| 59 | `static_invoice_pay` | Supports paying BOLT 12 static invoices | R | `option_onion_messages` | [BOLT #12](bolt12-offers.md) | ## Definitions We define `option_anchors` as `option_anchor_outputs || option_anchors_zero_fee_htlc_tx`. +We define `option_om_mailbox` as the ability to store an onion message on behalf +of an offline peer, and forward it once the peer comes online (subject to rate +limiting). + ## Requirements The origin node: diff --git a/12-offer-encoding.md b/12-offer-encoding.md index 638f48bf7..74f8d61de 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. @@ -59,6 +59,19 @@ 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 if the mobile user + is offline. If they are online, the `invoice_request` is forwarded to the mobile + user as usual. +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 @@ -71,6 +84,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 @@ -255,8 +271,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`: @@ -274,6 +291,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 greater or equal to 80: @@ -409,6 +428,20 @@ for [Signature Calculation](#signature-calculation). 2. data: * [`bip340sig`:`sig`] +## Invoice Request Features + +| Bits | Description | Name | +|------|----------------------------------|-----------------------------| +| 59 | Supports paying static invoices | static_invoice_pay/optional | + +Setting `static_invoice_pay` indicates that the payer supports receiving a +`payment_hash`-less invoice in response to their `invoice_request`, and +subsequently setting `sender_provided_payment_preimage` in their payment onion. + +Useful if the payee is often offline and the invoice is being returned on +their behalf by another node, to avoid trusting that other node to not reuse a +`payment_hash`. + ## Requirements for Invoice Requests The writer: @@ -486,7 +519,14 @@ The reader: - MUST fail the request if bitcoin is not a supported chain. - otherwise: - MUST fail the request if `invreq_chain`.`chain` is not a supported chain. - + - if receiving the `invoice_request` on behalf of an often-offline payee: + - if the payee is online: + - MUST forward the `invoice_request` to the payee + - otherwise (payee is offline): + - if `invreq_features` supports `static_invoice_pay`: + - MUST reply with the static invoice previously provided by the payee + - otherwise: + - MUST reply with `invoice_error` ## Rationale @@ -515,10 +555,11 @@ informative for the payer to know how the sender claims # Invoices -Invoices are a payment request, and when the payment is made, -it 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` @@ -604,6 +645,9 @@ the `onion_message` `invoice` field. 1. type: 176 (`invoice_node_id`) 2. data: * [`point`:`node_id`] + 1. type: 178 (`invoice_message_paths`) + 2. data: + * [`...*blinded_path`:`paths`] 1. type: 240 (`signature`) 2. data: * [`bip340sig`:`sig`] @@ -642,21 +686,25 @@ 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*. - - otherwise (invoice not requested, e.g. for user to scan directly): + - otherwise if the invoice was not requested (e.g. for user to scan directly): - MUST set `invreq_chain` as it would for an invoice_request. - MUST set `offer_description` as it would for an offer. - MUST NOT set `invreq_payer_id` or `offer_node_id`. - - MUST set `invoice_payment_hash` to the SHA256 hash of the - `payment_preimage` that will be given in return for payment. + - if the invoice is intended to be provided by a node other than the recipient: + - MUST NOT set `invoice_payment_hash`. + - MUST NOT set `invoice_amount`. + - MUST include `invoice_message_paths` containing at least two paths to + the recipient, where the penultimate hop supports `option_om_mailbox`. + - MUST NOT set any `invoice_request` TLV fields (i.e. 0 or in the 80-159 range) + - otherwise: + - MUST set `invoice_payment_hash` to the SHA256 hash of the + `payment_preimage` that will be given in return for payment. - if `offer_node_id` is present: - MUST set `invoice_node_id` to `offer_node_id`. - otherwise: @@ -682,15 +730,16 @@ 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 `offer_node_id` is present, and `invreq_payer_id` is identical to a previous `invoice_request`: - - MAY simply reuse the previous invoice. + - if `offer_node_id` is present, and `invreq_payer_id` is identical to a previous `invoice_request`, or if providing invoices on behalf of an often offline recipient: + - MAY reuse the previous invoice. - otherwise: - MUST NOT reuse a 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. + - if `static_invoice_pay` was not supported in `invreq_features`: + - MUST reject the invoice if `invoice_payment_hash` is not present. + - MUST reject the invoice if `invoice_amount` is not present. - MUST reject the invoice if `invoice_node_id` is not present. - if `invoice_features` contains unknown _odd_ bits that are non-zero: - MUST ignore the bit. @@ -707,7 +756,8 @@ 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 less than type 160 do not exactly match the `invoice_request`. + - if `invoice_payment_hash` is set: + - MUST reject the invoice if all fields less than type 160 do not exactly match the `invoice_request`. - if `offer_node_id` is present (invoice_request for an offer): - MUST reject the invoice if `invoice_node_id` is not equal to `offer_node_id`. - otherwise (invoice_request without an offer): @@ -727,6 +777,10 @@ A reader of an invoice: - MUST ignore any `fallback_address` for which `version` is greater than 16. - MUST ignore any `fallback_address` for which `address` is less than 2 or greater than 40 bytes. - MUST ignore any `fallback_address` for which `address` does not meet known requirements for the given `version` + - if `invoice_payment_hash` is unset: + - MUST reject the invoice if `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 `invoice_message_paths`. ## Rationale