diff --git a/plugins/libplugin-pay.c b/plugins/libplugin-pay.c index 46d9beb86..53e883bc9 100644 --- a/plugins/libplugin-pay.c +++ b/plugins/libplugin-pay.c @@ -233,25 +233,54 @@ static struct command_result *payment_getroute_error(struct command *cmd, return command_still_pending(cmd); } -/* Iterate through the channel_hints and exclude any channel that we are - * confident will not be able to handle this payment. */ -static void payment_getroute_add_excludes(struct payment *p, - struct json_stream *js) +static const struct short_channel_id_dir * +payment_get_excluded_channels(const tal_t *ctx, struct payment *p) { struct payment *root = payment_root(p); struct channel_hint *hint; - - json_array_start(js, "exclude"); + struct short_channel_id_dir *res = + tal_arr(ctx, struct short_channel_id_dir, 0); for (size_t i = 0; i < tal_count(root->channel_hints); i++) { hint = &root->channel_hints[i]; if (!hint->enabled) - json_add_short_channel_id_dir(js, NULL, &hint->scid); + tal_arr_expand(&res, hint->scid); else if (amount_msat_greater_eq(p->amount, hint->estimated_capacity)) - json_add_short_channel_id_dir(js, NULL, &hint->scid); + tal_arr_expand(&res, hint->scid); } + return res; +} + +static const struct node_id *payment_get_excluded_nodes(const tal_t *ctx, + struct payment *p) +{ + struct payment *root = payment_root(p); + return root->excluded_nodes; +} + +/* Iterate through the channel_hints and exclude any channel that we are + * confident will not be able to handle this payment. */ +static void payment_getroute_add_excludes(struct payment *p, + struct json_stream *js) +{ + const struct node_id *nodes; + const struct short_channel_id_dir *chans; + + json_array_start(js, "exclude"); + + /* Collect and exclude all channels that are disabled or we know have + * insufficient capacity. */ + chans = payment_get_excluded_channels(tmpctx, p); + for (size_t i=0; i 10 hops */ + size_t max_hops = ROUTING_MAX_HOPS / 2; + if (tal_count(hints[i]) > max_hops) { + tal_append_fmt(&mods, + "Trimmed routehint %zu (%zu hops) to %zu. ", + i, tal_count(hints[i]), max_hops); + trim_route(&hints[i], max_hops); + } + + /* If we are first hop, trim. */ + if (tal_count(hints[i]) > 0 + && node_id_eq(&hints[i][0].pubkey, myid)) { + tal_append_fmt(&mods, + "Removed ourselves from routehint %zu. ", + i); + trim_route(&hints[i], tal_count(hints[i])-1); + } + + /* If route is empty, remove altogether. */ + if (tal_count(hints[i]) == 0) { + tal_append_fmt(&mods, + "Removed empty routehint %zu. ", i); + tal_arr_remove(&hints, i); + i--; + } + } + + if (!streq(mods, "")) + d->routehint_modifications = tal_steal(d, mods); + + return tal_steal(d, hints); +} + +static bool routehint_excluded(struct payment *p, + const struct route_info *routehint) +{ + const struct node_id *nodes = payment_get_excluded_nodes(tmpctx, p); + const struct short_channel_id_dir *chans = + payment_get_excluded_channels(tmpctx, p); + + /* Note that we ignore direction here: in theory, we could have + * 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(routehint); i++) { + const struct route_info *r = &routehint[i]; + for (size_t j=0; tal_count(nodes); j++) + if (node_id_eq(&r->pubkey, &nodes[j])) + return true; + + for (size_t j = 0; j < tal_count(chans); j++) + if (short_channel_id_eq(&chans[j].scid, &r->short_channel_id)) + return true; + } + return false; +} + +static struct route_info *next_routehint(struct routehints_data *d, + struct payment *p) +{ + while (tal_count(d->routehints) > 0) { + if (!routehint_excluded(p, d->routehints[0])) { + d->current_routehint = d->routehints[0]; + tal_arr_remove(&d->routehints, 0); + return d->current_routehint; + } + tal_free(d->routehints[0]); + tal_arr_remove(&d->routehints, 0); + } + return NULL; +} + +/* Calculate how many millisatoshi we need at the start of this route + * to get msatoshi to the end. */ +static bool route_msatoshi(struct amount_msat *total, + const struct amount_msat msat, + const struct route_info *route, size_t num_route) +{ + *total = msat; + for (ssize_t i = num_route - 1; i >= 0; i--) { + if (!amount_msat_add_fee(total, + route[i].fee_base_msat, + route[i].fee_proportional_millionths)) + return false; + } + return true; +} + +/* The pubkey to use is the destination of this routehint. */ +static const struct node_id *route_pubkey(const struct payment *p, + const struct route_info *routehint, + size_t n) +{ + if (n == tal_count(routehint)) + return p->destination; + return &routehint[n].pubkey; +} + +static u32 route_cltv(u32 cltv, + const struct route_info *route, size_t num_route) +{ + for (size_t i = 0; i < num_route; i++) + cltv += route[i].cltv_expiry_delta; + return cltv; +} + +static void routehint_step_cb(struct routehints_data *d, struct payment *p) +{ + struct routehints_data *pd; + struct route_hop hop; + const struct payment *root = payment_root(p); + + if (p->step == PAYMENT_STEP_INITIALIZED) { + if (root->invoice == NULL || root->invoice->routes == NULL) + return payment_continue(p); + + /* The root payment gets the unmodified routehints, children may + * start dropping some as they learn that they were not + * functional. */ + if (p->parent == NULL) { + d->routehints = filter_routehints(d, p->local_id, + p->invoice->routes); + } else { + pd = payment_mod_get_data(p->parent, + &routehints_pay_mod); + d->routehints = tal_dup_talarr(d, struct route_info *, + pd->routehints); + } + d->current_routehint = next_routehint(d, p); + + if (d->current_routehint != NULL) { + /* Change the destination and compute the final msatoshi + * amount to send to the routehint entry point. */ + if (!route_msatoshi(&p->getroute->amount, p->amount, + d->current_routehint, + tal_count(d->current_routehint))) { + } + d->final_cltv = p->getroute->cltv; + p->getroute->destination = &d->current_routehint[0].pubkey; + p->getroute->cltv = + route_cltv(p->getroute->cltv, d->current_routehint, + tal_count(d->current_routehint)); + } + } else if (p->step == PAYMENT_STEP_GOT_ROUTE) { + /* Now it's time to stitch the two partial routes together. */ + struct amount_msat dest_amount; + struct route_info *routehint = d->current_routehint; + struct route_hop *prev_hop; + for (ssize_t i = 0; i < tal_count(routehint); i++) { + prev_hop = &p->route[tal_count(p->route)-1]; + if (!route_msatoshi(&dest_amount, p->amount, + routehint + i + 1, + tal_count(routehint) - i - 1)) { + /* Just let it fail, since we couldn't stitch + * the routes together. */ + return payment_continue(p); + } + + hop.nodeid = *route_pubkey(p, routehint, i + 1); + hop.style = ROUTE_HOP_TLV; + hop.channel_id = routehint[i].short_channel_id; + hop.amount = dest_amount; + hop.delay = route_cltv(d->final_cltv, routehint + i + 1, + tal_count(routehint) - i - 1); + + /* Should we get a failure inside the routehint we'll + * need the direction so we can exclude it. Luckily + * it's rather easy to compute given the two + * subsequent hops. */ + hop.direction = + node_id_cmp(&prev_hop->nodeid, &hop.nodeid) > 0 ? 1 + : 0; + tal_arr_expand(&p->route, hop); + } + } + + payment_continue(p); +} + +static struct routehints_data *routehint_data_init(struct payment *p) +{ + /* We defer the actual initialization to the step callback when we have + * the invoice attached. */ + return talz(p, struct routehints_data); +} + +REGISTER_PAYMENT_MODIFIER(routehints, struct routehints_data *, + routehint_data_init, routehint_step_cb); diff --git a/plugins/libplugin-pay.h b/plugins/libplugin-pay.h index b822638ea..6f1148f73 100644 --- a/plugins/libplugin-pay.h +++ b/plugins/libplugin-pay.h @@ -264,8 +264,25 @@ void *payment_mod_get_data(const struct payment *payment, struct retry_mod_data { int retries; }; + +struct routehints_data { + /* What we did about routehints (if anything) */ + const char *routehint_modifications; + + /* Any remaining routehints to try. */ + struct route_info **routehints; + + /* Current routehint, if any. */ + struct route_info *current_routehint; + + /* We modify the CLTV in the getroute call, so we need to remember + * what the final cltv delta was so we re-apply it correctly. */ + u32 final_cltv; +}; + /* List of globally available payment modifiers. */ REGISTER_PAYMENT_MODIFIER_HEADER(retry, struct retry_mod_data); +REGISTER_PAYMENT_MODIFIER_HEADER(routehints, struct routehints_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 diff --git a/plugins/pay.c b/plugins/pay.c index 720d4a5d4..ecacc639b 100644 --- a/plugins/pay.c +++ b/plugins/pay.c @@ -1834,6 +1834,7 @@ static void init(struct plugin *p, } struct payment_modifier *paymod_mods[] = { + &routehints_pay_mod, &local_channel_hints_pay_mod, &retry_pay_mod, NULL,