From 40e9d60ce018450e6e6deccb6de100a3dc1fd54f Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Tue, 2 Mar 2021 13:12:05 +0100 Subject: [PATCH] pay: Do not clobber error messages if we fail decoding a bolt11 We were automatically falling back to bolt12 decoding, clobbering the fail message. Ultimately resulting in confusing error messages (expected prefix lni but got lnbtrc). Now we first determine which decoding we're trying to do, and then only decode accordingly. Changelog-Fixed: pay: Report the correct decoding error if bolt11 parsing fails. --- plugins/pay.c | 59 ++++++++++++++++++++++++++++++++------------------- 1 file changed, 37 insertions(+), 22 deletions(-) diff --git a/plugins/pay.c b/plugins/pay.c index 13733d077..9062cf4d1 100644 --- a/plugins/pay.c +++ b/plugins/pay.c @@ -1973,6 +1973,7 @@ static struct command_result *json_paymod(struct command *cmd, u64 invexpiry; struct sha256 *local_offer_id; const struct tlv_invoice *b12; + bool is_b12; #if DEVELOPER bool *use_shadow; #endif @@ -2003,31 +2004,45 @@ static struct command_result *json_paymod(struct command *cmd, p = payment_new(cmd, cmd, NULL /* No parent */, paymod_mods); p->invstring = tal_steal(p, b11str); - b11 = bolt11_decode(cmd, b11str, plugin_feature_set(cmd->plugin), - NULL, chainparams, &fail); - if (b11) { + is_b12 = strlen(b11str) > 3 && + (strstarts(b11str, "lni") || strstarts(b11str, "lno") || + strstarts(b11str, "lnr")); + + b11 = bolt11_decode(cmd, b11str, plugin_feature_set(cmd->plugin), NULL, + chainparams, &fail); + if (!is_b12) { + if (b11 == NULL) + return command_fail(cmd, JSONRPC2_INVALID_PARAMS, + "Invalid bolt11: %s", fail); invmsat = b11->msat; invexpiry = b11->timestamp + b11->expiry; p->destination = tal_dup(p, struct node_id, &b11->receiver_id); - p->destination_has_tlv = feature_offered(b11->features, - OPT_VAR_ONION); + p->destination_has_tlv = + feature_offered(b11->features, OPT_VAR_ONION); p->payment_hash = tal_dup(p, struct sha256, &b11->payment_hash); - p->payment_secret = b11->payment_secret + p->payment_secret = + b11->payment_secret ? tal_dup(p, struct secret, b11->payment_secret) : NULL; p->routes = tal_steal(p, b11->routes); p->min_final_cltv_expiry = b11->min_final_cltv_expiry; p->features = tal_steal(p, b11->features); /* Sanity check */ - if (feature_offered(b11->features, OPT_VAR_ONION) - && !b11->payment_secret) + if (feature_offered(b11->features, OPT_VAR_ONION) && + !b11->payment_secret) + return command_fail( + cmd, JSONRPC2_INVALID_PARAMS, + "Invalid bolt11:" + " sets feature var_onion with no secret"); + } else { + b12 = invoice_decode(cmd, b11str, strlen(b11str), + plugin_feature_set(cmd->plugin), + chainparams, &fail); + if (b12 == NULL) return command_fail(cmd, JSONRPC2_INVALID_PARAMS, - "Invalid bolt11:" - " sets feature var_onion with no secret"); - } else if ((b12 = invoice_decode(cmd, b11str, strlen(b11str), - plugin_feature_set(cmd->plugin), - chainparams, &fail)) != NULL) { + "Invalid bolt12: %s", fail); + if (!exp_offers) return command_fail(cmd, JSONRPC2_INVALID_PARAMS, "experimental-offers disabled"); @@ -2051,14 +2066,14 @@ static struct command_result *json_paymod(struct command *cmd, /* FIXME: gossmap should store as pubkey32 */ p->destination = tal(p, struct node_id); - gossmap_guess_node_id(get_gossmap(cmd->plugin), - b12->node_id, + gossmap_guess_node_id(get_gossmap(cmd->plugin), b12->node_id, p->destination); p->destination_has_tlv = true; p->payment_hash = tal_dup(p, struct sha256, b12->payment_hash); if (b12->recurrence_counter && !label) - return command_fail(cmd, JSONRPC2_INVALID_PARAMS, - "recurring invoice requires a label"); + return command_fail( + cmd, JSONRPC2_INVALID_PARAMS, + "recurring invoice requires a label"); /* FIXME payment_secret should be signature! */ { struct sha256 merkle; @@ -2066,7 +2081,8 @@ static struct command_result *json_paymod(struct command *cmd, p->payment_secret = tal(p, struct secret); merkle_tlv(b12->fields, &merkle); memcpy(p->payment_secret, &merkle, sizeof(merkle)); - BUILD_ASSERT(sizeof(*p->payment_secret) == sizeof(merkle)); + BUILD_ASSERT(sizeof(*p->payment_secret) == + sizeof(merkle)); } p->routes = NULL; /* FIXME: paths! */ @@ -2081,16 +2097,15 @@ static struct command_result *json_paymod(struct command *cmd, * `seconds_from_timestamp`. * - otherwise: * - MUST reject the invoice if the current time since - * 1970-01-01 UTC is greater than `timestamp` plus 7200. + * 1970-01-01 UTC is greater than `timestamp` plus + * 7200. */ if (b12->relative_expiry) invexpiry = *b12->timestamp + *b12->relative_expiry; else invexpiry = *b12->timestamp + BOLT12_DEFAULT_REL_EXPIRY; p->local_offer_id = tal_steal(p, local_offer_id); - } else - return command_fail(cmd, JSONRPC2_INVALID_PARAMS, - "Invalid bolt11: %s", fail); + } if (time_now().ts.tv_sec > invexpiry) return command_fail(cmd, PAY_INVOICE_EXPIRED, "Invoice expired");