pay: Exclude nodes if the failcode of sendpay has the NODE bit set

This commit is contained in:
trueptolemy 2019-09-05 22:41:26 +08:00 committed by ZmnSCPxj, ZmnSCPxj jxPCSmnZ
parent 36852c36e6
commit 22e8d242fc

View file

@ -10,6 +10,7 @@
#include <gossipd/gossip_constants.h> #include <gossipd/gossip_constants.h>
#include <plugins/libplugin.h> #include <plugins/libplugin.h>
#include <stdio.h> #include <stdio.h>
#include <wire/onion_defs.h>
/* Public key of this node. */ /* Public key of this node. */
static struct node_id my_id; static struct node_id my_id;
@ -190,19 +191,30 @@ static struct command_result *start_pay_attempt(struct command *cmd,
const char *fmt, ...); const char *fmt, ...);
/* Is this (erring) channel within the routehint itself? */ /* Is this (erring) channel within the routehint itself? */
static bool channel_in_routehint(const struct route_info *routehint, static bool node_or_channel_in_routehint(const struct route_info *routehint,
const char *scidstr, size_t scidlen) const char *idstr, size_t idlen)
{ {
struct node_id nodeid;
struct short_channel_id scid; struct short_channel_id scid;
bool node_err = true;
if (!short_channel_id_from_str(scidstr, scidlen, &scid)) if (!node_id_from_hexstr(idstr, idlen, &nodeid)) {
plugin_err("bad erring_channel '%.*s'", if (!short_channel_id_from_str(idstr, idlen, &scid))
(int)scidlen, scidstr); plugin_err("bad erring_node or erring_channel '%.*s'",
(int)idlen, idstr);
else
node_err = false;
}
for (size_t i = 0; i < tal_count(routehint); i++) for (size_t i = 0; i < tal_count(routehint); i++) {
if (node_err) {
if (node_id_eq(&nodeid, &routehint[i].pubkey))
return true;
} else {
if (short_channel_id_eq(&scid, &routehint[i].short_channel_id)) if (short_channel_id_eq(&scid, &routehint[i].short_channel_id))
return true; return true;
}
}
return false; return false;
} }
@ -251,8 +263,9 @@ static bool routehint_excluded(const struct route_info *routehint,
* found that one direction of a channel is unavailable, but they * found that one direction of a channel is unavailable, but they
* are suggesting we use it the other way. Very unlikely though! */ * are suggesting we use it the other way. Very unlikely though! */
for (size_t i = 0; i < tal_count(excludes); i++) for (size_t i = 0; i < tal_count(excludes); i++)
if (channel_in_routehint(routehint, if (node_or_channel_in_routehint(routehint,
excludes[i], strlen(excludes[i]))) excludes[i],
strlen(excludes[i])))
return true; return true;
return false; return false;
} }
@ -293,8 +306,9 @@ static struct command_result *waitsendpay_error(struct command *cmd,
const jsmntok_t *error, const jsmntok_t *error,
struct pay_command *pc) struct pay_command *pc)
{ {
const jsmntok_t *codetok, *scidtok, *dirtok; const jsmntok_t *codetok, *failcodetok, *nodeidtok, *scidtok, *dirtok;
int code; int code, failcode;
bool node_err = false;
attempt_failed_tok(pc, "waitsendpay", buf, error); attempt_failed_tok(pc, "waitsendpay", buf, error);
@ -310,6 +324,18 @@ static struct command_result *waitsendpay_error(struct command *cmd,
return forward_error(cmd, buf, error, pc); return forward_error(cmd, buf, error, pc);
} }
failcodetok = json_delve(buf, error, ".data.failcode");
if (!json_to_int(buf, failcodetok, &failcode))
plugin_err("waitsendpay error gave no 'failcode'? '%.*s'",
error->end - error->start, buf + error->start);
if (failcode & NODE) {
nodeidtok = json_delve(buf, error, ".data.erring_node");
if (!nodeidtok)
plugin_err("waitsendpay error no erring_node '%.*s'",
error->end - error->start, buf + error->start);
node_err = true;
} else {
scidtok = json_delve(buf, error, ".data.erring_channel"); scidtok = json_delve(buf, error, ".data.erring_channel");
if (!scidtok) if (!scidtok)
plugin_err("waitsendpay error no erring_channel '%.*s'", plugin_err("waitsendpay error no erring_channel '%.*s'",
@ -318,13 +344,27 @@ static struct command_result *waitsendpay_error(struct command *cmd,
if (!dirtok) if (!dirtok)
plugin_err("waitsendpay error no erring_direction '%.*s'", plugin_err("waitsendpay error no erring_direction '%.*s'",
error->end - error->start, buf + error->start); error->end - error->start, buf + error->start);
}
if (time_after(time_now(), pc->stoptime)) { if (time_after(time_now(), pc->stoptime)) {
return waitsendpay_expired(cmd, pc); return waitsendpay_expired(cmd, pc);
} }
if (node_err) {
/* If failure is in routehint part, try next one */ /* If failure is in routehint part, try next one */
if (channel_in_routehint(pc->current_routehint, if (node_or_channel_in_routehint(pc->current_routehint,
buf + nodeidtok->start,
nodeidtok->end - nodeidtok->start))
return next_routehint(cmd, pc);
/* Otherwise, add erring channel to exclusion list. */
tal_arr_expand(&pc->excludes,
tal_fmt(pc->excludes, "%.*s",
nodeidtok->end - nodeidtok->start,
buf + nodeidtok->start));
} else {
/* If failure is in routehint part, try next one */
if (node_or_channel_in_routehint(pc->current_routehint,
buf + scidtok->start, buf + scidtok->start,
scidtok->end - scidtok->start)) scidtok->end - scidtok->start))
return next_routehint(cmd, pc); return next_routehint(cmd, pc);
@ -335,8 +375,11 @@ static struct command_result *waitsendpay_error(struct command *cmd,
scidtok->end - scidtok->start, scidtok->end - scidtok->start,
buf + scidtok->start, buf + scidtok->start,
buf[dirtok->start])); buf[dirtok->start]));
}
/* Try again. */ /* Try again. */
return start_pay_attempt(cmd, pc, "Excluded channel %s", return start_pay_attempt(cmd, pc, "Excluded %s %s",
node_err ? "node" : "channel",
pc->excludes[tal_count(pc->excludes)-1]); pc->excludes[tal_count(pc->excludes)-1]);
} }
@ -480,8 +523,9 @@ static bool maybe_exclude(struct pay_command *pc,
scid = json_get_member(buf, route, "channel"); scid = json_get_member(buf, route, "channel");
if (channel_in_routehint(pc->current_routehint, if (node_or_channel_in_routehint(pc->current_routehint,
buf + scid->start, scid->end - scid->start)) buf + scid->start,
scid->end - scid->start))
return false; return false;
dir = json_get_member(buf, route, "direction"); dir = json_get_member(buf, route, "direction");
@ -1076,7 +1120,7 @@ static void add_attempt(struct json_out *ret,
json_out_end(ret, ']'); json_out_end(ret, ']');
} }
if (tal_count(attempt->excludes)) { if (tal_count(attempt->excludes)) {
json_out_start(ret, "excluded_channels", '['); json_out_start(ret, "excluded_nodes_or_channels", '[');
for (size_t i = 0; i < tal_count(attempt->excludes); i++) for (size_t i = 0; i < tal_count(attempt->excludes); i++)
json_out_addstr(ret, NULL, attempt->excludes[i]); json_out_addstr(ret, NULL, attempt->excludes[i]);
json_out_end(ret, ']'); json_out_end(ret, ']');