diff --git a/plugins/pay.c b/plugins/pay.c index 26ef13bca4e8..2d23051a8ccb 100644 --- a/plugins/pay.c +++ b/plugins/pay.c @@ -10,6 +10,7 @@ #include #include #include +#include /* Public key of this node. */ static struct node_id my_id; @@ -190,19 +191,30 @@ static struct command_result *start_pay_attempt(struct command *cmd, const char *fmt, ...); /* Is this (erring) channel within the routehint itself? */ -static bool channel_in_routehint(const struct route_info *routehint, - const char *scidstr, size_t scidlen) +static bool node_or_channel_in_routehint(const struct route_info *routehint, + const char *idstr, size_t idlen) { + struct node_id nodeid; struct short_channel_id scid; + bool node_err = true; - if (!short_channel_id_from_str(scidstr, scidlen, &scid)) - plugin_err("bad erring_channel '%.*s'", - (int)scidlen, scidstr); - - for (size_t i = 0; i < tal_count(routehint); i++) - if (short_channel_id_eq(&scid, &routehint[i].short_channel_id)) - return true; + if (!node_id_from_hexstr(idstr, idlen, &nodeid)) { + if (!short_channel_id_from_str(idstr, idlen, &scid)) + 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++) { + 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)) + return true; + } + } 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 * are suggesting we use it the other way. Very unlikely though! */ for (size_t i = 0; i < tal_count(excludes); i++) - if (channel_in_routehint(routehint, - excludes[i], strlen(excludes[i]))) + if (node_or_channel_in_routehint(routehint, + excludes[i], + strlen(excludes[i]))) return true; return false; } @@ -293,8 +306,9 @@ static struct command_result *waitsendpay_error(struct command *cmd, const jsmntok_t *error, struct pay_command *pc) { - const jsmntok_t *codetok, *scidtok, *dirtok; - int code; + const jsmntok_t *codetok, *failcodetok, *nodeidtok, *scidtok, *dirtok; + int code, failcode; + bool node_err = false; attempt_failed_tok(pc, "waitsendpay", buf, error); @@ -310,33 +324,62 @@ static struct command_result *waitsendpay_error(struct command *cmd, return forward_error(cmd, buf, error, pc); } - scidtok = json_delve(buf, error, ".data.erring_channel"); - if (!scidtok) - plugin_err("waitsendpay error no erring_channel '%.*s'", - error->end - error->start, buf + error->start); - dirtok = json_delve(buf, error, ".data.erring_direction"); - if (!dirtok) - plugin_err("waitsendpay error no erring_direction '%.*s'", + 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"); + if (!scidtok) + plugin_err("waitsendpay error no erring_channel '%.*s'", + error->end - error->start, buf + error->start); + dirtok = json_delve(buf, error, ".data.erring_direction"); + if (!dirtok) + plugin_err("waitsendpay error no erring_direction '%.*s'", + error->end - error->start, buf + error->start); + } + if (time_after(time_now(), pc->stoptime)) { return waitsendpay_expired(cmd, pc); } - /* If failure is in routehint part, try next one */ - if (channel_in_routehint(pc->current_routehint, - buf + scidtok->start, - scidtok->end - scidtok->start)) - return next_routehint(cmd, pc); + if (node_err) { + /* If failure is in routehint part, try next one */ + 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/%c", + /* 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, + scidtok->end - scidtok->start)) + return next_routehint(cmd, pc); + + /* Otherwise, add erring channel to exclusion list. */ + tal_arr_expand(&pc->excludes, + tal_fmt(pc->excludes, "%.*s/%c", scidtok->end - scidtok->start, buf + scidtok->start, buf[dirtok->start])); + } + /* 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]); } @@ -480,8 +523,9 @@ static bool maybe_exclude(struct pay_command *pc, scid = json_get_member(buf, route, "channel"); - if (channel_in_routehint(pc->current_routehint, - buf + scid->start, scid->end - scid->start)) + if (node_or_channel_in_routehint(pc->current_routehint, + buf + scid->start, + scid->end - scid->start)) return false; dir = json_get_member(buf, route, "direction"); @@ -1076,7 +1120,7 @@ static void add_attempt(struct json_out *ret, json_out_end(ret, ']'); } 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++) json_out_addstr(ret, NULL, attempt->excludes[i]); json_out_end(ret, ']');