pay: inject channel updates from errors ourselves.

Cut & paste from gossipd.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
Rusty Russell 2021-02-02 15:46:22 +10:30 committed by Christian Decker
parent 3c5502426b
commit 780fc25413
1 changed files with 106 additions and 16 deletions

View File

@ -12,6 +12,7 @@
#include <common/type_to_string.h>
#include <errno.h>
#include <plugins/libplugin-pay.h>
#include <wire/peer_wire.h>
static struct gossmap *global_gossmap;
@ -1340,12 +1341,105 @@ 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_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)
{
const struct node_id *errnode;
const struct route_hop *errchan;
u8 *update;
assert(p->route != NULL);
@ -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,