Skip to content

Commit cb8228c

Browse files
Define bLIP-0066, static invoice server protocol
1 parent a833e7b commit cb8228c

File tree

3 files changed

+192
-0
lines changed

3 files changed

+192
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,4 @@ For more detail on the process, please read [bLIP-0001](./blip-0001.md) and
3232
| [51](./blip-0051.md) | LSPS1: Channel Requests | Severin Bühler | Active |
3333
| [52](./blip-0052.md) | LSPS2: JIT Channel Negotiation | ZmnSCPxj jxPCSnmZ | Active |
3434
| [55](./blip-0055.md) | LSPS5: Webhook Registration | ZmnSCPxj jxPCSnmZ | Active |
35+
| [66](./blip-0066.md) | Static Invoice Server Protocol | Valentine Wallace | Active |

blip-0002.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,10 @@ The following table contains tlv fields for use in onion messages as the payload
132132
|-------|-----------------------------|--------------------------------|
133133
| 65536 | `dnssec_query` | [bLIP 32](./blip-0032.md) |
134134
| 65538 | `dnssec_proof` | [bLIP 32](./blip-0032.md) |
135+
| 65540 | `offer_paths_request` | [bLIP 66](./blip-0066.md) |
136+
| 65542 | `offer_paths` | [bLIP 66](./blip-0066.md) |
137+
| 65544 | `serve_static_invoice` | [bLIP 66](./blip-0066.md) |
138+
| 65546 | `static_invoice_persisted` | [bLIP 66](./blip-0066.md) |
135139

136140
## Copyright
137141

blip-0066.md

Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
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

Comments
 (0)