Skip to content

Instantly share code, notes, and snippets.

@valentinewallace
Last active January 22, 2024 19:32
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save valentinewallace/440764378601ad113a76bc15b57c8d12 to your computer and use it in GitHub Desktop.
Save valentinewallace/440764378601ad113a76bc15b57c8d12 to your computer and use it in GitHub Desktop.
BOLT 12 for Async Payments

Intro

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.

Proposed Protocol

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.

  1. Counterparty sends receiver a 16-byte unique_salt and 16-byte encrypted_counterparty_secret
    • encrypted_counterparty_secret is a static counterparty_secret encrypted with ChaCha20-Poly1305 using counterparty_secret itself as the key and unique_salt as the nonce
  2. Receiver creates an offer containing these values as optional TLVs and a blinded path that includes the counterparty
  3. Receiver sends counterparty a payment_hash-less (or "keysend") BOLT 12 invoice corresponding to the offer. The counterparty stores this invoice, keyed by unique_salt

--- some time later --

  1. Payer scans offer
    • If they don't support async payments, they'll send an invoice request per the usual BOLT 12 protocol
  2. 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
  1. 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
  2. Counterparty rederives encrypted_counterparty_secret using unique_salt and decrypts the reply path
  3. Counterparty uses unique_salt to fetch the invoice and sends it to the payer via the newly decrypted reply path
  4. 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              |                                                   |                                
              |  <---------------------------------|                                                   |                                
              |                                    |                                                   |                                

Future directions

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment