global: update BOLT12 quotes.

This is a final sweep to match the current BOLT12 text:

	1563d13999d342680140c693de0b9d65aa522372 ("More bolt12 test vectors.")

Only two code changes, to change the order of checks to match the bolt,
and to give a warning on decode if a path is empty.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
Rusty Russell 2024-08-01 09:34:20 +09:30
parent f2a7b19281
commit bf549130d6
16 changed files with 657 additions and 615 deletions

View file

@ -997,6 +997,7 @@
"Decode.unknown_offer_tlvs[]": 19,
"Decode.valid": 2,
"Decode.version": 71,
"Decode.warning_empty_blinded_path": 86,
"Decode.warning_invalid_invoice_request_signature": 39,
"Decode.warning_invalid_invoice_signature": 58,
"Decode.warning_invalid_invreq_payer_note": 37,
@ -4815,6 +4816,10 @@
"added": "pre-v0.10.1",
"deprecated": null
},
"Decode.warning_empty_blinded_path": {
"added": "v24.08",
"deprecated": null
},
"Decode.warning_invalid_invoice_request_signature": {
"added": "pre-v0.10.1",
"deprecated": null

View file

@ -1663,6 +1663,7 @@ message DecodeResponse {
optional bytes offer_issuer_id = 83;
optional string warning_missing_offer_issuer_id = 84;
repeated DecodeInvreq_paths invreq_paths = 85;
optional string warning_empty_blinded_path = 86;
}
message DecodeOffer_paths {

View file

@ -1599,6 +1599,7 @@ impl From<responses::DecodeResponse> for pb::DecodeResponse {
unique_id: c.unique_id, // Rule #2 for type string?
valid: c.valid, // Rule #2 for type boolean
version: c.version, // Rule #2 for type string?
warning_empty_blinded_path: c.warning_empty_blinded_path, // Rule #2 for type string?
warning_invalid_invoice_request_signature: c.warning_invalid_invoice_request_signature, // Rule #2 for type string?
warning_invalid_invoice_signature: c.warning_invalid_invoice_signature, // Rule #2 for type string?
warning_invalid_invreq_payer_note: c.warning_invalid_invreq_payer_note, // Rule #2 for type string?

2
cln-rpc/src/model.rs generated
View file

@ -6872,6 +6872,8 @@ pub mod responses {
#[serde(skip_serializing_if = "Option::is_none")]
pub version: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub warning_empty_blinded_path: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub warning_invalid_invoice_request_signature: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub warning_invalid_invoice_signature: Option<String>,

View file

@ -248,7 +248,7 @@ int main(int argc, char *argv[])
/* BOLT-offers #12:
* - if it includes `offer_paths`:
* - SHOULD ignore any invoice_request which does not use the path.
* - MAY set `offer_issuer_id` to the node's public key to request the invoice from.
* - MAY set `offer_issuer_id`.
* - otherwise:
* - MUST set `offer_issuer_id` to the node's public key to request the invoice from.
*/

View file

@ -117,10 +117,7 @@ int main(int argc, char *argv[])
offer = tlv_offer_new(tmpctx);
/* BOLT-offers #12:
* A writer of an offer:
* - MUST NOT set any tlv fields greater or equal to 80, or tlv field 0.
* - MUST set `offer_issuer_id` to the node's public key to request the invoice from.
* - MUST set `offer_description` to a complete description of the purpose
* of the payment.
* - 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:
@ -134,9 +131,12 @@ int main(int argc, char *argv[])
* - MUST specify `offer_currency` `iso4217` as an ISO 4712 three-letter code.
* - MUST specify `offer_amount` in the currency unit adjusted by the ISO 4712
* 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.
@ -151,6 +151,9 @@ int main(int argc, char *argv[])
* - 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.
* - if it sets `offer_issuer`:
* - SHOULD set it to identify the issuer of the invoice clearly.
* - if it includes a domain name:

View file

@ -5726,6 +5726,13 @@
}
}
},
"warning_empty_blinded_path": {
"added": "v24.08",
"type": "string",
"description": [
"The blinded path has 0 hops."
]
},
"offer_node_id": {
"type": "pubkey",
"deprecated": [

File diff suppressed because one or more lines are too long

View file

@ -1312,6 +1312,7 @@ def decode2py(m):
"unique_id": m.unique_id, # PrimitiveField in generate_composite
"valid": m.valid, # PrimitiveField in generate_composite
"version": m.version, # PrimitiveField in generate_composite
"warning_empty_blinded_path": m.warning_empty_blinded_path, # PrimitiveField in generate_composite
"warning_invalid_invoice_request_signature": m.warning_invalid_invoice_request_signature, # PrimitiveField in generate_composite
"warning_invalid_invoice_signature": m.warning_invalid_invoice_signature, # PrimitiveField in generate_composite
"warning_invalid_invreq_payer_note": m.warning_invalid_invreq_payer_note, # PrimitiveField in generate_composite

View file

@ -459,7 +459,7 @@ static u64 get_offer_type(const char *name)
* * [`tu64`:`max`]
* 1. type: 22 (`offer_issuer_id`)
* 2. data:
* * [`point`:`node_id`]
* * [`point`:`id`]
*/
{ "offer_chains", 2 },
{ "offer_metadata", 4 },
@ -510,7 +510,7 @@ static u64 get_offer_type(const char *name)
* * [`tu64`:`max`]
* 1. type: 22 (`offer_issuer_id`)
* 2. data:
* * [`point`:`node_id`]
* * [`point`:`id`]
* 1. type: 80 (`invreq_chain`)
* 2. data:
* * [`chain_hash`:`chain`]
@ -583,7 +583,7 @@ static u64 get_offer_type(const char *name)
* * [`tu64`:`max`]
* 1. type: 22 (`offer_issuer_id`)
* 2. data:
* * [`point`:`node_id`]
* * [`point`:`id`]
* 1. type: 80 (`invreq_chain`)
* 2. data:
* * [`chain_hash`:`chain`]
@ -602,6 +602,9 @@ static u64 get_offer_type(const char *name)
* 1. type: 89 (`invreq_payer_note`)
* 2. data:
* * [`...*utf8`:`note`]
* 1. type: 90 (`invreq_paths`)
* 2. data:
* * [`...*blinded_path`:`paths`]
* 1. type: 160 (`invoice_paths`)
* 2. data:
* * [`...*blinded_path`:`paths`]

View file

@ -235,6 +235,13 @@
}
}
},
"warning_empty_blinded_path": {
"added": "v24.08",
"type": "string",
"description": [
"The blinded path has 0 hops."
]
},
"offer_node_id": {
"type": "pubkey",
"deprecated": [

View file

@ -221,8 +221,11 @@ static struct command_result *handle_invreq_response(struct command *cmd,
}
/* BOLT-offers #12:
* - if `offer_node_id` or `offer_paths` are present (invoice_request for an offer):
* - MUST reject the invoice if `invoice_node_id` is not equal to the public key it sent the `invoice_request` to.
* - 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.
*/
if (!inv->invoice_node_id || !pubkey_eq(inv->offer_issuer_id, sent->issuer_key)) {
badfield = "invoice_node_id";
@ -560,7 +563,7 @@ static struct command_result *try_establish(struct command *cmd,
target = first.pubkey;
/* BOLT-offers #12:
* - if `offer_issuer_id` is present (invoice_request for an offer):
* - MUST reject the invoice if `invoice_node_id` is not equal `offer_issuer_id`
* - 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.
*/
@ -1171,7 +1174,7 @@ static struct command_result *param_invreq(struct command *cmd,
* The reader:
* - MUST fail the request if `invreq_payer_id` or `invreq_metadata`
* are not present.
* - MUST fail the request if any non-signature TLV fields are outside the inclusive ranges: 0 to 159 and 1000000000 to 2999999999.
* - MUST fail the 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.
@ -1228,7 +1231,7 @@ static struct command_result *param_invreq(struct command *cmd,
}
/* BOLT-offers #12:
* - otherwise (no `offer_node_id` or `offer_paths`, not a response to our offer):
* - otherwise (no `offer_issuer_id` or `offer_paths`, not a response to our offer):
* - MUST fail the request if any of the following are present:
* - `offer_chains`, `offer_features` or `offer_quantity_max`.
* - MUST fail the request if `invreq_amount` is not present.
@ -1338,7 +1341,7 @@ struct command_result *json_sendinvoice(struct command *cmd,
/* BOLT-offers #12:
* - if `offer_issuer_id` is present:
* - MUST set `invoice_node_id` to `offer_issuer_id`.
* - 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`
* - otherwise:

View file

@ -565,6 +565,19 @@ static bool json_add_blinded_paths(struct json_stream *js,
return false;
}
/* BOLT-offers #12:
* - if `num_hops` is 0 in any `blinded_path` in `offer_paths`:
* - MUST NOT respond to the offer.
*/
for (size_t i = 0; i < tal_count(paths); i++) {
if (tal_count(paths[i]->path) == 0) {
json_add_str_fmt(js, "warning_empty_blinded_path",
"blinded path %zu has 0 hops",
i);
return false;
}
}
return true;
}
@ -1011,6 +1024,7 @@ static void json_add_b12_invoice(struct json_stream *js,
/* BOLT-offers #12:
* - 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`.

View file

@ -136,13 +136,11 @@ static struct command_result *listinvreqs_done(struct command *cmd,
* - 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`.
* - 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.
*
* - otherwise: (a invoice presented without being requested, eg. scanned by user):
*/
/* Since the invreq_id hashes all fields in those ranges, we know it matches */
@ -317,11 +315,16 @@ struct command_result *handle_invoice(struct command *cmd,
* A reader of an invoice:
*...
* - 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`.
*/
if (!inv->inv->invoice_paths)
return fail_inv(cmd, inv, "Missing invoice_paths");
for (size_t i = 0; i < tal_count(inv->inv->invoice_paths); i++) {
if (tal_count(inv->inv->invoice_paths[i]->path) == 0)
return fail_inv(cmd, inv, "Empty path in invoice_paths");
}
if (!inv->inv->invoice_blindedpay)
return fail_inv(cmd, inv, "Missing invoice_blindedpay");
if (tal_count(inv->inv->invoice_blindedpay)
@ -329,14 +332,6 @@ struct command_result *handle_invoice(struct command *cmd,
return fail_inv(cmd, inv,
"Mismatch between invoice_blindedpay and invoice_paths");
/* BOLT-offers #12:
* - MUST reject the invoice if `num_hops` is 0 in any `blinded_path` in `invoice_paths`.
*/
for (size_t i = 0; i < tal_count(inv->inv->invoice_paths); i++) {
if (tal_count(inv->inv->invoice_paths[i]->path) == 0)
return fail_inv(cmd, inv, "Empty path in invoice_paths");
}
/* BOLT-offers #12:
* A reader of an invoice:
*...

View file

@ -930,7 +930,7 @@ static struct command_result *listoffers_done(struct command *cmd,
/* BOLT-offers #12:
* - if `offer_issuer_id` is present:
* - MUST set `invoice_node_id` to `offer_issuer_id`.
* - MUST set `invoice_node_id` to the `offer_issuer_id`
*/
/* FIXME: We always provide an offer_issuer_id! */
ir->inv->invoice_node_id = ir->inv->offer_issuer_id;

View file

@ -745,7 +745,7 @@ struct command_result *json_invoicerequest(struct command *cmd,
/* BOLT-offers #12:
* - otherwise (not responding to an offer):
*...
* - MUST set `invreq_payer_id` as it would set `offer_issuer_id` for an offer.
* - MUST set `invreq_payer_id` (as it would set `offer_issuer_id` for an offer).
*/
/* FIXME: Allow invoicerequests using aliases! */
invreq->invreq_payer_id = tal_dup(invreq, struct pubkey, &id);