BOLT #12: Flexible Protocol for Lightning Payments

Table of Contents

Limitations of BOLT 11

The BOLT 11 invoice format has proven popular but has several limitations:

  1. The entangling of bech32 encoding makes it awkward to send in other forms (e.g. inside the lightning network itself).
  2. The signature applying to the entire invoice makes it impossible to prove an invoice without revealing its entirety.
  3. Fields cannot generally be extracted for external use: the h field was a boutique extraction of the d field only.
  4. The lack of the 'it's OK to be odd' rule makes backward compatibility harder.
  5. The 'human-readable' idea of separating amounts proved fraught: p was often mishandled, and amounts in pico-bitcoin are harder than the modern satoshi-based counting.
  6. Developers found the bech32 encoding to have an issue with extensions, which means we want to replace or discard it anyway.
  7. The payment_secret designed to prevent probing by other nodes in the path was only useful if the invoice remained private between the payer and payee.
  8. Invoices must be given per user and are actively dangerous if two payment attempts are made for the same user.

Payment Flow Scenarios

Here we use "user" as shorthand for the individual user's lightning node and "merchant" as the shorthand for the node of someone who is selling or has sold something.

There are two basic payment flows supported by BOLT 12:

The general user-pays-merchant flow is:

  1. A merchant publishes an offer, such as on a web page or a QR code.
  2. Every user requests a unique invoice over the lightning network using an invoice_request message, which contains the offer fields.
  3. The merchant replies with the invoice.
  4. The user makes a payment to the merchant as indicated by the invoice.

The merchant-pays-user flow (e.g. ATM or refund):

  1. The merchant publishes an invoice_request which contains offer fields which refer to its attempt to send money, including an amount.
  2. The user sends an invoice over the lightning network for the amount in the invoice_request, using a (possibly temporary) invoice_node_id.
  3. The merchant confirms the invoice_node_id to ensure it's about to pay the correct person, and makes a payment to the invoice.

Payment Proofs and Payer Proofs

Note that the normal lightning "proof of payment" can only demonstrate that an invoice was paid (by showing the preimage of the payment_hash), not who paid it. The merchant can claim an invoice was paid, and once revealed, anyone can claim they paid the invoice, too.[1]

Providing a key in invoice_request allows the payer to prove that they were the one to request the invoice. In addition, the Merkle construction of the BOLT 12 invoice signature allows the user to reveal invoice fields in case of a dispute selectively.

Encoding

Each of the forms documented here are in TLV format.

The supported ASCII encoding is the human-readable prefix, followed by a 1, followed by a bech32-style data string of the TLVs in order, optionally interspersed with + (for indicating additional data is to come). There is no checksum, unlike bech32m.

Requirements

Readers of a bolt12 string:

Rationale

The use of bech32 is arbitrary but already exists in the bitcoin world. We currently omit the six-character trailing checksum: QR codes have their own checksums anyway, and errors don't result in loss of funds, simply an invalid offer (or inability to parse).

The use of + (which is ignored) allows use over limited text fields like Twitter:

lno1xxxxxxxx+

yyyyyyyyyyyy+

zzzzz

See format-string-test.json.

Signature Calculation

All signatures are created as per BIP-340 and tagged as recommended there. Thus we define H(tag,msg) as SHA256(SHA256(tag) || SHA256(tag) || msg), and SIG(tag,msg,key) as the signature of H(tag,msg) using key.

Each form is signed using one or more signature TLV elements: TLV types 240 through 1000 (inclusive). For these, the tag is "lightning" || messagename || fieldname, and msg is the Merkle-root; "lightning" is the literal 9-byte ASCII string, messagename is the name of the TLV stream being signed (i.e. "invoice_request" or "invoice") and the fieldname is the TLV field containing the signature (e.g. "signature").

The formulation of the Merkle tree is similar to that proposed in BIP-341, with each TLV leaf paired with a nonce leaf to avoid revealing adjacent nodes in proofs.

The Merkle tree's leaves are, in TLV-ascending order for each tlv:

  1. The H("LnLeaf",tlv).
  2. The H("LnNonce"||first-tlv,tlv-type) where first-tlv is the numerically-first TLV entry in the stream, and tlv-type is the "type" field (1-9 bytes) of the current tlv.

The Merkle tree inner nodes are H("LnBranch", lesser-SHA256||greater-SHA256); this ordering means proofs are more compact since left/right is inherently determined.

If there is not exactly a power of 2 leaves, then the tree depth will be uneven, with the deepest tree on the lowest-order leaves.

e.g. consider the encoding of an invoice signature with TLVs TLV1, TLV2, and TLV3 (of types 1, 2 and 3 respectively):

L1=H("LnLeaf",TLV1)
L1nonce=H("LnNonce"||TLV1,1)
L2=H("LnLeaf",TLV2)
L2nonce=H("LnNonce"||TLV1,2)
L3=H("LnLeaf",TLV3)
L3nonce=H("LnNonce"||TLV1,3)

Assume L1 < L1nonce, L2 > L2nonce and L3 > L3nonce.

   L1    L1nonce                      L2   L2nonce                L3   L3nonce
     \   /                             \   /                       \   /
      v v                               v v                         v v
L1A=H("LnBranch",L1||L1nonce) L2A=H("LnBranch",L2nonce||L2)  L3A=H("LnBranch",L3nonce||L3)

Assume L1A < L2A:

       L1A   L2A                                 L3A=H("LnBranch",L3nonce||L3)
         \   /                                    |
          v v                                     v
  L1A2A=H("LnBranch",L1A||L2A)                   L3A=H("LnBranch",L3nonce||L3)

Assume L1A2A > L3A:

  L1A2A=H("LnBranch",L1A||L2A)          L3A
                          \            /
                           v          v
                Root=H("LnBranch",L3A||L1A2A)

Signature = SIG("lightninginvoicesignature", Root, nodekey)

Offers

Offers are a precursor to an invoice_request: readers will request an invoice (or multiple) based on the offer. An offer can be much longer-lived than a particular invoice, so it has some different characteristics; in particular the amount can be in a non-lightning currency. It's also designed for compactness to fit inside a QR code easily.

Note that the non-signature TLV elements get mirrored into invoice_request and invoice messages, so they each have specific and distinct TLV ranges.

The human-readable prefix for offers is lno.

TLV Fields for Offers

  1. tlv_stream: offer
  2. types:
    1. type: 2 (offer_chains)
    2. data:
      • [...*chain_hash:chains]
    3. type: 4 (offer_metadata)
    4. data:
      • [...*byte:data]
    5. type: 6 (offer_currency)
    6. data:
      • [...*utf8:iso4217]
    7. type: 8 (offer_amount)
    8. data:
      • [tu64:amount]
    9. type: 10 (offer_description)
    10. data:
      • [...*utf8:description]
    11. type: 12 (offer_features)
    12. data:
      • [...*byte:features]
    13. type: 14 (offer_absolute_expiry)
    14. data:
      • [tu64:seconds_from_epoch]
    15. type: 16 (offer_paths)
    16. data:
      • [...*blinded_path:paths]
    17. type: 18 (offer_issuer)
    18. data:
      • [...*utf8:issuer]
    19. type: 20 (offer_quantity_max)
    20. data:
      • [tu64:max]
    21. type: 22 (offer_node_id)
    22. data:
      • [point:node_id]

Requirements For Offers

A writer of an offer:

A reader of an offer:

Rationale

The entire offer is reflected in the invoice_request, both for completeness (so all information will be returned in the invoice), and so that the offer node can be stateless. This makes offer_metadata particularly useful, since it can contain an authentication cookie to validate the other fields.

A signature is unnecessary, and makes for a longer string (potentially limiting QR code use on low-end cameras); if the offer has an error, no invoice will be given since the request includes all the non-signature fields.

offer_quantity_max is allowed to be 1, which seems useless, but useful in a system which bases it on available stock. It would be painful to have to special-case the "only one left" offer generation.

Invoice Requests

Invoice Requests are a request for an invoice; the human-readable prefix for invoice requests is lnr.

There are two similar-looking uses for invoice requests, which are almost identical from a workflow perspective, but are quite different from a user's point of view.

One is a response to an offer; this contains the offer_node_id and all other offer details, and is generally received over an onion message: if it's valid and refers to a known offer, the response is generally to reply with an invoice using the reply_path field of the onion message.

The second case is publishing an invoice_request without an offer, such as via QR code. It contains no offer_node_id (using the invreq_payer_id instead, as it in the one paying), and the other offer fields are filled by the creator of the invoice_request, forming a kind of offer-to-send-money.

Note: the invreq_metadata is numbered 0 (not in the 80-159 range for other invreq fields) as this is the first TLV element, which ensures payer-provided entropy is used in hashing for Signature Calculation.

TLV Fields for invoice_request

  1. tlv_stream: invoice_request
  2. types:
    1. type: 0 (invreq_metadata)
    2. data:
      • [...*byte:blob]
    3. type: 2 (offer_chains)
    4. data:
      • [...*chain_hash:chains]
    5. type: 4 (offer_metadata)
    6. data:
      • [...*byte:data]
    7. type: 6 (offer_currency)
    8. data:
      • [...*utf8:iso4217]
    9. type: 8 (offer_amount)
    10. data:
      • [tu64:amount]
    11. type: 10 (offer_description)
    12. data:
      • [...*utf8:description]
    13. type: 12 (offer_features)
    14. data:
      • [...*byte:features]
    15. type: 14 (offer_absolute_expiry)
    16. data:
      • [tu64:seconds_from_epoch]
    17. type: 16 (offer_paths)
    18. data:
      • [...*blinded_path:paths]
    19. type: 18 (offer_issuer)
    20. data:
      • [...*utf8:issuer]
    21. type: 20 (offer_quantity_max)
    22. data:
      • [tu64:max]
    23. type: 22 (offer_node_id)
    24. data:
      • [point:node_id]
    25. type: 80 (invreq_chain)
    26. data:
      • [chain_hash:chain]
    27. type: 82 (invreq_amount)
    28. data:
      • [tu64:msat]
    29. type: 84 (invreq_features)
    30. data:
      • [...*byte:features]
    31. type: 86 (invreq_quantity)
    32. data:
      • [tu64:quantity]
    33. type: 88 (invreq_payer_id)
    34. data:
      • [point:key]
    35. type: 89 (invreq_payer_note)
    36. data:
      • [...*utf8:note]
    37. type: 240 (signature)
    38. data:
      • [bip340sig:sig]

Requirements for Invoice Requests

The writer:

The reader:

Rationale

invreq_metadata might typically contain information about the derivation of the invreq_payer_id. This should not leak any information (such as using a simple BIP-32 derivation path); a valid system might be for a node to maintain a base payer key and encode a 128-bit tweak here. The payer_id would be derived by tweaking the base key with SHA256(payer_base_pubkey || tweak). It's also the first entry (if present), ensuring an unpredictable nonce for hashing.

invreq_payer_note allows you to compliment, taunt, or otherwise engrave graffiti into the invoice for all to see.

Users can give a tip (or obscure the amount sent) by specifying an invreq_amount in their invoice request, even though the offer specifies an offer_amount. The recipient will only accept this if the invoice request amount exceeds the amount it's expecting (i.e. its offer_amount after any currency conversion, multiplied by invreq_quantity, if any).

Non-offer-response invoice_requests are currently required to explicitly state the invreq_amount in the chain currency, so offer_amount and offer_currency are redundant (but may be informative for the payer to know how the sender claims invreq_amount was derived).

Invoices

Invoices are a payment request, and when the payment is made, it can be combined with the invoice to form a cryptographic receipt.

The recipient sends an invoice in response to an invoice_request using the onion_message invoice field.

  1. tlv_stream: invoice
  2. types:

    1. type: 0 (invreq_metadata)
    2. data:
      • [...*byte:blob]
    3. type: 2 (offer_chains)
    4. data:
      • [...*chain_hash:chains]
    5. type: 4 (offer_metadata)
    6. data:
      • [...*byte:data]
    7. type: 6 (offer_currency)
    8. data:
      • [...*utf8:iso4217]
    9. type: 8 (offer_amount)
    10. data:
      • [tu64:amount]
    11. type: 10 (offer_description)
    12. data:
      • [...*utf8:description]
    13. type: 12 (offer_features)
    14. data:
      • [...*byte:features]
    15. type: 14 (offer_absolute_expiry)
    16. data:
      • [tu64:seconds_from_epoch]
    17. type: 16 (offer_paths)
    18. data:
      • [...*blinded_path:paths]
    19. type: 18 (offer_issuer)
    20. data:
      • [...*utf8:issuer]
    21. type: 20 (offer_quantity_max)
    22. data:
      • [tu64:max]
    23. type: 22 (offer_node_id)
    24. data:
      • [point:node_id]
    25. type: 80 (invreq_chain)
    26. data:
      • [chain_hash:chain]
    27. type: 82 (invreq_amount)
    28. data:
      • [tu64:msat]
    29. type: 84 (invreq_features)
    30. data:
      • [...*byte:features]
    31. type: 86 (invreq_quantity)
    32. data:
      • [tu64:quantity]
    33. type: 88 (invreq_payer_id)
    34. data:
      • [point:key]
    35. type: 89 (invreq_payer_note)
    36. data:
      • [...*utf8:note]
    37. type: 160 (invoice_paths)
    38. data:
      • [...*blinded_path:paths]
    39. type: 162 (invoice_blindedpay)
    40. data:
      • [...*blinded_payinfo:payinfo]
    41. type: 164 (invoice_created_at)
    42. data:
      • [tu64:timestamp]
    43. type: 166 (invoice_relative_expiry)
    44. data:
      • [tu32:seconds_from_creation]
    45. type: 168 (invoice_payment_hash)
    46. data:
      • [sha256:payment_hash]
    47. type: 170 (invoice_amount)
    48. data:
      • [tu64:msat]
    49. type: 172 (invoice_fallbacks)
    50. data:
      • [...*fallback_address:fallbacks]
    51. type: 174 (invoice_features)
    52. data:
      • [...*byte:features]
    53. type: 176 (invoice_node_id)
    54. data:
      • [point:node_id]
    55. type: 240 (signature)
    56. data:
      • [bip340sig:sig]
  3. subtype: blinded_payinfo

  4. data:

  5. subtype: fallback_address

  6. data:

Invoice Features

Bits Description Name
16 Multi-part-payment support MPP/compulsory
17 Multi-part-payment support MPP/optional

The 'MPP support' invoice feature indicates that the payer MUST (16) or MAY (17) use multiple part payments to pay the invoice.

Some implementations may not support MPP (e.g. for small payments), or may (due to capacity limits on a single channel) require it.

Requirements

A writer of an invoice:

A reader of an invoice:

Rationale

Because the messaging layer is unreliable, it's quite possible to receive multiple requests for the same offer. As it's the caller's responsibility not to reuse invreq_payer_id the writer doesn't have to check all the fields are duplicates before simply returning a previous invoice. Note that such caching is optional, and should be carefully limited when e.g. currency conversion is involved, or if the invoice has expired.

The invoice duplicates fields rather than committing to the previous invreq. This flattened format simplifies storage at some space cost, as the payer need only remember the invoice for any refunds or proof.

The reader of the invoice cannot trust the invoice correctly reflects the invreq fields, hence the requirements to check that they are correct, although allowance is made for simply sending an unrequested invoice directly.

Note that the recipient of the invoice can determine the expected amount from either the offer it received, or the invreq it sent, so often already has authorization for the expected amount.

The default invoice_relative_expiry of 7200 seconds, which is generally a sufficient time for payment, even if new channels need to be opened.

Blinded paths provide an equivalent to payment_secret and payment_metadata used in BOLT 11. Even if invoice_node_id or invreq_payer_id is public, we force the use of blinding paths to keep these features. If the recipient does not care about the added privacy offered by blinded paths, they can create a path of length 1 with only themselves.

Rather than provide detailed per-hop-payinfo for each hop in a blinded path, we aggregate the fees and CLTV deltas. This avoids trivially revealing any distinguishing non-uniformity which may distinguish the path.

In the case of an invoice where there was no offer (just an invoice request), the payer needs to ensure that the invoice is from the intended payment recipient. This is the basis for the suggestion to confirm the invoice_node_id for this case.

Raw invoices (not based on an invoice_request) are generally not supported, though an implementation is allowed to support them, and we may define the behavior in future.

Invoice Errors

Informative errors can be returned in an onion message invoice_error field (via the onion reply_path) for either invoice_request or invoice.

TLV Fields for invoice_error

  1. tlv_stream: invoice_error
  2. types:
    1. type: 1 (erroneous_field)
    2. data:
      • [tu64:tlv_fieldnum]
    3. type: 3 (suggested_value)
    4. data:
      • [...*byte:value]
    5. type: 5 (error)
    6. data:
      • [...*utf8:msg]

Requirements

A writer of an invoice_error:

A reader of an invoice_error: FIXME!

Rationale

Usually an error message is sufficient for diagnostics, however future enhancements may make automated handling useful.

In particular, we could allow non-offer-response invoice_requests to omit invreq_amount in future and use offer fields to indicate alternate currencies. ("I will send you 10c!"). Then the sender of the invoice would have to guess how many msat that was, and could use the invoice_error to indicate if the recipient disagreed with the conversion so the sender can send a new invoice.

FIXME: Possible future extensions:

  1. The offer can require delivery info in the invoice_request.
  2. An offer can be updated: the response to an invoice_request is another offer, perhaps with a signature from the original offer_node_id
  3. Any empty TLV fields can mean the value is supposed to be known by other means (i.e. transport-specific), but is still hashed for sig.
  4. We could upgrade to allow multiple offers in one invreq and invoice, to make a shopping list.
  5. All-zero offer_id == gratuitous payment.
  6. Streaming invoices?
  7. Re-add recurrence.
  8. Re-add invreq_refund_for to support proofs.
  9. Re-add invoice_replace for requesting replacement of a (stuck-payment) invoice with a new one.
  10. Allow non-offer invoice_request with alternate currencies?
  11. Add offer_quantity_unit to indicate stepping for quantity (e.g. 100 grams).

[1] https://www.youtube.com/watch?v=4SYc_flMnMQ