An issue with BOLT 12 for async payments is that recipients may be offline when the payer sends them their invoice request. However, their channel counterparty is likely to be an always-online node. Here we outline a scheme for always-online channel counterparties (e.g. LSPs) to supply BOLT 12 invoices on behalf of often-offline receivers when requested by payers.
TL;DR when creating an offer, the receiver will give their counterparty a payment_hash
-less (or "keysend") invoice. Later, when the payer sends an invoice request to the receiver, the payer will include an encrypted reply path in the onion payload destined for the counterparty. The counterparty will then send the aforementioned keysend invoice over the reply path to the payer.
- Counterparty sends receiver a 16-byte
unique_salt
and 16-byteencrypted_counterparty_secret
encrypted_counterparty_secret
is a staticcounterparty_secret
encrypted withChaCha20-Poly1305
usingcounterparty_secret
itself as the key andunique_salt
as the nonce
- Receiver creates an offer containing these values as optional TLVs and a blinded path that includes the counterparty
- Receiver sends counterparty a
payment_hash
-less (or "keysend") BOLT 12 invoice corresponding to the offer. The counterparty stores this invoice, keyed byunique_salt
--- some time later --
- Payer scans offer
- If they don't support async payments, they'll send an invoice request per the usual BOLT 12 protocol
- Payer sends an invoice request with the receiver as the final hop as usual, but includes an encrypted reply path in blinded intermediate nodes’ onion payloads.
For example: given that the offer’s blinded path is over nodes [B, C, D], the payer will insert an encrypted reply path and unique_salt
into B and C’s payloads, i.e. every payload but the last. If the payer selects first hop A, the payloads constructed by the payer look like:
payload for A payload for B payload for C (counterparty) payload for D (receiver)
┌───────────────────────────────────┐ ┌─────────────────────────────────────┐ ┌─────────────────────────────────────┐ ┌─────────────────────────────────────┐
│ (4, payer_encrypted_control_tlvs) │ │ (4, receiver_encrypted_control_tlvs)│ │ (4, receiver_encrypted_control_tlvs)│ │ (2, reply_path) │
└───────────────────────────────────┘ │ │ │ │ │ │
│ (4141, payer_encrypted_reply_path) │ │ (4141, payer_encrypted_reply_path) │ │ (4, receiver_encrypted_control_tlvs)│
│ │ │ │ │ │
│ (4343, unique_salt) │ │ (4343, unique_salt) │ │ (64, invoice_request) │
└─────────────────────────────────────┘ └─────────────────────────────────────┘ └─────────────────────────────────────┘
- The reply path is encrypted by the payer with the
encrypted_counterparty_secret
in the offer - Note that
payer_encrypted_reply_path
must be included in every blinded hop's onion payload besides the final hop because the payer can't know whether dummy hops are present in the offer's blinded path
- Counterparty receives a forwardable onion message with a payload containing the encrypted reply path and
unique_salt
- If the receiver happens to be online at the time, counterparty can forward the invoice request per normal BOLT 12
- Counterparty rederives
encrypted_counterparty_secret
usingunique_salt
and decrypts the reply path - Counterparty uses
unique_salt
to fetch the invoice and sends it to the payer via the newly decrypted reply path - Payer receives the invoice, sees that it's correctly signed with
offer_node_id
and pays it with keysend
+-------+ +------------+ +--------+
| Payer | |Counterparty| |Receiver|
+-------+ +------------+ +--------+
| | |
| | unique_salt + encrypted_counterparty_secret |
| | ------------------------------------------------> |
| | | Create offer with salt + secret
| | |
| | keysend BOLT 12 invoice for offer |
| | <------------------------------------------------ |
| Persist invoice | |
| | |
| | ... |
| | |
Scan offer | | |
| invoice request OM | |
| +------------------------+ | |
| | counterparty_payload | | |
| |+---------------------+ | | |
|-||encrypted_reply_path | |-------->| Decrypt reply_path + fetch persisted invoice |
| || .. | | | |
| |+---------------------+ | | |
| +------------------------+ | |
| | |
| keysend invoice | |
| <---------------------------------| |
| | |
In the future, we can add in-protocol messages for counterparties and receivers exchanging encrypted_counterparty_secret
s and invoices. For v1, we can assume counterparties and receivers have the ability to communicate out-of-band, e.g. an LSP and its client.