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_saltand 16-byteencrypted_counterparty_secretencrypted_counterparty_secretis a staticcounterparty_secretencrypted withChaCha20-Poly1305usingcounterparty_secretitself as the key andunique_saltas 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_secretin the offer - Note that
payer_encrypted_reply_pathmust 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_secretusingunique_saltand decrypts the reply path - Counterparty uses
unique_saltto 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_idand 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_secrets 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.