The BOLT 11 invoice format has proven popular but has several limitations:
h
field was a boutique extraction of the d
field only.p
was often mishandled, and amounts in pico-bitcoin are harder
than the modern satoshi-based counting.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.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:
The merchant-pays-user flow (e.g. ATM or refund):
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.
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.
Readers of a bolt12 string:
+
followed by zero or more whitespace characters between
two bech32 characters:+
and whitespace.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
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:
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 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_stream
: offer
offer_chains
)...*chain_hash
:chains
]offer_metadata
)...*byte
:data
]offer_currency
)...*utf8
:iso4217
]offer_amount
)tu64
:amount
]offer_description
)...*utf8
:description
]offer_features
)...*byte
:features
]offer_absolute_expiry
)tu64
:seconds_from_epoch
]offer_paths
)...*blinded_path
:paths
]offer_issuer
)...*utf8
:issuer
]offer_quantity_max
)tu64
:max
]offer_node_id
)point
:node_id
]A writer of an offer:
offer_node_id
to the node's public key to request the invoice from.offer_description
to a complete description of the purpose
of the payment.offer_chains
the offer is valid for.offer_chains
, implying that bitcoin is only chain.offer_amount
is required for successful payment:offer_amount
to the amount expected (per item).offer_amount
is that of all entries in chains
:amount
in multiples of the minimum lightning-payable unit
(e.g. milli-satoshis for bitcoin).offer_currency
iso4217
as an ISO 4712 three-letter code.offer_amount
in the currency unit adjusted by the ISO 4712
exponent (e.g. USD cents).offer_amount
offer_currency
offer_metadata
for its own use.offer_features
.features
to the bitmap of bolt12 features.offer_absolute_expiry
seconds_from_epoch
to the number of seconds
after midnight 1 January 1970, UTC that invoice_request should not be
attempted.offer_paths
containing one or more paths to the node from
publicly reachable nodes.offer_paths
.offer_paths
:offer_issuer
:offer_quantity_max
.offer_quantity_max
to 0.offer_quantity_max
to 0.offer_quantity_max
.A reader of an offer:
offer_features
contains unknown odd bits that are non-zero:offer_features
contains unknown even bits that are non-zero:offer_chains
is not set:offer_chains
is set):chains
:offer_description
is not set:offer_node_id
is not set:offer_amount
to provide the user with a cost estimate:offer_amount
:offer_currency
field if setoffer_absolute_expiry
.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 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.
invoice_request
tlv_stream
: invoice_request
invreq_metadata
)...*byte
:blob
]offer_chains
)...*chain_hash
:chains
]offer_metadata
)...*byte
:data
]offer_currency
)...*utf8
:iso4217
]offer_amount
)tu64
:amount
]offer_description
)...*utf8
:description
]offer_features
)...*byte
:features
]offer_absolute_expiry
)tu64
:seconds_from_epoch
]offer_paths
)...*blinded_path
:paths
]offer_issuer
)...*utf8
:issuer
]offer_quantity_max
)tu64
:max
]offer_node_id
)point
:node_id
]invreq_chain
)chain_hash
:chain
]invreq_amount
)tu64
:msat
]invreq_features
)...*byte
:features
]invreq_quantity
)tu64
:quantity
]invreq_payer_id
)point
:key
]invreq_payer_note
)...*utf8
:note
]signature
)bip340sig
:sig
]The writer:
offer_chains
is set:invreq_chain
to one of offer_chains
unless that chain is bitcoin, in which case it MAY omit invreq_chain
.invreq_chain
it MUST set it to bitcoin.signature
.sig
as detailed in Signature Calculation using the invreq_payer_id
.offer_amount
is not present:invreq_amount
.invreq_amount
.invreq_amount
:invreq_amount
.msat
as greater or equal to amount expected by offer_amount
(and, if present, offer_currency
and invreq_quantity
).invreq_payer_id
to a transient public key.invreq_payer_id
.offer_quantity_max
is present:invreq_quantity
to greater than zero.offer_quantity_max
is non-zero:invreq_quantity
less than or equal to offer_quantity_max
.invreq_quantity
offer_description
, offer_absolute_expiry
, offer_paths
and offer_issuer
as it would for an offer.invreq_payer_id
as it would set offer_node_id
for an offer.signature
, offer_metadata
, offer_chains
, offer_amount
, offer_currency
, offer_features
, offer_quantity_max
or offer_node_id
invreq_chain
the offer is valid for.invreq_amount
.invreq_metadata
to an unpredictable series of bytes.invreq_amount
: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
).invreq_features
.features
to the bitmap of features.The reader:
invreq_payer_id
or invreq_metadata
are not present.invreq_features
contains unknown odd bits that are non-zero:invreq_features
contains unknown even bits that are non-zero:signature
is not correct as detailed in Signature Calculation using the invreq_payer_id
.offer_node_id
is present (response to an offer):offer_quantity_max
is present:invreq_quantity
field.offer_quantity_max
is non-zero:invreq_quantity
is zero, OR greater than offer_quantity_max
.invreq_quantity
field.offer_amount
is present:offer_amount
:offer_currency
is not the invreq_chain
currency, convert to the
invreq_chain
currency.invreq_quantity
is present, multiply by invreq_quantity
.quantity
.invreq_amount
is present:invreq_amount
.msat
is less than the expected amount.invreq_amount
.msat
greatly exceeds the expected amount.offer_amount
):invreq_amount
.onionmsg_tlv
reply_path
.offer_node_id
, not a response to our offer):offer_chains
, offer_features
or offer_quantity_max
.invreq_amount
is not present.offer_amount
(or offer_currency
) for informational display to user.offer_paths
if present, otherwise MUST use invreq_payer_id
as the node id to send to.invreq_chain
is not present:invreq_chain
.chain
is not a supported chain.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_request
s 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 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.
tlv_stream
: invoice
types:
invreq_metadata
)...*byte
:blob
]offer_chains
)...*chain_hash
:chains
]offer_metadata
)...*byte
:data
]offer_currency
)...*utf8
:iso4217
]offer_amount
)tu64
:amount
]offer_description
)...*utf8
:description
]offer_features
)...*byte
:features
]offer_absolute_expiry
)tu64
:seconds_from_epoch
]offer_paths
)...*blinded_path
:paths
]offer_issuer
)...*utf8
:issuer
]offer_quantity_max
)tu64
:max
]offer_node_id
)point
:node_id
]invreq_chain
)chain_hash
:chain
]invreq_amount
)tu64
:msat
]invreq_features
)...*byte
:features
]invreq_quantity
)tu64
:quantity
]invreq_payer_id
)point
:key
]invreq_payer_note
)...*utf8
:note
]invoice_paths
)...*blinded_path
:paths
]invoice_blindedpay
)...*blinded_payinfo
:payinfo
]invoice_created_at
)tu64
:timestamp
]invoice_relative_expiry
)tu32
:seconds_from_creation
]invoice_payment_hash
)sha256
:payment_hash
]invoice_amount
)tu64
:msat
]invoice_fallbacks
)...*fallback_address
:fallbacks
]invoice_features
)...*byte
:features
]invoice_node_id
)point
:node_id
]signature
)bip340sig
:sig
]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
byte
:version
]u16
:len
]len*byte
:address
]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.
A writer of an invoice:
invoice_created_at
to the number of seconds since Midnight 1
January 1970, UTC when the invoice was created.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
.invoice_request
:invoice_request
(including unknown fields).invreq_amount
is present:invoice_amount
to invreq_amount
invoice_amount
to the expected amount.invreq_chain
as it would for an invoice_request.offer_description
as it would for an offer.invreq_payer_id
or offer_node_id
.invoice_payment_hash
to the SHA256 hash of the
payment_preimage
that will be given in return for payment.offer_node_id
is present:invoice_node_id
to offer_node_id
.invoice_node_id
to a valid public key.signature
.sig
to the signature using invoice_node_id
as described in Signature Calculation.invoice_features
.features
bit MPP/compulsory
invoice_features
.features
bit MPP/optional
invoice_created_at
:invoice_relative_expiry
.seconds_from_creation
to the number of
seconds after invoice_created_at
that payment of this invoice should not be attempted.invoice_fallbacks
invoice_fallbacks
in order of most-preferred to least-preferred
if it has a preference.fallback_address
with
version
as a valid witness version and address
as a valid witness
programinvoice_paths
containing one or more paths to the node.invoice_paths
in order of most-preferred to least-preferred if it has a preference.invoice_blindedpay
with exactly one blinded_payinfo
for each blinded_path
in paths
, in order.features
in each blinded_payinfo
to match encrypted_data_tlv
.allowed_features
(or empty, if no allowed_features
).offer_node_id
is present, and invreq_payer_id
is identical to a previous invoice_request
:A reader of an invoice:
invoice_amount
is not present.invoice_created_at
is not present.invoice_payment_hash
is not present.invoice_node_id
is not present.invoice_features
contains unknown odd bits that are non-zero:invoice_features
contains unknown even bits that are non-zero:invoice_relative_expiry
is present:invoice_created_at
plus seconds_from_creation
.invoice_created_at
plus 7200.invoice_paths
is not present or is empty.invoice_blindedpay
is not present.invoice_blindedpay
does not contain exactly one blinded_payinfo
per invoice_paths
.blinded_path
.invoice_blindedpay
.payinfo
:invoice_paths
.path
if payinfo
.features
has any unknown even bits set.invoice_request
:invoice_request
.offer_node_id
is present (invoice_request for an offer):invoice_node_id
is not equal to offer_node_id
.invoice_node_id
is correct, out-of-band.signature
is not a valid signature using invoice_node_id
as described in Signature Calculation.invoice_paths
over later ones if it has no other reason for preference.invoice_features
contains the MPP/compulsory bit:invoice_features
contains the MPP/optional bit:invoice_amount
.msat
is not within the amount range authorized.invoice_fallbacks
:fallback_address
for which version
is greater than 16.fallback_address
for which address
is less than 2 or greater than 40 bytes.fallback_address
for which address
does not meet known requirements for the given version
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.
Informative errors can be returned in an onion message invoice_error
field (via the onion reply_path
) for either invoice_request
or
invoice
.
invoice_error
tlv_stream
: invoice_error
erroneous_field
)tu64
:tlv_fieldnum
]suggested_value
)...*byte
:value
]error
)...*utf8
:msg
]A writer of an invoice_error:
error
to an explanatory string.erroneous_field
to a specific field number in the
invoice
or invoice_request
which had a problem.erroneous_field
:suggested_value
.suggested_value
:suggested_value
to a valid field for that tlv_fieldnum
.suggested_value
.A reader of an invoice_error: FIXME!
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_request
s 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.
invoice_request
.invoice_request
is another offer,
perhaps with a signature from the original offer_node_id
invreq_refund_for
to support proofs.invoice_replace
for requesting replacement of a (stuck-payment)
invoice with a new one.invoice_request
with alternate currencies?offer_quantity_unit
to indicate stepping for quantity
(e.g. 100 grams).