diff --git a/plugins/libplugin-pay.c b/plugins/libplugin-pay.c index 852e35a4d..8f25bdce6 100644 --- a/plugins/libplugin-pay.c +++ b/plugins/libplugin-pay.c @@ -54,6 +54,7 @@ struct payment *payment_new(tal_t *ctx, struct command *cmd, p->partid = 0; p->next_partid = 1; p->plugin = cmd->plugin; + p->channel_hints = tal_arr(p, struct channel_hint, 0); } /* Initialize all modifier data so we can point to the fields when @@ -793,3 +794,61 @@ static inline void retry_step_cb(struct retry_mod_data *rd, payment_continue(p); } + +static struct command_result * +local_channel_hints_listpeers(struct command *cmd, const char *buffer, + const jsmntok_t *toks, struct payment *p) +{ + const jsmntok_t *peers, *peer, *channels, *channel, *spendsats, *scid, *dir, *connected; + size_t i, j; + peers = json_get_member(buffer, toks, "peers"); + + if (peers == NULL) + goto done; + /* cppcheck-suppress uninitvar - cppcheck can't undestand these macros. */ + json_for_each_arr(i, peer, peers) { + channels = json_get_member(buffer, peer, "channels"); + if (channels == NULL) + continue; + + connected = json_get_member(buffer, peer, "connected"); + + json_for_each_arr(j, channel, channels) { + struct channel_hint h; + spendsats = json_get_member(buffer, channel, "spendable_msat"); + scid = json_get_member(buffer, channel, "short_channel_id"); + dir = json_get_member(buffer, channel, "direction"); + assert(spendsats != NULL && scid != NULL && dir != NULL); + + json_to_bool(buffer, connected, &h.enabled); + json_to_short_channel_id(buffer, scid, &h.scid.scid); + json_to_int(buffer, dir, &h.scid.dir); + + json_to_msat(buffer, spendsats, &h.estimated_capacity); + tal_arr_expand(&p->channel_hints, h); + } + } + +done: + payment_continue(p); + return command_still_pending(cmd); +} + +static void local_channel_hints_cb(void *d UNUSED, struct payment *p) +{ + struct out_req *req; + /* If we are not the root we don't look up the channel balances since + * it is unlikely that the capacities have changed much since the root + * payment looked at them. We also only call `listpeers` when the + * payment is in state PAYMENT_STEP_INITIALIZED, right before calling + * `getroute`. */ + if (p->parent != NULL || p->step != PAYMENT_STEP_INITIALIZED) + return payment_continue(p); + + req = jsonrpc_request_start(p->plugin, NULL, "listpeers", + local_channel_hints_listpeers, + local_channel_hints_listpeers, p); + send_outreq(p->plugin, req); +} + +REGISTER_PAYMENT_MODIFIER(local_channel_hints, void *, NULL, local_channel_hints_cb); diff --git a/plugins/libplugin-pay.h b/plugins/libplugin-pay.h index f175d7988..a52505863 100644 --- a/plugins/libplugin-pay.h +++ b/plugins/libplugin-pay.h @@ -66,8 +66,20 @@ struct payment_result { struct preimage *payment_preimage; }; -/* Relevant information about a local channel so we can exclude them early. */ -struct channel_status { +/* Information about channels we inferred from a) looking at our channels, and + * b) from failures encountered during attempts to perform a payment. These + * are attached to the root payment, since that information is + * global. Attempts update the estimated channel capacities when starting, and + * get remove on failure. Success keeps the capacities, since the capacities + * changed due to the successful HTLCs. */ +struct channel_hint { + struct short_channel_id_dir scid; + + /* Upper bound on remove channels inferred from payment failures. */ + struct amount_msat estimated_capacity; + + /* Is the channel enabled? */ + bool enabled; }; /* Each payment goes through a number of steps that are always processed in @@ -168,6 +180,10 @@ struct payment { struct bolt11 *invoice; + /* tal_arr of channel_hints we incrementally learn while performing + * payment attempts. */ + struct channel_hint *channel_hints; + struct payment_result *result; }; @@ -210,6 +226,12 @@ struct retry_mod_data { extern struct payment_modifier dummy_pay_mod; REGISTER_PAYMENT_MODIFIER_HEADER(retry, struct retry_mod_data); +/* For the root payment we can seed the channel_hints with the result from + * `listpeers`, hence avoid channels that we know have insufficient capacity + * or are disabled. We do this only for the root payment, to minimize the + * overhead. */ +REGISTER_PAYMENT_MODIFIER_HEADER(local_channel_hints, void); + struct payment *payment_new(tal_t *ctx, struct command *cmd, struct payment *parent, struct payment_modifier **mods); diff --git a/plugins/pay.c b/plugins/pay.c index f8fbf83e8..dc37ac143 100644 --- a/plugins/pay.c +++ b/plugins/pay.c @@ -1708,8 +1708,9 @@ static void init(struct plugin *p, maxdelay_default = atoi(field); } -struct payment_modifier *paymod_mods[3] = { +struct payment_modifier *paymod_mods[4] = { &dummy_pay_mod, + &local_channel_hints_pay_mod, &retry_pay_mod, NULL, };