|
| 1 | +``` |
| 2 | +bLIP: 66 |
| 3 | +Title: Static Invoice Server Protocol |
| 4 | +Status: Draft |
| 5 | +Author: Valentine Wallace <vwallace@protonmail.com> |
| 6 | +Created: 2025-10-16 |
| 7 | +License: CC0 |
| 8 | +``` |
| 9 | + |
| 10 | +## Motivation |
| 11 | + |
| 12 | +To recap, under the async payments protocol either a sender or a sender's LSP will hold onto an |
| 13 | +HTLC for a payment until the often-offline recipient sends them an onion message indicating that |
| 14 | +the recipient has come online and is ready to receive the HTLC, at which point the HTLC will be |
| 15 | +released. |
| 16 | + |
| 17 | +This design presents an issue for BOLT 12, because BOLT 12 requires recipients to be |
| 18 | +online to respond to invoice requests from senders. Without an invoice from the recipient provided |
| 19 | +at pay-time, the sender cannot lock in their held HTLC to begin with. |
| 20 | + |
| 21 | +Here we define a way for a sender to get a static or payment-hash-less BOLT 12 invoice created by |
| 22 | +the often-offline recipient, even if the recipient is offline when the sender goes to pay. |
| 23 | + |
| 24 | +## Abstract |
| 25 | + |
| 26 | +This bLIP defines a protocol for always-online nodes acting as "static invoice servers" to assist |
| 27 | +often-offline lightning recipients in receiving async payments. This protocol enables mobile wallets |
| 28 | +and other frequently-offline nodes to delegate the task of responding to invoice requests from |
| 29 | +payers, while maintaining custody of funds. The aforementioned invoice servers will respond to these |
| 30 | +invoice requests with a static or payment-hash-less invoice, which can then be paid via the [async |
| 31 | +payments protocol](https://github.com/lightning/bolts/pull/1149). |
| 32 | + |
| 33 | +## Specification |
| 34 | + |
| 35 | +Four new onion messages are defined, `offer_paths_request`, `offer_paths`, `serve_static_invoice`, |
| 36 | +and `static_invoice_persisted`. |
| 37 | + |
| 38 | +1. type: 65540 (`offer_paths_request`) |
| 39 | +2. `tlv_stream`: `offer_paths_request_tlvs` |
| 40 | +3. types: |
| 41 | + 1. type: 0 (`invoice_slot`) |
| 42 | + 2. data: |
| 43 | + - [`u16`: `slot_number`] |
| 44 | + |
| 45 | +1. type: 65542 (`offer_paths`) |
| 46 | +2. `tlv_stream`: `offer_paths_tlvs` |
| 47 | +3. types: |
| 48 | + 1. type: 0 (`offer_paths`) |
| 49 | + 2. data: |
| 50 | + - [`...*blinded_path`: `paths`] |
| 51 | + 3. type: 2 (`paths_absolute_expiry`) |
| 52 | + 4. data: |
| 53 | + - [`u64`: `seconds_from_epoch`] |
| 54 | + |
| 55 | +1. type: 65544 (`serve_static_invoice`) |
| 56 | +2. `tlv_stream`: `serve_static_invoice_tlvs` |
| 57 | +3. types: |
| 58 | + 1. type: 0 (`static_invoice`) |
| 59 | + 2. data: |
| 60 | + - [`tlv_static_invoice:`: `static_inv`] |
| 61 | + 3. type: 2 (`forward_invoice_request_path`) |
| 62 | + 4. data: |
| 63 | + - [`blinded_path: forward_invreq_path`] |
| 64 | + |
| 65 | +1. type: 65546 (`static_invoice_persisted`) |
| 66 | + |
| 67 | +### Overview |
| 68 | + |
| 69 | +The overall flow of the protocol is as follows: |
| 70 | +1. Often-offline recipients are configured out-of-band with blinded paths to reach the static |
| 71 | + invoice server, which allows the server to authenticate the `offer_paths_request` |
| 72 | +2. Recipient sends `offer_paths_request` to request offer paths from the static invoice server |
| 73 | +3. Server responds with `offer_paths` containing blinded paths that payers will eventually use to |
| 74 | + send invoice requests to the server |
| 75 | +4. Recipient creates an offer containing those blinded paths from the server, and also creates a |
| 76 | + corresponding static invoice |
| 77 | +5. Recipient sends `serve_static_invoice` containing the static invoice they just created |
| 78 | +6. Server receives the static invoice, persists it, and responds with `static_invoice_persisted` to |
| 79 | + confirm the offer is ready to receive async payments |
| 80 | +7. If the server later receives an invoice request on the recipient's behalf, they respond with the |
| 81 | + previously persisted static invoice and also forward the invoice request to the recipient if they |
| 82 | + are online |
| 83 | + |
| 84 | +### Requirements: |
| 85 | + |
| 86 | +Static invoice server nodes which have an out-of-band relationship with an often-offline node, e.g. |
| 87 | +a mobile lightning node: |
| 88 | +* SHOULD provide at least 1 blinded path out-of-band to the often-offline node that the |
| 89 | + often-offline node will send `offer_paths_request`s over |
| 90 | + |
| 91 | +Senders of `offer_paths_request`: |
| 92 | +* MUST set `reply_path` in the `onionmsg_tlv` stream |
| 93 | +* MAY include the `invoice_slot` in the `reply_path` to remember which slot the resulting offer |
| 94 | + corresponds to and replace the corresponding invoice in the future |
| 95 | + |
| 96 | +Recipients of `offer_paths_request`: |
| 97 | +* MUST authenticate the request using encrypted data from the blinded path they received the request |
| 98 | + over |
| 99 | +* SHOULD respond with an `offer_paths` message if authentication succeeds |
| 100 | + |
| 101 | +Senders of `offer_paths`: |
| 102 | +* MUST include at least 1 blinded path that terminates at their node in the `offer_paths` message, |
| 103 | + that payers will use in the future when sending `invoice_request`s for static invoices |
| 104 | +* MUST construct the blinded offer path encrypted data such that the static invoice can be uniquely |
| 105 | + identified and retrieved in the future in response to invoice requests. E.g., include an |
| 106 | + identifier for the recipient as well as the `invoice_slot` from the preceding |
| 107 | + `offer_paths_request` |
| 108 | +* MUST set `reply_path` in the `onionmsg_tlv` stream |
| 109 | +* MUST construct the reply path encrypted data such that the recipient can reuse the reply path in |
| 110 | + the future to update the invoice, e.g. by including a recipient identifier and the `invoice_slot` |
| 111 | + from the preceding `offer_paths_request` |
| 112 | +* MAY set `paths_absolute_expiry` to indicate the absolute time when the included blinded offer |
| 113 | + path(s) expire and can no longer be used |
| 114 | + |
| 115 | +Recipients of `offer_paths`: |
| 116 | +* MUST authenticate the message using encrypted data from the blinded reply path they received the |
| 117 | + `offer_paths` message over |
| 118 | +* SHOULD check that `paths_absolute_expiry` is not too soon for the offer paths to be usable |
| 119 | +* SHOULD respond with a `serve_static_invoice` message using the provided `reply_path` after |
| 120 | + building an offer using the provided path(s) and corresponding static invoice |
| 121 | +* SHOULD persist the reply path to the `offer_paths` message, which can be used later to update the |
| 122 | + static invoice |
| 123 | + |
| 124 | +Senders of `serve_static_invoice`: |
| 125 | +* When creating the offer corresponding to the static invoice, MUST use blinded paths from the |
| 126 | + preceding `offer_paths` message |
| 127 | +* MUST set `forward_invoice_request_path` to a blinded path that the invoice server can use to |
| 128 | + forward invoice requests from payers to this recipient's node |
| 129 | +* MUST set `reply_path` in the `onionmsg_tlv` stream |
| 130 | + |
| 131 | +Recipients of `serve_static_invoice`: |
| 132 | +* MUST authenticate the message using encrypted data from the blinded reply path they received the |
| 133 | + `serve_static_invoice` message over |
| 134 | +* If the `invoice_slot` in the encrypted data from the blinded path they received this message over |
| 135 | + corresponds to an existing invoice, SHOULD replace that invoice with this one and stop serving the |
| 136 | + previous invoice |
| 137 | +* SHOULD limit the amount of storage they will dedicate to persisting invoices for a particular |
| 138 | + client |
| 139 | +* SHOULD persist the `static_invoice` and `forward_invoice_request_path` and then respond with |
| 140 | + `static_invoice_persisted` using the provided reply path |
| 141 | + |
| 142 | +Senders of `static_invoice_persisted`: |
| 143 | +* SHOULD wait until the invoice is confirmed as persisted before sending this message |
| 144 | + |
| 145 | +Recipients of `static_invoice_persisted`: |
| 146 | +* MUST authenticate the message using encrypted data from the blinded reply path they received the |
| 147 | + `static_invoice_persisted` message over |
| 148 | +* SHOULD mark the offer corresponding to the invoice as ready to receive async payments |
| 149 | +* SHOULD update the persisted invoice periodically or when channels close/fees change, using the |
| 150 | + previously persisted reply path to the preceding `offer_paths` message |
| 151 | + |
| 152 | +Static invoice servers: |
| 153 | +* If an invoice request comes in corresponding to a static invoice that was previously persisted with |
| 154 | +them: |
| 155 | + * SHOULD retrieve the persisted invoice and respond to the payer with it |
| 156 | + * SHOULD forward the invoice request to the recipient over the previously provided |
| 157 | + `forward_invoice_request_path`, and awake them if possible, such as via the LSPS5 protocol |
| 158 | + |
| 159 | + |
| 160 | +## Rationale |
| 161 | + |
| 162 | +* Q: If we're configuring the recipient with blinded paths from the static invoice server, but those |
| 163 | + paths are used to retrieve offer blinded paths from the server, why not just configure the |
| 164 | + recipient with the offer blinded paths to begin with? |
| 165 | +* A: We want the recipient to have a variety of offers containing unique blinded paths to choose |
| 166 | + from, for privacy reasons. They shouldn't have to reuse the same offer paths in all situations. |
| 167 | + Implementations should aim to rotate the cached offers that are returned to the user. |
| 168 | + |
| 169 | +* Q: Why use blinded paths for server<>recipient authentication? |
| 170 | +* A: The protocol is based on BOLT 12 so route blinding is known to be a supported mechanism by both |
| 171 | + parties already. It also allows the sender and recipient to not be direct [channel] peers, if that |
| 172 | + flexibility is desired. |
| 173 | + |
| 174 | + |
| 175 | +## Universality |
| 176 | + |
| 177 | +For async payments to work with BOLT 12, we need some way for senders to get invoices from |
| 178 | +often-offline recipients when they go to pay, that is self-custodial and ideally as trustless as |
| 179 | +possible. This protocol provides a way, that is implemented in LDK, but wallets may have their own |
| 180 | +way of accomplishing this. Therefore, it doesn't need to be universal in the BOLTs. |
| 181 | + |
| 182 | +## Reference Implementations |
| 183 | +LDK: [1](https://github.com/lightningdevkit/rust-lightning/pull/3618) [2](https://github.com/lightningdevkit/rust-lightning/pull/3628) [3](https://github.com/lightningdevkit/rust-lightning/pull/4049) |
| 184 | + |
| 185 | +## Copyright |
| 186 | + |
| 187 | +This bLIP is licensed under the CC0 license. |
0 commit comments