bolt12: update comments to match latest spec.

No code changes.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
Rusty Russell
2022-11-09 13:02:01 +10:30
committed by Christian Decker
parent 37bc4603b8
commit 8a217f13cf
10 changed files with 81 additions and 75 deletions

View File

@@ -17,17 +17,19 @@ bool bolt12_chains_match(const struct bitcoin_blkid *chains,
{ {
/* BOLT-offers #12: /* BOLT-offers #12:
* - if the chain for the invoice is not solely bitcoin: * - if the chain for the invoice is not solely bitcoin:
* - MUST specify `chains` the offer is valid for. * - MUST specify `offer_chains` the offer is valid for.
* - otherwise: * - otherwise:
* - the bitcoin chain is implied as the first and only entry. * - MAY omit `offer_chains`, implying that bitcoin is only chain.
*/ */
/* BOLT-offers #12: /* BOLT-offers #12:
* The reader of an invoice_request: * A reader of an offer:
*... *...
* - if `chain` is not present: * - if `offer_chains` is not set:
* - MUST fail the request if bitcoin is not a supported chain. * - if the node does not accept bitcoin invoices:
* - otherwise: * - MUST NOT respond to the offer
* - MUST fail the request if `chain` is not a supported chain. * - otherwise: (`offer_chains` is set):
* - if the node does not accept invoices for any of the `chains`:
* - MUST NOT respond to the offer
*/ */
if (!chains) { if (!chains) {
max_num_chains = 1; max_num_chains = 1;
@@ -556,7 +558,9 @@ struct tlv_invoice *invoice_for_invreq(const tal_t *ctx,
/* BOLT-offers #12: /* BOLT-offers #12:
* A writer of an invoice: * A writer of an invoice:
* - MUST copy all non-signature fields from the invreq (including *...
* - if the invoice is in response to an `invoice_request`:
* - MUST copy all non-signature fields from the `invoice_request` (including
* unknown fields). * unknown fields).
*/ */
len = tlv_span(wire, 0, 159, &start); len = tlv_span(wire, 0, 159, &start);

View File

@@ -10,12 +10,12 @@
struct feature_set; struct feature_set;
/* BOLT-offers #12: /* BOLT-offers #12:
* - if `relative_expiry` is present: * - if `invoice_relative_expiry` is present:
* - MUST reject the invoice if the current time since 1970-01-01 UTC * - MUST reject the invoice if the current time since 1970-01-01 UTC
* is greater than `created_at` plus `seconds_from_creation`. * is greater than `invoice_created_at` plus `seconds_from_creation`.
* - otherwise: * - otherwise:
* - MUST reject the invoice if the current time since 1970-01-01 UTC * - MUST reject the invoice if the current time since 1970-01-01 UTC
* is greater than `created_at` plus 7200. * is greater than `invoice_created_at` plus 7200.
*/ */
#define BOLT12_DEFAULT_REL_EXPIRY 7200 #define BOLT12_DEFAULT_REL_EXPIRY 7200

View File

@@ -10,7 +10,8 @@
#endif #endif
/* BOLT-offers #12: /* BOLT-offers #12:
* TLV types 240 through 1000 are considered signature elements. * Each form is signed using one or more *signature TLV elements*: TLV
* types 240 through 1000 (inclusive).
*/ */
static bool is_signature_field(const struct tlv_field *field) static bool is_signature_field(const struct tlv_field *field)
{ {
@@ -193,18 +194,17 @@ void merkle_tlv(const struct tlv_field *fields, struct sha256 *merkle)
/* BOLT-offers #12: /* BOLT-offers #12:
* All signatures are created as per * All signatures are created as per
* [BIP-340](https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki), * [BIP-340](https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki)
* and tagged as recommended there. Thus we define H(`tag`,`msg`) as * and tagged as recommended there. Thus we define H(`tag`,`msg`) as
* SHA256(SHA256(`tag`) || SHA256(`tag`) || `msg`), and SIG(`tag`,`msg`,`key`) * SHA256(SHA256(`tag`) || SHA256(`tag`) || `msg`), and SIG(`tag`,`msg`,`key`)
* as the signature of H(`tag`,`msg`) using `key`. * as the signature of H(`tag`,`msg`) using `key`.
* *
* Each form is signed using one or more TLV signature elements; TLV * Each form is signed using one or more *signature TLV elements*: TLV types
* types 240 through 1000 are considered signature elements. For these * 240 through 1000 (inclusive). For these, the tag is "lightning" ||
* the tag is "lightning" || `messagename` || `fieldname`, and `msg` is the * `messagename` || `fieldname`, and `msg` is the Merkle-root; "lightning" is
* Merkle-root; "lightning" is the literal 9-byte ASCII string, * the literal 9-byte ASCII string, `messagename` is the name of the TLV
* `messagename` is the name of the TLV stream being signed (i.e. "offer", * stream being signed (i.e. "invoice_request" or "invoice") and the
* "invoice_request" or "invoice") and the `fieldname` is the TLV field * `fieldname` is the TLV field containing the signature (e.g. "signature").
* containing the signature (e.g. "signature" or "refund_signature").
*/ */
void sighash_from_merkle(const char *messagename, void sighash_from_merkle(const char *messagename,
const char *fieldname, const char *fieldname,

View File

@@ -5,8 +5,8 @@
/* BOLT-offers #12: /* BOLT-offers #12:
* *
* - MUST specify `iso4217` as an ISO 4712 three-letter code. * - MUST specify `offer_currency` `iso4217` as an ISO 4712 three-letter code.
* - MUST specify `amount` in the currency unit adjusted by the ISO 4712 * - MUST specify `offer_amount` in the currency unit adjusted by the ISO 4712
* exponent (e.g. USD cents). * exponent (e.g. USD cents).
*/ */
struct iso4217_name_and_divisor { struct iso4217_name_and_divisor {

View File

@@ -362,12 +362,12 @@ static void print_relative_expiry(u64 *created_at, u32 *relative)
return; return;
/* BOLT-offers #12: /* BOLT-offers #12:
* - if `relative_expiry` is present: * - if `invoice_relative_expiry` is present:
* - MUST reject the invoice if the current time since 1970-01-01 UTC * - MUST reject the invoice if the current time since 1970-01-01 UTC
* is greater than `created_at` plus `seconds_from_creation`. * is greater than `invoice_created_at` plus `seconds_from_creation`.
* - otherwise: * - otherwise:
* - MUST reject the invoice if the current time since 1970-01-01 UTC * - MUST reject the invoice if the current time since 1970-01-01 UTC
* is greater than `created_at` plus 7200. * is greater than `invoice_created_at` plus 7200.
*/ */
if (!relative) if (!relative)
printf("invoice_relative_expiry: %u (%s) (default)\n", printf("invoice_relative_expiry: %u (%s) (default)\n",

View File

@@ -463,8 +463,8 @@ static struct command_result *json_createinvoicerequest(struct command *cmd,
} }
/* BOLT-offers #12: /* BOLT-offers #12:
* - MUST set `signature` `sig` as detailed in * - MUST set `signature`.`sig` as detailed in
* [Signature Calculation](#signature-calculation) using the `payer_key`. * [Signature Calculation](#signature-calculation) using the `invreq_payer_id`.
*/ */
/* This populates the ->fields from our entries */ /* This populates the ->fields from our entries */
invreq->fields = tlv_make_fields(invreq, tlv_invoice_request); invreq->fields = tlv_make_fields(invreq, tlv_invoice_request);

View File

@@ -266,8 +266,8 @@ static struct command_result *handle_invreq_response(struct command *cmd,
json_add_string(out, "invoice", invoice_encode(tmpctx, inv)); json_add_string(out, "invoice", invoice_encode(tmpctx, inv));
json_object_start(out, "changes"); json_object_start(out, "changes");
/* BOLT-offers #12: /* BOLT-offers #12:
* - SHOULD confirm authorization if `msat` is not within the amount * - SHOULD confirm authorization if `invoice_amount`.`msat` is not within
* range authorized. * the amount range authorized.
*/ */
/* We always tell them this unless it's trivial to calc and /* We always tell them this unless it's trivial to calc and
* exactly as expected. */ * exactly as expected. */
@@ -436,17 +436,13 @@ static struct command_result *param_offer(struct command *cmd,
fail)); fail));
/* BOLT-offers #12: /* BOLT-offers #12:
* A reader of an offer: * A reader of an offer:
* - if the offer contains any unknown TLV fields greater or equal to 80: * - if the offer contains any TLV fields greater or equal to 80:
* - MUST NOT respond to the offer. * - MUST NOT respond to the offer.
* - if `offer_features` contains unknown _odd_ bits that are non-zero: * - if `offer_features` contains unknown _odd_ bits that are non-zero:
* - MUST ignore the bit. * - MUST ignore the bit.
* - if `offer_features` contains unknown _even_ bits that are non-zero: * - if `offer_features` contains unknown _even_ bits that are non-zero:
* - MUST NOT respond to the offer. * - MUST NOT respond to the offer.
* - SHOULD indicate the unknown bit to the user. * - SHOULD indicate the unknown bit to the user.
* - if `offer_description` is not set:
* - MUST NOT respond to the offer.
* - if `offer_node_id` is not set:
* - MUST NOT respond to the offer.
*/ */
for (size_t i = 0; i < tal_count((*offer)->fields); i++) { for (size_t i = 0; i < tal_count((*offer)->fields); i++) {
if ((*offer)->fields[i].numtype > 80) { if ((*offer)->fields[i].numtype > 80) {
@@ -467,6 +463,13 @@ static struct command_result *param_offer(struct command *cmd,
"unknown feature %i", "unknown feature %i",
badf)); badf));
} }
/* BOLT-offers #12:
* - if `offer_description` is not set:
* - MUST NOT respond to the offer.
* - if `offer_node_id` is not set:
* - MUST NOT respond to the offer.
*/
if (!(*offer)->offer_description) if (!(*offer)->offer_description)
return command_fail_badparam(cmd, name, buffer, tok, return command_fail_badparam(cmd, name, buffer, tok,
"Offer does not contain a description"); "Offer does not contain a description");
@@ -1012,7 +1015,7 @@ static struct command_result *json_fetchinvoice(struct command *cmd,
/* BOLT-offers #12: /* BOLT-offers #12:
* - SHOULD not respond to an offer if the current time is after * - SHOULD not respond to an offer if the current time is after
* `absolute_expiry`. * `offer_absolute_expiry`.
*/ */
if (sent->offer->offer_absolute_expiry if (sent->offer->offer_absolute_expiry
&& time_now().ts.tv_sec > *sent->offer->offer_absolute_expiry) && time_now().ts.tv_sec > *sent->offer->offer_absolute_expiry)
@@ -1053,7 +1056,7 @@ static struct command_result *json_fetchinvoice(struct command *cmd,
/* BOLT-offers #12: /* BOLT-offers #12:
* - if `offer_quantity_max` is present: * - if `offer_quantity_max` is present:
* - MUST set `invreq_quantity` * - MUST set `invreq_quantity` to greater than zero.
* - if `offer_quantity_max` is non-zero: * - if `offer_quantity_max` is non-zero:
* - MUST set `invreq_quantity` less than or equal to * - MUST set `invreq_quantity` less than or equal to
* `offer_quantity_max`. * `offer_quantity_max`.
@@ -1062,6 +1065,9 @@ static struct command_result *json_fetchinvoice(struct command *cmd,
if (!invreq->invreq_quantity) if (!invreq->invreq_quantity)
return command_fail(cmd, JSONRPC2_INVALID_PARAMS, return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
"quantity parameter required"); "quantity parameter required");
if (*invreq->invreq_quantity == 0)
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
"quantity parameter must be non-zero");
if (*invreq->offer_quantity_max if (*invreq->offer_quantity_max
&& *invreq->invreq_quantity > *invreq->offer_quantity_max) && *invreq->invreq_quantity > *invreq->offer_quantity_max)
return command_fail(cmd, JSONRPC2_INVALID_PARAMS, return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
@@ -1448,9 +1454,9 @@ static struct command_result *json_sendinvoice(struct command *cmd,
return command_param_failed(); return command_param_failed();
/* BOLT-offers #12: /* BOLT-offers #12:
* The writer: * - if the invoice is in response to an `invoice_request`:
* - MUST copy all non-signature fields from the invreq (including * - MUST copy all non-signature fields from the `invoice_request`
* unknown fields). * (including unknown fields).
*/ */
sent->inv = invoice_for_invreq(sent, sent->invreq); sent->inv = invoice_for_invreq(sent, sent->invreq);

View File

@@ -292,9 +292,8 @@ static bool json_add_blinded_paths(struct json_stream *js,
json_array_end(js); json_array_end(js);
/* BOLT-offers #12: /* BOLT-offers #12:
* - MUST reject the invoice if `blinded_payinfo` does not contain * - MUST reject the invoice if `invoice_blindedpay` does not contain
* exactly as many `payinfo` as total `onionmsg_path` in * exactly one `blinded_payinfo` per `invoice_paths`.`blinded_path`.
* `blinded_path`.
*/ */
if (blindedpay && n != tal_count(blindedpay)) { if (blindedpay && n != tal_count(blindedpay)) {
json_add_string(js, "warning_invalid_invoice_blindedpay", json_add_string(js, "warning_invalid_invoice_blindedpay",
@@ -594,7 +593,7 @@ static bool json_add_fallbacks(struct json_stream *js,
json_add_hex_talarr(js, "hex", fallbacks[i]->address); json_add_hex_talarr(js, "hex", fallbacks[i]->address);
/* BOLT-offers #12: /* BOLT-offers #12:
* - for the bitcoin chain, if the invoice specifies `fallbacks`: * - for the bitcoin chain, if the invoice specifies `invoice_fallbacks`:
* - MUST ignore any `fallback_address` for which `version` is * - MUST ignore any `fallback_address` for which `version` is
* greater than 16. * greater than 16.
* - MUST ignore any `fallback_address` for which `address` is * - MUST ignore any `fallback_address` for which `address` is
@@ -749,14 +748,6 @@ static void json_add_b12_invoice(struct json_stream *js,
* exactly one `blinded_payinfo` per `invoice_paths`.`blinded_path`. * exactly one `blinded_payinfo` per `invoice_paths`.`blinded_path`.
*/ */
if (invoice->invoice_paths) { if (invoice->invoice_paths) {
/* BOLT-offers #12:
* - if `blinded_path` is present:
* - MUST reject the invoice if `blinded_payinfo` is not
* present.
* - MUST reject the invoice if `blinded_payinfo` does not
* contain exactly as many `payinfo` as total `onionmsg_path`
* in `blinded_path`.
*/
if (!invoice->invoice_blindedpay) { if (!invoice->invoice_blindedpay) {
json_add_string(js, "warning_missing_invoice_blindedpay", json_add_string(js, "warning_missing_invoice_blindedpay",
"invoices with paths without blindedpay are invalid"); "invoices with paths without blindedpay are invalid");

View File

@@ -624,7 +624,13 @@ static struct command_result *check_previous_invoice(struct command *cmd,
} }
/* BOLT-offers #12: /* BOLT-offers #12:
* - MUST fail the request if `signature` is not correct.
* - MUST fail the request if `signature` is not correct as detailed in
* [Signature Calculation](#signature-calculation) using the
* `invreq_payer_id`.
*...
* - MUST reject the invoice if `signature` is not a valid signature using
* `invoice_node_id` as described in [Signature Calculation](#signature-calculation).
*/ */
static bool check_payer_sig(struct command *cmd, static bool check_payer_sig(struct command *cmd,
const struct tlv_invoice_request *invreq, const struct tlv_invoice_request *invreq,
@@ -645,12 +651,12 @@ static struct command_result *invreq_amount_by_quantity(struct command *cmd,
assert(ir->invreq->offer_amount); assert(ir->invreq->offer_amount);
/* BOLT-offers #12: /* BOLT-offers #12:
* - MUST calculate the *base invoice amount* using the offer `amount`: * - MUST calculate the *expected amount* using the `offer_amount`:
*/ */
*raw_amt = *ir->invreq->offer_amount; *raw_amt = *ir->invreq->offer_amount;
/* BOLT-offers #12: /* BOLT-offers #12:
* - if request contains `quantity`, multiply by `quantity`. * - if `invreq_quantity` is present, multiply by `invreq_quantity`.`quantity`.
*/ */
if (ir->invreq->invreq_quantity) { if (ir->invreq->invreq_quantity) {
if (mul_overflows_u64(*ir->invreq->invreq_quantity, *raw_amt)) { if (mul_overflows_u64(*ir->invreq->invreq_quantity, *raw_amt)) {
@@ -703,11 +709,9 @@ static struct command_result *handle_amount_and_recurrence(struct command *cmd,
struct amount_msat base_inv_amount) struct amount_msat base_inv_amount)
{ {
/* BOLT-offers #12: /* BOLT-offers #12:
* - if the offer included `amount`: * - if `invreq_amount` is present:
*... * - MUST fail the request if `invreq_amount`.`msat` is less than the
* - if the request contains `amount`: * *expected amount*.
* - MUST fail the request if its `amount` is less than the
* *base invoice amount*.
*/ */
if (ir->invreq->offer_amount && ir->invreq->invreq_amount) { if (ir->invreq->offer_amount && ir->invreq->invreq_amount) {
if (amount_msat_less(amount_msat(*ir->invreq->invreq_amount), base_inv_amount)) { if (amount_msat_less(amount_msat(*ir->invreq->invreq_amount), base_inv_amount)) {
@@ -716,8 +720,8 @@ static struct command_result *handle_amount_and_recurrence(struct command *cmd,
&base_inv_amount)); &base_inv_amount));
} }
/* BOLT-offers #12: /* BOLT-offers #12:
* - MAY fail the request if its `amount` is much greater than * - MAY fail the request if `invreq_amount`.`msat` greatly exceeds
* the *base invoice amount*. * the *expected amount*.
*/ */
/* Much == 5? Easier to divide and compare, than multiply. */ /* Much == 5? Easier to divide and compare, than multiply. */
if (amount_msat_greater(amount_msat_div(amount_msat(*ir->invreq->invreq_amount), 5), if (amount_msat_greater(amount_msat_div(amount_msat(*ir->invreq->invreq_amount), 5),
@@ -726,13 +730,15 @@ static struct command_result *handle_amount_and_recurrence(struct command *cmd,
type_to_string(tmpctx, struct amount_msat, type_to_string(tmpctx, struct amount_msat,
&base_inv_amount)); &base_inv_amount));
} }
/* BOLT-offers #12:
* - MUST use the request's `amount` as the *base invoice
* amount*.
*/
base_inv_amount = amount_msat(*ir->invreq->invreq_amount); base_inv_amount = amount_msat(*ir->invreq->invreq_amount);
} }
/* BOLT-offers #12:
* - if `invreq_amount` is present:
* - MUST set `invoice_amount` to `invreq_amount`
* - otherwise:
* - MUST set `invoice_amount` to the *expected amount*.
*/
/* This may be adjusted by recurrence if proportional_amount set */ /* This may be adjusted by recurrence if proportional_amount set */
ir->inv->invoice_amount = tal_dup(ir->inv, u64, ir->inv->invoice_amount = tal_dup(ir->inv, u64,
&base_inv_amount.millisatoshis); /* Raw: wire protocol */ &base_inv_amount.millisatoshis); /* Raw: wire protocol */
@@ -794,10 +800,9 @@ static struct command_result *convert_currency(struct command *cmd,
return err; return err;
/* BOLT-offers #12: /* BOLT-offers #12:
* - MUST calculate the *base invoice amount* using the offer * - MUST calculate the *expected amount* using the `offer_amount`:
* `amount`: * - if `offer_currency` is not the `invreq_chain` currency, convert to the
* - if offer `currency` is not the invoice currency, convert * `invreq_chain` currency.
* to the invoice currency.
*/ */
iso4217 = find_iso4217(ir->invreq->offer_currency, iso4217 = find_iso4217(ir->invreq->offer_currency,
tal_bytelen(ir->invreq->offer_currency)); tal_bytelen(ir->invreq->offer_currency));
@@ -900,7 +905,7 @@ static struct command_result *listoffers_done(struct command *cmd,
} }
/* BOLT-offers #12: /* BOLT-offers #12:
* - MUST fail the request if `invreq_signature` is not correct as * - MUST fail the request if `signature` is not correct as
* detailed in [Signature Calculation](#signature-calculation) using * detailed in [Signature Calculation](#signature-calculation) using
* the `invreq_payer_id`. * the `invreq_payer_id`.
*/ */
@@ -940,10 +945,10 @@ static struct command_result *listoffers_done(struct command *cmd,
} }
/* BOLT-offers #12: /* BOLT-offers #12:
* The writer of an invoice: * A writer of an invoice:
*... *...
* - if the invoice is in response to an `invoice_request`: * - if the invoice is in response to an `invoice_request`:
* - MUST copy all non-signature fields from the invreq (including * - MUST copy all non-signature fields from the `invoice_request` (including
* unknown fields). * unknown fields).
*/ */
ir->inv = invoice_for_invreq(cmd, ir->invreq); ir->inv = invoice_for_invreq(cmd, ir->invreq);
@@ -1035,7 +1040,7 @@ struct command_result *handle_invoice_request(struct command *cmd,
/* BOLT-offers #12: /* BOLT-offers #12:
* *
* The reader of an invreq: * The reader:
*... *...
* - if `invreq_features` contains unknown _even_ bits that are non-zero: * - if `invreq_features` contains unknown _even_ bits that are non-zero:
* - MUST fail the request. * - MUST fail the request.

View File

@@ -44,8 +44,8 @@ static struct command_result *param_amount(struct command *cmd,
/* BOLT-offers #12: /* BOLT-offers #12:
* *
* - MUST specify `iso4217` as an ISO 4712 three-letter code. * - MUST specify `offer_currency` `iso4217` as an ISO 4712 three-letter code.
* - MUST specify `amount` in the currency unit adjusted by the ISO 4712 * - MUST specify `offer_amount` in the currency unit adjusted by the ISO 4712
* exponent (e.g. USD cents). * exponent (e.g. USD cents).
*/ */
if (tok->end - tok->start < ISO4217_NAMELEN) if (tok->end - tok->start < ISO4217_NAMELEN)