From 780fc2541326ef1eb8a4513e8cd8197139dbcc8f Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 2 Feb 2021 15:46:22 +1030 Subject: [PATCH] pay: inject channel updates from errors ourselves. Cut & paste from gossipd. Signed-off-by: Rusty Russell --- plugins/libplugin-pay.c | 122 ++++++++++++++++++++++++++++++++++------ 1 file changed, 106 insertions(+), 16 deletions(-) diff --git a/plugins/libplugin-pay.c b/plugins/libplugin-pay.c index fabb9c61f..936d9cb5e 100644 --- a/plugins/libplugin-pay.c +++ b/plugins/libplugin-pay.c @@ -12,6 +12,7 @@ #include #include #include +#include static struct gossmap *global_gossmap; @@ -1340,13 +1341,106 @@ static bool assign_blame(const struct payment *p, return true; } +/* Fix up the channel_update to include the type if it doesn't currently have + * one. See ElementsProject/lightning#1730 and lightningnetwork/lnd#1599 for the + * in-depth discussion on why we break message parsing here... */ +static u8 *patch_channel_update(const tal_t *ctx, u8 *channel_update TAKES) +{ + u8 *fixed; + if (channel_update != NULL && + fromwire_peektype(channel_update) != WIRE_CHANNEL_UPDATE) { + /* This should be a channel_update, prefix with the + * WIRE_CHANNEL_UPDATE type, but isn't. Let's prefix it. */ + fixed = tal_arr(ctx, u8, 0); + towire_u16(&fixed, WIRE_CHANNEL_UPDATE); + towire(&fixed, channel_update, tal_bytelen(channel_update)); + if (taken(channel_update)) + tal_free(channel_update); + return fixed; + } else { + return tal_dup_talarr(ctx, u8, channel_update); + } +} + +/* Return NULL if the wrapped onion error message has no channel_update field, + * or return the embedded channel_update message otherwise. */ +static u8 *channel_update_from_onion_error(const tal_t *ctx, + const u8 *onion_message) +{ + u8 *channel_update = NULL; + struct amount_msat unused_msat; + u32 unused32; + + /* Identify failcodes that have some channel_update. + * + * TODO > BOLT 1.0: Add new failcodes when updating to a + * new BOLT version. */ + if (!fromwire_temporary_channel_failure(ctx, + onion_message, + &channel_update) && + !fromwire_amount_below_minimum(ctx, + onion_message, &unused_msat, + &channel_update) && + !fromwire_fee_insufficient(ctx, + onion_message, &unused_msat, + &channel_update) && + !fromwire_incorrect_cltv_expiry(ctx, + onion_message, &unused32, + &channel_update) && + !fromwire_expiry_too_soon(ctx, + onion_message, + &channel_update)) + /* No channel update. */ + return NULL; + + return patch_channel_update(ctx, take(channel_update)); +} + + static struct command_result * -payment_waitsendpay_finished(struct command *cmd, const char *buffer, - const jsmntok_t *toks, struct payment *p) +payment_addgossip_success(struct command *cmd, const char *buffer, + const jsmntok_t *toks, struct payment *p) { const struct node_id *errnode; const struct route_hop *errchan; + if (!assign_blame(p, &errnode, &errchan)) { + paymod_log(p, LOG_UNUSUAL, + "No erring_index set in `waitsendpay` result: %.*s", + json_tok_full_len(toks), + json_tok_full(buffer, toks)); + /* FIXME: Pick a random channel to fail? */ + payment_set_step(p, PAYMENT_STEP_FAILED); + payment_continue(p); + return command_still_pending(cmd); + } + + if (!errchan) + return handle_final_failure(cmd, p, errnode, + p->result->failcode); + + return handle_intermediate_failure(cmd, p, errnode, errchan, + p->result->failcode); +} + +/* If someone gives us an invalid update, all we can do is log it */ +static struct command_result * +payment_addgossip_failure(struct command *cmd, const char *buffer, + const jsmntok_t *toks, struct payment *p) +{ + paymod_log(p, LOG_DBG, "Invalid channel_update: %.*s", + json_tok_full_len(toks), + json_tok_full(buffer, toks)); + + return payment_addgossip_success(cmd, NULL, NULL, p); +} + +static struct command_result * +payment_waitsendpay_finished(struct command *cmd, const char *buffer, + const jsmntok_t *toks, struct payment *p) +{ + u8 *update; + assert(p->route != NULL); p->end_time = time_now(); @@ -1372,23 +1466,19 @@ payment_waitsendpay_finished(struct command *cmd, const char *buffer, payment_chanhints_apply_route(p, true); - if (!assign_blame(p, &errnode, &errchan)) { - paymod_log(p, LOG_UNUSUAL, - "No erring_index set in `waitsendpay` result: %.*s", - json_tok_full_len(toks), - json_tok_full(buffer, toks)); - /* FIXME: Pick a random channel to fail? */ - payment_set_step(p, PAYMENT_STEP_FAILED); - payment_continue(p); + /* Tell gossipd, if we received an update */ + update = channel_update_from_onion_error(tmpctx, p->result->raw_message); + if (update) { + struct out_req *req; + req = jsonrpc_request_start(p->plugin, NULL, "addgossip", + payment_addgossip_success, + payment_addgossip_failure, p); + json_add_hex_talarr(req->js, "message", update); + send_outreq(p->plugin, req); return command_still_pending(cmd); } - if (!errchan) - return handle_final_failure(cmd, p, errnode, - p->result->failcode); - - return handle_intermediate_failure(cmd, p, errnode, errchan, - p->result->failcode); + return payment_addgossip_success(cmd, NULL, NULL, p); } static struct command_result *payment_sendonion_success(struct command *cmd,