BOLT #12: Negotiation Protocol for Lightning Payments
Table of Contents
- Limitations of BOLT 11
- Payment Flow Scenarios
- Encoding
- Signature calculation
- Offers
- Invoice Requests
- Invoices
- Invoice Errors
- Payer Proofs
Limitations of BOLT 11
The BOLT 11 invoice format has proven popular but has several limitations:
- The entangling of bech32 encoding makes it awkward to send in other forms (e.g. inside the lightning network itself).
- The signature applying to the entire invoice makes it impossible to prove an invoice without revealing its entirety.
- Fields cannot generally be extracted for external use: the
hfield was a boutique extraction of thedfield only. - The lack of the ‘it’s OK to be odd’ rule makes backward compatibility harder.
- The ‘human-readable’ idea of separating amounts proved
fraught:
pwas often mishandled, and amounts in pico-bitcoin are harder than the modern satoshi-based counting. - Developers found the bech32 encoding to have an issue with extensions, which means we want to replace or discard it anyway.
- The
payment_secretdesigned to prevent probing by other nodes in the path was only useful if the invoice remained private between the payer and payee. - 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 includes an amount it wishes to send to the user. 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
Writers of a bolt12 string: - MUST either use all lowercase or
all UPPERCASE. - SHOULD use uppercase for QR codes. - SHOULD use
lower case otherwise. - MAY use +, optionally
followed by whitespace, to separate large bolt12 strings.
Readers of a bolt12 string: - MUST handle strings which are all
lowercase, or all uppercase. - if it encounters a +
followed by zero or more whitespace characters between two bech32
characters: - MUST remove the + and whitespace.
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
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”, “invoice” or “payer_proof”) 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 TLV0, TLV1, and TLV2 (of types 0,
1, and 2 respectively):
L1=H("LnLeaf",TLV0)
L1nonce=H("LnNonce"||TLV0,0)
L2=H("LnLeaf",TLV1)
L2nonce=H("LnNonce"||TLV0,1)
L3=H("LnLeaf",TLV2)
L3nonce=H("LnNonce"||TLV0,2)
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
tlv_stream:offer- types:
- type: 2 (
offer_chains) - data:
- [
...*chain_hash:chains]
- [
- type: 4 (
offer_metadata) - data:
- [
...*byte:data]
- [
- type: 6 (
offer_currency) - data:
- [
...*utf8:iso4217]
- [
- type: 8 (
offer_amount) - data:
- [
tu64:amount]
- [
- type: 10 (
offer_description) - data:
- [
...*utf8:description]
- [
- type: 12 (
offer_features) - data:
- [
...*byte:features]
- [
- type: 14 (
offer_absolute_expiry) - data:
- [
tu64:seconds_from_epoch]
- [
- type: 16 (
offer_paths) - data:
- [
...*blinded_path:paths]
- [
- type: 18 (
offer_issuer) - data:
- [
...*utf8:issuer]
- [
- type: 20 (
offer_quantity_max) - data:
- [
tu64:max]
- [
- type: 22 (
offer_issuer_id) - data:
- [
point:id]
- [
- type: 2 (
Requirements For Offers
A writer of an offer: - MUST NOT set any TLV fields outside the
inclusive ranges: 1 to 79 and 1000000000 to 1999999999. - if the
chain for the invoice is not solely bitcoin: - MUST specify
offer_chains the offer is valid for. - otherwise: -
SHOULD omit offer_chains, implying that bitcoin is
only chain. - if a specific minimum offer_amount is
required for successful payment: - MUST set
offer_amount to the amount expected (per item). -
MUST set offer_amount greater than zero. - if the
currency for offer_amount is that of all entries in
chains: - MUST specify offer_amount in
multiples of the minimum lightning-payable unit
(e.g. milli-satoshis for bitcoin). - otherwise: - MUST specify
offer_currency iso4217 as an ISO 4217
three-letter code. - MUST specify offer_amount in the
currency unit adjusted by the ISO 4217 exponent (e.g. USD cents).
- MUST set offer_description to a complete
description of the purpose of the payment. - otherwise: - MUST NOT
set offer_amount - MUST NOT set
offer_currency - MAY set
offer_description - MAY set
offer_metadata for its own use. - if it supports
bolt12 offer features: - MUST set
offer_features.features to the bitmap of
bolt12 features. - if the offer expires: - MUST set
offer_absolute_expiry seconds_from_epoch
to the number of seconds after midnight 1 January 1970, UTC that
invoice_request should not be attempted. - if it is connected only
by private channels: - MUST include offer_paths
containing one or more paths to the node from publicly reachable
nodes. - otherwise: - MAY include offer_paths. - if
it includes offer_paths: - MAY set
offer_issuer_id. - otherwise: - MUST set
offer_issuer_id to the node’s public key to request
the invoice from. - if it sets offer_issuer: - SHOULD
set it to identify the issuer of the invoice clearly. - if it
includes a domain name: - SHOULD begin it with either user@domain
or domain - MAY follow with a space and more text - if it can
supply more than one item for a single invoice: - if the maximum
quantity is known: - MUST set that maximum in
offer_quantity_max. - MUST NOT set
offer_quantity_max to 0. - otherwise: - MUST set
offer_quantity_max to 0. - otherwise: - MUST NOT set
offer_quantity_max.
A reader of an offer: - if the offer contains any TLV fields
outside the inclusive ranges: 1 to 79 and 1000000000 to
1999999999: - MUST NOT respond to the offer. - if
offer_features contains unknown odd bits
that are non-zero: - MUST ignore the bit. - if
offer_features contains unknown even bits
that are non-zero: - MUST NOT respond to the offer. - SHOULD
indicate the unknown bit to the user. - if
offer_chains is not set: - if the node does not
accept bitcoin invoices: - MUST NOT respond to the offer -
otherwise: (offer_chains is set): - if the node does
not accept invoices for at least one of the chains: -
MUST NOT respond to the offer - if offer_amount is
set and offer_description is not set: - MUST NOT
respond to the offer. - if offer_amount is set and is
not greater than zero: - MUST NOT respond to the offer. - if
offer_currency is set and offer_amount
is not set: - MUST NOT respond to the offer. - if neither
offer_issuer_id nor offer_paths are set:
- MUST NOT respond to the offer. - if num_hops is 0
in any blinded_path in offer_paths: -
MUST NOT respond to the offer. - if it uses
offer_amount to provide the user with a cost
estimate: - MUST take into account the currency units for
offer_amount: - offer_currency field if
set - otherwise, the minimum lightning-payable unit
(e.g. milli-satoshis for bitcoin). - MUST warn the user if the
received invoice_amount differs significantly from
that estimate. - if the current time is after
offer_absolute_expiry: - MUST NOT respond to the
offer. - if it chooses to send an invoice request, it sends an
onion message: - if offer_paths is set: - MUST send
the onion message via any path in offer_paths to the
final onion_msg_hop.blinded_node_id in
that path - otherwise: - MUST send the onion message to
offer_issuer_id - MAY send more than one invoice
request onion message at once.
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.
Because offer fields are copied into the invoice request (and then the invoice), they require distinct ranges. The range 1-79 is the normal range, with another billion for self-assigning experimental ranges.
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.
The offer_issuer_id can be omitted for brevity, if
offer_paths is set, as each of the final
blinded_node_id in the paths can serve as a valid
public key for the destination.
Because offer_amount can be in a different
currency (using the offer_currency field) it is
merely a guide: the issuer will convert it into a number of
millisatoshis for invoice_amount at the time they
generate an invoice, or the invoice request can specify the exact
amount in invreq_amount, but the issuer may then
reject it if it disagrees.
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.
Offers can be used to simply send money without expecting
anything in return (tips, kudos, donations, etc), which means the
description field is optional (the offer_issuer field
is very useful for this case!); if you are charging for something
specific, the description is vital for the user to know what it
was they paid for.
An empty offer_chains (present but with zero
entries) is explicitly invalid because it would make invoice
requests impossible. The payer cannot set
invreq_chain to “one of offer_chains”
when there are no chains listed. Rejecting such offers early
provides clear feedback rather than leaving implementations to
fail at the invoice request stage.
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_issuer_id or offer_paths 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 neither
offer_issuer_id nor offer_paths, setting
the invreq_payer_id (and possibly
invreq_paths) instead, as it is the one paying: 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) making it the
“numerically-first TLV entry” for Signature
Calculation. This ensures that merkle leaves are unguessable,
allowing a future compact representation to hide fields while
still allowing signature validation.
TLV Fields for
invoice_request
tlv_stream:invoice_request- types:
- type: 0 (
invreq_metadata) - data:
- [
...*byte:blob]
- [
- type: 2 (
offer_chains) - data:
- [
...*chain_hash:chains]
- [
- type: 4 (
offer_metadata) - data:
- [
...*byte:data]
- [
- type: 6 (
offer_currency) - data:
- [
...*utf8:iso4217]
- [
- type: 8 (
offer_amount) - data:
- [
tu64:amount]
- [
- type: 10 (
offer_description) - data:
- [
...*utf8:description]
- [
- type: 12 (
offer_features) - data:
- [
...*byte:features]
- [
- type: 14 (
offer_absolute_expiry) - data:
- [
tu64:seconds_from_epoch]
- [
- type: 16 (
offer_paths) - data:
- [
...*blinded_path:paths]
- [
- type: 18 (
offer_issuer) - data:
- [
...*utf8:issuer]
- [
- type: 20 (
offer_quantity_max) - data:
- [
tu64:max]
- [
- type: 22 (
offer_issuer_id) - data:
- [
point:id]
- [
- type: 80 (
invreq_chain) - data:
- [
chain_hash:chain]
- [
- type: 82 (
invreq_amount) - data:
- [
tu64:msat]
- [
- type: 84 (
invreq_features) - data:
- [
...*byte:features]
- [
- type: 86 (
invreq_quantity) - data:
- [
tu64:quantity]
- [
- type: 88 (
invreq_payer_id) - data:
- [
point:key]
- [
- type: 89 (
invreq_payer_note) - data:
- [
...*utf8:note]
- [
- type: 90 (
invreq_paths) - data:
- [
...*blinded_path:paths]
- [
- type: 91 (
invreq_bip_353_name) - data:
- [
u8:name_len] - [
name_len*byte:name] - [
u8:domain_len] - [
domain_len*byte:domain]
- [
- type: 240 (
signature) - data:
- [
bip340sig:sig]
- [
- type: 0 (
Requirements for Invoice Requests
The writer: - if it is responding to an offer: - MUST copy all
fields from the offer (including unknown fields). - if
offer_chains is set: - MUST set
invreq_chain to one of offer_chains
unless that chain is bitcoin, in which case it SHOULD omit
invreq_chain. - otherwise: - if it sets
invreq_chain it MUST set it to bitcoin. - MUST set
signature.sig as detailed in Signature
Calculation using the invreq_payer_id. - if
offer_amount is not present: - MUST specify
invreq_amount. - otherwise: - MAY omit
invreq_amount. - if it sets
invreq_amount: - MUST specify
invreq_amount.msat as greater or equal
to amount expected by offer_amount (and, if present,
offer_currency and invreq_quantity). -
MUST set invreq_payer_id to a transient public key. -
MUST remember the secret key corresponding to
invreq_payer_id. - if offer_quantity_max
is present: - MUST set invreq_quantity to greater
than zero. - if offer_quantity_max is non-zero: -
MUST set invreq_quantity less than or equal to
offer_quantity_max. - otherwise: - MUST NOT set
invreq_quantity - otherwise (not responding to an
offer): - MUST set offer_description to a complete
description of the purpose of the payment. - MUST set (or not set)
offer_absolute_expiry and offer_issuer
as it would for an offer. - MUST set invreq_payer_id
(as it would set offer_issuer_id for an offer). -
MUST set invreq_paths as it would set (or not set)
offer_paths for an offer. - MUST NOT include
signature, offer_metadata,
offer_chains, offer_amount,
offer_currency, offer_features,
offer_quantity_max, offer_paths or
offer_issuer_id - if the chain for the invoice is not
solely bitcoin: - MUST specify invreq_chain the offer
is valid for. - MUST set invreq_amount. - MUST NOT
set any non-signature TLV fields outside the inclusive ranges: 0
to 159 and 1000000000 to 2999999999 - MUST set
invreq_metadata to an unpredictable series of bytes.
- if it sets invreq_amount: - MUST set
msat in multiples of the minimum lightning-payable
unit (e.g. milli-satoshis for bitcoin) for
invreq_chain (or for bitcoin, if there is no
invreq_chain). - if it supports bolt12 invoice
request features: - MUST set
invreq_features.features to the bitmap
of features. - if it received the offer from which it constructed
this invoice_request using BIP 353 resolution: - MUST
include invreq_bip_353_name with, - name
set to the post-₿, pre-@ part of the BIP 353 HRN, -
domain set to the post-@ part of the BIP 353 HRN.
The reader: - MUST reject the invoice request if
invreq_payer_id or invreq_metadata are
not present. - MUST reject the invoice request if any
non-signature TLV fields are outside the inclusive ranges: 0 to
159 and 1000000000 to 2999999999 - if invreq_features
contains unknown odd bits that are non-zero: - MUST
ignore the bit. - if invreq_features contains unknown
even bits that are non-zero: - MUST reject the invoice
request. - MUST reject the invoice request if
signature is not correct as detailed in Signature
Calculation using the invreq_payer_id. - if
num_hops is 0 in any blinded_path in
invreq_paths: - MUST reject the invoice request. - if
offer_issuer_id is present, and
invreq_metadata is identical to a previous
invoice_request: - MAY simply reply with the previous
invoice. - otherwise: - MUST NOT reply with a previous invoice. -
if offer_issuer_id or offer_paths are
present (response to an offer): - MUST reject the invoice request
if the offer fields do not exactly match a valid, unexpired offer.
- if offer_paths is present: - MUST ignore the
invoice_request if it did not arrive via one of those paths. -
otherwise: - MUST ignore any invoice_request if it arrived via a
blinded path. - if offer_quantity_max is present: -
MUST reject the invoice request if there is no
invreq_quantity field. - if
offer_quantity_max is non-zero: - MUST reject the
invoice request if invreq_quantity is zero, OR
greater than offer_quantity_max. - otherwise: - MUST
reject the invoice request if there is an
invreq_quantity field. - if offer_amount
is present: - MUST calculate the expected amount using
the offer_amount: - if offer_currency is
not the invreq_chain currency, convert to the
invreq_chain currency. - if
invreq_quantity is present, multiply by
invreq_quantity.quantity. - if
invreq_amount is present: - MUST reject the invoice
request if invreq_amount.msat is less
than the expected amount. - MAY reject the invoice
request if invreq_amount.msat greatly
exceeds the expected amount. - otherwise (no
offer_amount): - MUST reject the invoice request if
it does not contain invreq_amount. - SHOULD send an
invoice in response using the onionmsg_tlv
reply_path. - otherwise (no
offer_issuer_id or offer_paths, not a
response to our offer): - MUST reject the invoice request if any
of the following are present: - offer_chains,
offer_features or offer_quantity_max. -
MUST reject the invoice request if invreq_amount is
not present. - MAY use offer_amount (or
offer_currency) for informational display to user. -
if it sends an invoice in response: - MUST use
invreq_paths if present, otherwise MUST use
invreq_payer_id as the node id to send to. - if
invreq_chain is not present: - MUST reject the
invoice request if bitcoin is not a supported chain. - otherwise:
- MUST reject the invoice request if
invreq_chain.chain is not a supported
chain. - if invreq_bip_353_name is present: - MUST
reject the invoice request if name or
domain contain any bytes which are not
0-9, a-z,
A-Z, -, _ or
..
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).
The requirement to use offer_paths if present,
ensures a node does not reveal it is the source of an offer if it
is asked directly. Similarly, the requirement that the correct
path is used for the offer ensures that cannot be made to reveal
that it is the same node that created some other offer.
Invoices
Invoices are a payment request, and when the payment is made, the payment preimage 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.
tlv_stream:invoice- types:
- type: 0 (
invreq_metadata) - data:
- [
...*byte:blob]
- [
- type: 2 (
offer_chains) - data:
- [
...*chain_hash:chains]
- [
- type: 4 (
offer_metadata) - data:
- [
...*byte:data]
- [
- type: 6 (
offer_currency) - data:
- [
...*utf8:iso4217]
- [
- type: 8 (
offer_amount) - data:
- [
tu64:amount]
- [
- type: 10 (
offer_description) - data:
- [
...*utf8:description]
- [
- type: 12 (
offer_features) - data:
- [
...*byte:features]
- [
- type: 14 (
offer_absolute_expiry) - data:
- [
tu64:seconds_from_epoch]
- [
- type: 16 (
offer_paths) - data:
- [
...*blinded_path:paths]
- [
- type: 18 (
offer_issuer) - data:
- [
...*utf8:issuer]
- [
- type: 20 (
offer_quantity_max) - data:
- [
tu64:max]
- [
- type: 22 (
offer_issuer_id) - data:
- [
point:id]
- [
- type: 80 (
invreq_chain) - data:
- [
chain_hash:chain]
- [
- type: 82 (
invreq_amount) - data:
- [
tu64:msat]
- [
- type: 84 (
invreq_features) - data:
- [
...*byte:features]
- [
- type: 86 (
invreq_quantity) - data:
- [
tu64:quantity]
- [
- type: 88 (
invreq_payer_id) - data:
- [
point:key]
- [
- type: 89 (
invreq_payer_note) - data:
- [
...*utf8:note]
- [
- type: 90 (
invreq_paths) - data:
- [
...*blinded_path:paths]
- [
- type: 91 (
invreq_bip_353_name) - data:
- [
u8:name_len] - [
name_len*byte:name] - [
u8:domain_len] - [
domain_len*byte:domain]
- [
- type: 160 (
invoice_paths) - data:
- [
...*blinded_path:paths]
- [
- type: 162 (
invoice_blindedpay) - data:
- [
...*blinded_payinfo:payinfo]
- [
- type: 164 (
invoice_created_at) - data:
- [
tu64:timestamp]
- [
- type: 166 (
invoice_relative_expiry) - data:
- [
tu32:seconds_from_creation]
- [
- type: 168 (
invoice_payment_hash) - data:
- [
sha256:payment_hash]
- [
- type: 170 (
invoice_amount) - data:
- [
tu64:msat]
- [
- type: 172 (
invoice_fallbacks) - data:
- [
...*fallback_address:fallbacks]
- [
- type: 174 (
invoice_features) - data:
- [
...*byte:features]
- [
- type: 176 (
invoice_node_id) - data:
- [
point:node_id]
- [
- type: 240 (
signature) - data:
- [
bip340sig:sig]
- [
- type: 0 (
- subtype:
blinded_payinfo - data:
- [
u32:fee_base_msat] - [
u32:fee_proportional_millionths] - [
u16:cltv_expiry_delta] - [
u64:htlc_minimum_msat] - [
u64:htlc_maximum_msat] - [
u16:flen] - [
flen*byte:features]
- [
- subtype:
fallback_address - data:
- [
byte:version] - [
u16:len] - [
len*byte:address]
- [
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: - MUST set
invoice_created_at to the number of seconds since
Midnight 1 January 1970, UTC when the invoice was created. - MUST
set invoice_amount to the minimum amount it will
accept, in units of the minimal lightning-payable unit
(e.g. milli-satoshis for bitcoin) for invreq_chain. -
if the invoice is in response to an invoice_request:
- MUST copy all non-signature fields from the invoice request
(including unknown fields). - if invreq_amount is
present: - MUST set invoice_amount to
invreq_amount - otherwise: - MUST set
invoice_amount to the expected amount. -
MUST set invoice_payment_hash to the SHA256 hash of
the payment_preimage that will be given in return for
payment. - if offer_issuer_id is present: - MUST set
invoice_node_id to the offer_issuer_id -
otherwise, if offer_paths is present: - MUST set
invoice_node_id to the final
blinded_node_id on the path it received the invoice
request - MUST specify exactly one signature TLV element:
signature. - MUST set sig to the
signature using invoice_node_id as described in Signature
Calculation. - if it requires multiple parts to pay the
invoice: - MUST set
invoice_features.features bit
MPP/compulsory - or if it allows multiple parts to
pay the invoice: - MUST set
invoice_features.features bit
MPP/optional - if the expiry for accepting payment is
not 7200 seconds after invoice_created_at: - MUST set
invoice_relative_expiry.seconds_from_creation
to the number of seconds after invoice_created_at
that payment of this invoice should not be attempted. - if it
accepts onchain payments: - MAY specify
invoice_fallbacks - SHOULD specify
invoice_fallbacks in order of most-preferred to
least-preferred if it has a preference. - for the bitcoin chain,
it MUST set each fallback_address with
version as a valid witness version and
address as a valid witness program - MUST include
invoice_paths containing one or more paths to the
node. - MUST specify invoice_paths in order of
most-preferred to least-preferred if it has a preference. - MUST
include invoice_blindedpay with exactly one
blinded_payinfo for each blinded_path in
paths, in order. - MUST set features in
each blinded_payinfo to match
encrypted_data_tlv.allowed_features (or
empty, if no allowed_features). - SHOULD ignore any
payment which does not use one of the paths. - MUST NOT set any
non-signature TLV fields outside the inclusive ranges: 0 to 239
and 1000000000 to 3999999999.
A reader of an invoice: - MUST reject the invoice if
invoice_amount is not present. - MUST reject the
invoice if invoice_created_at is not present. - MUST
reject the invoice if invoice_payment_hash is not
present. - MUST reject the invoice if invoice_node_id
is not present. - if invreq_chain is not present: -
MUST reject the invoice if bitcoin is not a supported chain. -
otherwise: - MUST reject the invoice if
invreq_chain.chain is not a supported
chain. - if invoice_features contains unknown
odd bits that are non-zero: - MUST ignore the bit. - if
invoice_features contains unknown even bits
that are non-zero: - MUST reject the invoice. - if
invoice_relative_expiry is present: - MUST reject the
invoice if the current time since 1970-01-01 UTC is greater than
invoice_created_at plus
seconds_from_creation. - otherwise: - MUST reject the
invoice if the current time since 1970-01-01 UTC is greater than
invoice_created_at plus 7200. - MUST reject the
invoice if invoice_paths is not present or is empty.
- MUST reject the invoice if num_hops is 0 in any
blinded_path in invoice_paths. - MUST
reject the invoice if invoice_blindedpay is not
present. - MUST reject the invoice if
invoice_blindedpay does not contain exactly one
blinded_payinfo per
invoice_paths.blinded_path. - For each
invoice_blindedpay.payinfo: - MUST NOT
use the corresponding invoice_paths.path
if payinfo.features has any unknown even
bits set. - MUST reject the invoice if this leaves no usable
paths. - if the invoice is a response to an
invoice_request: - MUST reject the invoice if all
fields in ranges 0 to 159 and 1000000000 to 2999999999 (inclusive)
do not exactly match the invoice request. - if
offer_issuer_id is present (invoice_request for an
offer): - MUST reject the invoice if invoice_node_id
is not equal to offer_issuer_id - otherwise, if
offer_paths is present (invoice_request for an offer
without id): - MUST reject the invoice if
invoice_node_id is not equal to the final
blinded_node_id it sent the invoice request to. -
otherwise (invoice_request without an offer): - MAY reject the
invoice if it cannot confirm that invoice_node_id is
correct, out-of-band. - MUST reject the invoice if
signature is not a valid signature using
invoice_node_id as described in Signature
Calculation. - SHOULD prefer to use earlier
invoice_paths over later ones if it has no other
reason for preference. - if invoice_features contains
the MPP/compulsory bit: - MUST pay the invoice via multiple
separate blinded paths. - otherwise, if
invoice_features contains the MPP/optional bit: - MAY
pay the invoice via multiple separate payments. - otherwise: -
MUST NOT use multiple parts to pay the invoice. - if
invreq_amount is present: - MUST reject the invoice
if invoice_amount is not equal to
invreq_amount - otherwise: - SHOULD confirm
authorization if invoice_amount.msat is
not within the amount range authorized. - for the bitcoin chain,
if the invoice specifies invoice_fallbacks: - MUST
ignore any fallback_address for which
version is greater than 16. - MUST ignore any
fallback_address for which address is
less than 2 or greater than 40 bytes. - MUST ignore any
fallback_address for which address does
not meet known requirements for the given version -
if invreq_paths is present: - MUST reject the invoice
if it did not arrive via one of those paths. - otherwise, neither
offer_issuer_id nor offer_paths are
present (not derived from an offer): - MUST reject the invoice if
it arrived via a blinded path. - otherwise (derived from an
offer): - MUST reject the invoice if it did not arrive via invoice
request onionmsg_tlv reply_path.
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 to make invreq_metadata both
unpredictable and unique, 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 the future. The redundant
requirement to check invreq_chain explicitly is a nod
to this: if the invoice is a response to an invoice request, that
field must have existed due to the invoice request requirements,
and we also require it to be mirrored here.
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
tlv_stream:invoice_error- types:
- type: 1 (
erroneous_field) - data:
- [
tu64:tlv_fieldnum]
- [
- type: 3 (
suggested_value) - data:
- [
...*byte:value]
- [
- type: 5 (
error) - data:
- [
...*utf8:msg]
- [
- type: 1 (
Requirements
A writer of an invoice_error: - MUST set error to
an explanatory string. - MAY set erroneous_field to a
specific field number in the invoice or
invoice_request which had a problem. - if it sets
erroneous_field: - MAY set
suggested_value. - if it sets
suggested_value: - MUST set
suggested_value to a valid field for that
tlv_fieldnum. - otherwise: - MUST NOT set
suggested_value.
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 the 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.
Payer Proofs
Payer proofs are proofs of invoice payment; the human-readable
prefix for payer proofs is lnp.
The payer proof is based on the invoice TLV
stream, with the exception that invreq_metadata
cannot be included. Various other invoice fields may be omitted
for privacy: numbers corresponding to (but not identical to) their
position in the invoice are included, as well as the minimal
hashes for missing merkle branches, to allow verification of the
invoicing node’s signature.
To prove that this payer_proof was created by
someone who has the secret key used to request the invoice in the
first place, it includes a signature using the
invreq_payer_id: this signs the proof fields and
invoice fields.
TLV Fields for
payer_proof
tlv_stream:payer_proof- types:
- type: 2 (
offer_chains) - data:
- [
...*chain_hash:chains]
- [
- type: 4 (
offer_metadata) - data:
- [
...*byte:data]
- [
- type: 6 (
offer_currency) - data:
- [
...*utf8:iso4217]
- [
- type: 8 (
offer_amount) - data:
- [
tu64:amount]
- [
- type: 10 (
offer_description) - data:
- [
...*utf8:description]
- [
- type: 12 (
offer_features) - data:
- [
...*byte:features]
- [
- type: 14 (
offer_absolute_expiry) - data:
- [
tu64:seconds_from_epoch]
- [
- type: 16 (
offer_paths) - data:
- [
...*blinded_path:paths]
- [
- type: 18 (
offer_issuer) - data:
- [
...*utf8:issuer]
- [
- type: 20 (
offer_quantity_max) - data:
- [
tu64:max]
- [
- type: 22 (
offer_issuer_id) - data:
- [
point:id]
- [
- type: 80 (
invreq_chain) - data:
- [
chain_hash:chain]
- [
- type: 82 (
invreq_amount) - data:
- [
tu64:msat]
- [
- type: 84 (
invreq_features) - data:
- [
...*byte:features]
- [
- type: 86 (
invreq_quantity) - data:
- [
tu64:quantity]
- [
- type: 88 (
invreq_payer_id) - data:
- [
point:key]
- [
- type: 89 (
invreq_payer_note) - data:
- [
...*utf8:note]
- [
- type: 90 (
invreq_paths) - data:
- [
...*blinded_path:paths]
- [
- type: 91 (
invreq_bip_353_name) - data:
- [
u8:name_len] - [
name_len*byte:name] - [
u8:domain_len] - [
domain_len*byte:domain]
- [
- type: 160 (
invoice_paths) - data:
- [
...*blinded_path:paths]
- [
- type: 162 (
invoice_blindedpay) - data:
- [
...*blinded_payinfo:payinfo]
- [
- type: 164 (
invoice_created_at) - data:
- [
tu64:timestamp]
- [
- type: 166 (
invoice_relative_expiry) - data:
- [
tu32:seconds_from_creation]
- [
- type: 168 (
invoice_payment_hash) - data:
- [
sha256:payment_hash]
- [
- type: 170 (
invoice_amount) - data:
- [
tu64:msat]
- [
- type: 172 (
invoice_fallbacks) - data:
- [
...*fallback_address:fallbacks]
- [
- type: 174 (
invoice_features) - data:
- [
...*byte:features]
- [
- type: 176 (
invoice_node_id) - data:
- [
point:node_id]
- [
- type: 240 (
signature) - data:
- [
bip340sig:sig]
- [
- type: 241 (
proof_signature) - data:
- [
bip340sig:sig]
- [
- type: 1001 (
proof_preimage) - data:
- [
32*byte:preimage]
- [
- type: 1002 (
proof_omitted_tlvs) - data:
- [
...*bigsize:missing]
- [
- type: 1003 (
proof_missing_hashes) - data:
- [
...*sha256:hashes]
- [
- type: 1004 (
proof_leaf_hashes) - data:
- [
...*sha256:hashes]
- [
- type: 1005 (
proof_note) - data:
- [
...*utf8:note]
- [
- type: 2 (
Requirements
A writer of a payer_proof: - MUST NOT include
invreq_metadata. - MUST include
invreq_payer_id, invoice_payment_hash,
invoice_node_id, signature and (if
present) invoice_features from the invoice. - MUST
include proof_preimage containing the
payment_preimage returned from successful payment of
this invoice. - For each non-signature TLV in the invoice in
ascending-type order: - If the field is to be included in the
payer_proof: - MUST copy it into the payer_proof. - MUST append
the nonce (H(“LnNonce”||TLV0,type)) to
proof_leaf_hashes. - otherwise, if the TLV type is
not zero: - MUST append a marker number to
proof_omitted_tlvs - If the previous TLV type was
included: - The marker number is that previous tlv type,
plus one. - Otherwise, if proof_omitted_tlvs is
empty: - The marker number is 1. - Otherwise, if the last
proof_omitted_tlvs entry is 239: - The marker
number is 1000000000. - Otherwise: - The marker
number is one greater than the last
proof_omitted_tlvs entry. - If
proof_omitted_tlvs is empty: - MAY omit
proof_omitted_tlvs from the payer_proof. - MAY
include an annotation on the proof in proof_note. -
MUST populate proof_missing_hashes with the merkle
hash of the omitted branch of each internal node that has exactly
one branch entirely omitted, in post-order depth-first
smallest-to-largest TLV order. - MUST copy signature
into the payer_proof. - MUST set proof_signature as
detailed in Signature
Calculation using the invreq_payer_id.
A reader of a payer_proof: - MUST reject the payer_proof if: -
invreq_payer_id, invoice_payment_hash,
invoice_node_id, signature,
proof_preimage, proof_missing_hashes,
proof_leaf_hashes or proof_signature are
missing. - SHA256(proof_preimage) does not equal
invoice_payment_hash. -
proof_omitted_tlvs are not in strict ascending order
(no duplicates). - proof_omitted_tlvs contains 0. -
proof_omitted_tlvs contains number outside both
ranges 1 to 239 and 1000000000 to 3999999999. -
proof_omitted_tlvs contains the number of an included
TLV field. - proof_omitted_tlvs contains a number
which is not: - one greater than an included TLV number, - one
greater than the previous proof_omitted_tlvs entry,
or 0 if it is the first number, or - 1000000000, if the previous
proof_omitted_tlvs entry is 239. -
proof_leaf_hashes does not contain exactly one hash
for each field in ranges 1 to 239 and 1000000000 to 3999999999. -
There are not exactly enough proof_missing_hashes to
reconstruct the merkle tree root using the
proof_omitted_tlvs values (with 0
implied as the first omitted TLV). - signature is not
a valid signature using invoice_node_id as described
in Signature
Calculation (with messagename “invoice”) of the
reconstructed merkle-root of the invoice (i.e. without fields 1001
through 999999999 inclusive). - proof_signature is
not a valid signature using invreq_payer_id as
described in Signature
Calculation.
Rationale
Using the invoice as a base enshrines information about the payment including important offer and invoice_request fields. However, many fields are not useful (such as payment paths), or may compromise privacy (such as invreq_payer_note containing delivery address information), so being able to elide them while still allowing signature validation is vital.
We disallow including invreq_metadata: that is the
hashing nonce, thus allowing brute-force of omitted fields.
invreq_payer_id is the key whose signature we have
to attach to the proof, and invoice_node_id and
signature are needed to validate the original
invoice. invoice_features may indicate additional
details in the future which would require additional fields to be
in the proof. Note that invoice_amount is not
compulsory, though it would probably be very useful in most
cases.
The requirement to include minimal hashes (rather than one for every unknown leaf) minimizes the size, especially when many consecutive fields are omitted. As the exact TLV types of omitted TLVs are unimportant (as long as ordering is maintained), we renumber them to be minimal, as further obfuscation of values.
The proof fields are outside the established offer, invoice
request and invoice TLV ranges, and above the signature range
(240-1000), so they are committed to by the
proof_signature.
The optional proof_note field allows a
challenge-response system to be implemented: someone requiring
proof can ask for a signature with a particular note. It can also
be missing.
Example for Payer Proofs
Consider a trivial TLV construct (not a valid invoice) which we are trying to prove, with the following fields:
0 - Omitted 10 - Omitted 20 - Omitted 30 - Omitted 40 - Included 50 - Omitted 60 - Omitted 240 - Omitted (signature field)
Here is the full signature Merkle tree, with omitted nodes
marked with (o):
____x____
______/ \_______
/ \
_x(o)* __x__
_/ \_ _/ \_
/ \ / \
x(o) x(o) x \
/ \ / \ / \ \
/ \ / \ / \ \
0(o) 10(o) 20(o) 30(o) 40 50(o) 60(o)
Note that the signature TLV 240 is not included in the merkle tree.
proof_leaf_hashes contains the nonce hashes for
the present non-signature TLVs:
- H(“LnNonce”||TLV0,40)
Since four adjacent nodes (0, 10 20 and 30) are omitted, we can (and must) simply provide the hash of the node above them, marked with an asterisk.
Thus, proof_missing_hashes contains the following
hashes in order:
- Merkle of H(“LnLeaf”,TLV50) and H(“LnNonce”||TLV0,50)
- Merkle of H(“LnLeaf”,TLV60) and H(“LnNonce”||TLV0,60)
- Merkle of (Merkle of (Merkle of H(“LnLeaf”,TLV0) and H(“LnNonce”||TLV0,0)) (Merkle of H(“LnLeaf”,TLV10) and H(“LnNonce”||TLV0,10))) and (Merkle of (Merkle of H(“LnLeaf”,TLV20) and H(“LnNonce”||TLV0,20)) (Merkle of H(“LnLeaf”,TLV30) and H(“LnNonce”||TLV0,30)))
In this example the correct proof_missing_hashes order is not ascending TLV order: the omitted subtree containing TLVs 0, 10, 20, and 30 is emitted after the omitted TLVs 50 and 60, because it is the missing sibling of their parent’s parent.
The proof_omitted_tlvs array is based on the
omitted tlvs: [0, 10, 20, 30, 50, 60]. It uses the minimal values
which hide the real field numbers without changing their order,
0 is implied (as it’s always omitted), giving an
array of [1, 2, 3, 41, 42].
The algorithm for creating proof_missing_hashes is
most easily implemented in a recursive fashion, traversing
smallest-to-largest TLV (left-to-right in the above
representation). When you need to combine two hashes where one
side is entirely omitted and the other is not, append that hash to
proof_missing_hashes.
Reconstruction is the exact opposite: when you need to combine
a hash where one side is entirely omitted and the other is not,
pull a hash from proof_missing_hashes. If there are
insufficient proof_missing_hashes, or it isn’t empty
when you have completed the merkle tree, the number of
proof_missing_hashes was incorrect.
See the Payer Proof Test Vectors for more examples.
FIXME: Possible future extensions:
- The offer can require delivery info in the
invoice_request. - An offer can be updated: the response to an
invoice_requestis another offer, perhaps with a signature from the originaloffer_issuer_id - 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.
- We could upgrade to allow multiple offers in one invreq and invoice, to make a shopping list.
- All-zero offer_id == gratuitous payment.
- Streaming invoices?
- Re-add recurrence.
- Re-add
invoice_replacefor requesting replacement of a (stuck-payment) invoice with a new one. - Allow non-offer
invoice_requestwith alternate currencies? - Add
offer_quantity_unitto indicate stepping for quantity (e.g. 100 grams).
[1] https://www.youtube.com/watch?v=4SYc_flMnMQ