Skip to content

Commit 38e3f54

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

File tree

3 files changed

+190
-0
lines changed

3 files changed

+190
-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: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
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+
* SHOULD include the `invoice_slot` in the `reply_path` so they can remember which slot the
94+
resulting offer 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+
* In the blinded offer path(s) encrypted data, MUST include the `invoice_slot` in order to uniquely
105+
identify and retrieve the static invoice when a future invoice request comes in
106+
* MUST set `reply_path` in the `onionmsg_tlv` stream
107+
* MUST include the `invoice_slot` from the preceding `offer_paths_request` in the encrypted data of
108+
the reply path to this message, so the recipient can reuse said reply path in the future to update
109+
the invoice
110+
* MAY set `paths_absolute_expiry` to indicate the absolute time when the included blinded offer
111+
path(s) expire and can no longer be used
112+
113+
Recipients of `offer_paths`:
114+
* MUST authenticate the message using encrypted data from the blinded reply path they received the
115+
`offer_paths` message over
116+
* If `paths_absolute_expiry` is not too soon, SHOULD respond with a `serve_static_invoice` message
117+
using the provided `reply_path` after building an offer using the provided path(s) and
118+
corresponding static invoice
119+
* If sending `serve_static_invoice`, SHOULD persist the reply path to the `offer_paths` message, which
120+
can be used later to update the static invoice
121+
122+
Senders of `serve_static_invoice`:
123+
* When creating the offer corresponding to the static invoice, MUST use the blinded paths from the
124+
preceding `offer_paths` message
125+
* MUST set `forward_invoice_request_path` to a blinded path that the invoice server can use to
126+
forward invoice requests from payers to this recipient's node
127+
* MUST set `reply_path` in the `onionmsg_tlv` stream
128+
129+
Recipients of `serve_static_invoice`:
130+
* MUST authenticate the message using encrypted data from the blinded reply path they received the
131+
`serve_static_invoice` message over
132+
* If the `invoice_slot` in the encrypted data from the blinded path they received this message over
133+
corresponds to an existing invoice, SHOULD replace that invoice with this one and stop serving the
134+
previous invoice
135+
* SHOULD limit the amount of storage they will dedicate to persisting invoices for a particular
136+
client
137+
* If limits are satisfied, SHOULD persist the `static_invoice` and `forward_invoice_request_path`
138+
and then respond with `static_invoice_persisted` using the provided reply path
139+
140+
Senders of `static_invoice_persisted`:
141+
* SHOULD wait until the invoice is confirmed as persisted before sending this message
142+
143+
Recipients of `static_invoice_persisted`:
144+
* MUST authenticate the message using encrypted data from the blinded reply path they received the
145+
`static_invoice_persisted` message over
146+
* SHOULD mark the offer corresponding to the invoice as ready to receive async payments
147+
* SHOULD update the persisted invoice periodically or when channels close/fees change, using the
148+
previously persisted reply path to the preceding `offer_paths` message
149+
150+
Static invoice servers:
151+
* If an invoice request comes in corresponding to a static invoice that was previously persisted with
152+
them:
153+
* SHOULD retrieve the persisted invoice and respond to the payer with it
154+
* SHOULD forward the invoice request to the recipient over the previously provided
155+
`forward_invoice_request_path`, and awake them if possible, such as via the LSPS5 protocol
156+
157+
158+
## Rationale
159+
160+
* Q: If we're configuring the recipient with blinded paths from the static invoice server, but those
161+
paths are used to retrieve offer blinded paths from the server, why not just configure the
162+
recipient with the offer blinded paths to begin with?
163+
* A: We want the recipient to have a variety of offers containing unique blinded paths to choose
164+
from, for privacy reasons. They shouldn't have to reuse the same offer paths in all situations.
165+
Implementations should aim to rotate the cached offers that are returned to the user.
166+
167+
* Q: Why use blinded paths for server<>recipient authentication?
168+
* A: The protocol is based on BOLT 12 so route blinding is known to be a supported mechanism by both
169+
parties already. It also allows the sender and recipient to not be direct [channel] peers, if that
170+
flexibility is desired.
171+
172+
173+
## Universality
174+
175+
For async payments to work with BOLT 12, we need some way for senders to get invoices from
176+
often-offline recipients when they go to pay, that is self-custodial and ideally as trustless as
177+
possible. This protocol provides a way, that is implemented in LDK, but wallets may have their own
178+
way of accomplishing this. Therefore, it doesn't need to be universal in the BOLTs.
179+
180+
## Reference Implementations
181+
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)
182+
183+
## Copyright
184+
185+
This bLIP is licensed under the CC0 license.

0 commit comments

Comments
 (0)