Skip to content

Commit

Permalink
More review feedback.
Browse files Browse the repository at this point in the history
1. From https://github.com/evansmj
   - Make it clear that UPPERCASE is good for QR codes
   - Added test vectors for that too.

2. From https://github.com/yyforyongyu:
   - Move the use of `offers_path` requirement to the `invoice_request` reader requirements, and make them explicit.
   - Use the same language for invoices & invreq_paths.
   - Change MAY to SHOULD for implying chain values (Bitcoin).
   - SHOULD->MUST for expired invoices
   - MUST->SHOULD for onchain addresses order
   - Various typos

3. From https://github.com/jkczyz
   - Add requirement for amount if currency set.
   - Make it clear that the payment_preimage from the payment is what we
     use to prove invoice completion.
   - Explicitly tell reader to check `invoice_amount` == `invreq_amount`

Offers test vectors fixed too.  Use bit 122 not 22 (that's anchors) which
is more clearly wrong.  And fix the "no desc, no amount" which was supposed
to be "no desc, with amount".

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
  • Loading branch information
rustyrussell committed Sep 10, 2024
1 parent 8dff88b commit db73bbb
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 15 deletions.
41 changes: 31 additions & 10 deletions 12-offer-encoding.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,14 @@ 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.
Expand Down Expand Up @@ -231,11 +238,11 @@ A writer of an offer:
- if the chain for the invoice is not solely bitcoin:
- MUST specify `offer_chains` the offer is valid for.
- otherwise:
- MAY omit `offer_chains`, implying that bitcoin is only chain.
- 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).
- if the currency for `offer_amount` is that of all entries in `chains`:
- MUST specify `amount` in multiples of the minimum lightning-payable unit
- 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 4712 three-letter code.
Expand All @@ -260,7 +267,6 @@ A writer of an offer:
- otherwise:
- MAY include `offer_paths`.
- if it includes `offer_paths`:
- SHOULD ignore any invoice_request which does not use the path.
- MAY set `offer_issuer_id`.
- otherwise:
- MUST set `offer_issuer_id` to the node's public key to request the invoice from.
Expand Down Expand Up @@ -294,6 +300,8 @@ A reader of an offer:
- 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 `offer_currency` 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`:
Expand All @@ -303,10 +311,10 @@ A reader of an offer:
- `offer_currency` field if set
- otherwise, the minimum lightning-payable unit (e.g. milli-satoshis for
bitcoin).
- MUST warn user if amount of actual invoice differs significantly
- MUST warn the user if the received `invoice_amount` differs significantly
from that estimate.
- SHOULD not respond to an offer if the current time is after
`offer_absolute_expiry`.
- if the current time is after `offer_absolute_expiry`:
- MUST NOT respond to the offer.
- if it chooses to sends 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
Expand Down Expand Up @@ -439,7 +447,7 @@ 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 MAY omit `invreq_chain`.
- 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](#signature-calculation) using the `invreq_payer_id`.
Expand Down Expand Up @@ -490,6 +498,10 @@ The reader:
- MUST NOT reply with a previous invoice.
- if `offer_issuer_id` or `offer_paths` are present (response to an offer):
- MUST fail the 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 fail the request if there is no `invreq_quantity` field.
- if `offer_quantity_max` is non-zero:
Expand Down Expand Up @@ -545,10 +557,12 @@ 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,
it can be combined with the invoice to form a cryptographic receipt.
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.
Expand Down Expand Up @@ -705,7 +719,7 @@ A writer of an invoice:
seconds after `invoice_created_at` that payment of this invoice should not be attempted.
- if it accepts onchain payments:
- MAY specify `invoice_fallbacks`
- MUST specify `invoice_fallbacks` in order of most-preferred to least-preferred
- 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
Expand Down Expand Up @@ -752,11 +766,18 @@ A reader of an invoice:
- MAY pay the invoice via multiple separate payments.
- otherwise:
- MUST NOT use multiple parts to pay the invoice.
- SHOULD confirm authorization if `invoice_amount`.`msat` is not within the amount range authorized.
- 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 ignore the invoice if it did not arrive via one of those paths.
- otherwise:
- MUST ignore any invoice if it arrived via a blinded path.

## Rationale

Expand Down
10 changes: 10 additions & 0 deletions bolt12/format-string-test.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@
"valid": true,
"string": "lno1pqps7sjqpgtyzm3qv4uxzmtsd3jjqer9wd3hy6tsw35k7msjzfpy7nz5yqcnygrfdej82um5wf5k2uckyypwa3eyt44h6txtxquqh7lz5djge4afgfjn7k4rgrkuag0jsd5xvxg"
},
{
"comment": "Uppercase is valid",
"valid": true,
"string": "LNO1PQPS7SJQPGTYZM3QV4UXZMTSD3JJQER9WD3HY6TSW35K7MSJZFPY7NZ5YQCNYGRFDEJ82UM5WF5K2UCKYYPWA3EYT44H6TXTXQUQH7LZ5DJGE4AFGFJN7K4RGRKUAG0JSD5XVXG"
},
{
"comment": "+ can join anywhere",
"valid": true,
Expand All @@ -19,6 +24,11 @@
"valid": true,
"string": "lno1pqps7sjqpgt+ yzm3qv4uxzmtsd3jjqer9wd3hy6tsw3+ 5k7msjzfpy7nz5yqcn+\nygrfdej82um5wf5k2uckyypwa3eyt44h6txtxquqh7lz5djge4afgfjn7k4rgrkuag0jsd+\r\n 5xvxg"
},
{
"comment": "+ can be followed by whitespace, UPPERCASE",
"valid": true,
"string": "LNO1PQPS7SJQPGT+ YZM3QV4UXZMTSD3JJQER9WD3HY6TSW3+ 5K7MSJZFPY7NZ5YQCN+\nYGRFDEJ82UM5WF5K2UCKYYPWA3EYT44H6TXTXQUQH7LZ5DJGE4AFGFJN7K4RGRKUAG0JSD+\r\n 5XVXG"
},
{
"comment": "+ must be surrounded by bech32 characters",
"valid": false,
Expand Down
33 changes: 28 additions & 5 deletions bolt12/offers-test.json
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,29 @@
}
]
},
{
"description": "same, with blinded path first_node_id using sciddir",
"valid": true,
"bolt12": "lno1pgx9getnwss8vetrw3hhyucs3yqqqqqqqqqqqqp2qgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqqyqqqqqqqqqqqqqqqqqqqqqqqqqqqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqqgzyg3zyg3zyg3z93pqthvwfzadd7jejes8q9lhc4rvjxd022zv5l44g6qah82ru5rdpnpj",
"field info": "short_channel_id is 0x0x42, direction is 0",
"fields": [
{
"type": 10,
"length": 12,
"hex": "5465737420766563746f7273"
},
{
"type": 16,
"length": 137,
"hex": "00000000000000002a0202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020200100000000000000000000000000000000002020202020202020202020202020202020202020202020202020202020202020200081111111111111111"
},
{
"type": 22,
"length": 33,
"hex": "02eec7245d6b7d2ccb30380bfbe2a3648cd7a942653f5aa340edcea1f283686619"
}
]
},
{
"description": "with no issuer_id and blinded path via Bob (0x424242...), blinding 020202...",
"valid": true,
Expand Down Expand Up @@ -541,17 +564,17 @@
"bolt12": "lno1pgz5znzfgdz3vggzqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgp06wu6egp9qgr0u2xq4dh3kdevrf4zg6hx8a60jv0gxe0ptgyfc6xkryqqqqqqqq"
},
{
"description": "Contains unknown feature 22",
"description": "Contains unknown feature 122",
"valid": false,
"bolt12": "lno1pgx9getnwss8vetrw3hhyucvqdqqqqqkyypwa3eyt44h6txtxquqh7lz5djge4afgfjn7k4rgrkuag0jsd5xvxg"
"bolt12": "lno1pgx9getnwss8vetrw3hhyucvzqzqqqqqqqqqqqqqqqqqqqqqqqqpvggzamrjghtt05kvkvpcp0a79gmy3nt6jsn98ad2xs8de6sl9qmgvcvs"
},
{
"description": "Missing offer_description and offer_amount",
"description": "Missing offer_description, but has offer_amount",
"valid": false,
"bolt12": "lno1zcss9mk8y3wkklfvevcrszlmu23kfrxh49px20665dqwmn4p72pksese"
"bolt12": "lno1pqpzwyqkyypwa3eyt44h6txtxquqh7lz5djge4afgfjn7k4rgrkuag0jsd5xvxg"
},
{
"description": "Missing offer_issuer_id",
"description": "Missing offer_issuer_id and no offer_path",
"valid": false,
"bolt12": "lno1pgx9getnwss8vetrw3hhyuc"
},
Expand Down

0 comments on commit db73bbb

Please sign in to comment.