diff --git a/plugins/libplugin-pay.c b/plugins/libplugin-pay.c index 9daa21dbe..9b77738f9 100644 --- a/plugins/libplugin-pay.c +++ b/plugins/libplugin-pay.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -505,75 +506,6 @@ static void payment_chanhints_apply_route(struct payment *p, bool remove) } } -static struct command_result *payment_getroute_result(struct command *cmd, - const char *buffer, - const jsmntok_t *toks, - struct payment *p) -{ - const jsmntok_t *rtok = json_get_member(buffer, toks, "route"); - struct amount_msat fee; - assert(rtok != NULL); - p->route = json_to_route(p, buffer, rtok); - p->step = PAYMENT_STEP_GOT_ROUTE; - - fee = payment_route_fee(p); - - /* Ensure that our fee and CLTV budgets are respected. */ - if (amount_msat_greater(fee, p->constraints.fee_budget)) { - payment_exclude_most_expensive(p); - p->route = tal_free(p->route); - payment_fail( - p, "Fee exceeds our fee budget: %s > %s, discarding route", - type_to_string(tmpctx, struct amount_msat, &fee), - type_to_string(tmpctx, struct amount_msat, - &p->constraints.fee_budget)); - return command_still_pending(cmd); - } - - if (p->route[0].delay > p->constraints.cltv_budget) { - u32 delay = p->route[0].delay; - payment_exclude_longest_delay(p); - p->route = tal_free(p->route); - payment_fail(p, "CLTV delay exceeds our CLTV budget: %d > %d", - delay, p->constraints.cltv_budget); - return command_still_pending(cmd); - } - - /* Now update the constraints in fee_budget and cltv_budget so - * modifiers know what constraints they need to adhere to. */ - if (!payment_constraints_update(&p->constraints, fee, p->route[0].delay)) { - paymod_log(p, LOG_BROKEN, - "Could not update constraints."); - abort(); - } - - /* Allow modifiers to modify the route, before - * payment_compute_onion_payloads uses the route to generate the - * onion_payloads */ - payment_continue(p); - return command_still_pending(cmd); -} - -static struct command_result *payment_getroute_error(struct command *cmd, - const char *buffer, - const jsmntok_t *toks, - struct payment *p) -{ - int code; - const jsmntok_t *codetok = json_get_member(buffer, toks, "code"), - *msgtok = json_get_member(buffer, toks, "message"); - json_to_int(buffer, codetok, &code); - p->route = NULL; - - payment_fail( - p, "Error computing a route to %s: %.*s (%d)", - type_to_string(tmpctx, struct node_id, p->getroute->destination), - json_tok_full_len(msgtok), json_tok_full(buffer, msgtok), code); - - /* Let payment_finished_ handle this, so we mark it as pending */ - return command_still_pending(cmd); -} - static const struct short_channel_id_dir * payment_get_excluded_channels(const tal_t *ctx, struct payment *p) { @@ -608,49 +540,256 @@ static const struct node_id *payment_get_excluded_nodes(const tal_t *ctx, 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) +/* FIXME: This is slow! */ +static const struct channel_hint *find_hint(const struct channel_hint *hints, + const struct short_channel_id *scid, + int dir) { - 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; itemp_exclusion) - for (size_t i = 0; i < tal_count(p->temp_exclusion); ++i) - json_add_string(js, NULL, p->temp_exclusion[i]); - - json_array_end(js); + for (size_t i = 0; i < tal_count(hints); i++) { + if (short_channel_id_eq(scid, &hints[i].scid.scid) + && dir == hints[i].scid.dir) + return &hints[i]; + } + return NULL; } -static void payment_getroute(struct payment *p) +/* FIXME: This is slow! */ +static bool dst_is_excluded(const struct gossmap *gossmmap, + const struct gossmap_chan *c, + int dir, + const struct node_id *nodes) { - struct out_req *req; - req = jsonrpc_request_start(p->plugin, NULL, "getroute", - payment_getroute_result, - payment_getroute_error, p); - json_add_node_id(req->js, "id", p->getroute->destination); - json_add_amount_msat_only(req->js, "msatoshi", p->getroute->amount); - json_add_num(req->js, "cltv", p->getroute->cltv); - json_add_num(req->js, "maxhops", p->getroute->max_hops); - json_add_member(req->js, "riskfactor", false, "%lf", - p->getroute->riskfactorppm / 1000000.0); - payment_getroute_add_excludes(p, req->js); - send_outreq(p->plugin, req); + struct node_id dstid; + + /* Premature optimization */ + if (!tal_count(nodes)) + return false; + + gossmap_node_get_id(gossmap, gossmap_nth_node(gossmap, c, !dir), + &dstid); + for (size_t i = 0; i < tal_count(nodes); i++) { + if (node_id_eq(&dstid, &nodes[i])) + return true; + } + return false; +} + +static bool payment_route_check(const struct gossmap *gossmap, + const struct gossmap_chan *c, + int dir, + struct amount_msat amount, + struct payment *p) +{ + struct short_channel_id scid; + const struct channel_hint *hint; + + if (dst_is_excluded(gossmap, c, dir, payment_root(p)->excluded_nodes)) + return false; + + if (dst_is_excluded(gossmap, c, dir, p->temp_exclusion)) + return false; + + scid = gossmap_chan_scid(gossmap, c); + hint = find_hint(payment_root(p)->channel_hints, &scid, dir); + if (!hint) + return true; + + if (!hint->enabled) + return false; + + if (amount_msat_greater_eq(amount, hint->estimated_capacity)) + /* We exclude on equality because we've set the + * estimate to the smallest failed attempt. */ + return false; + + if (hint->local && hint->htlc_budget == 0) + /* If we cannot add any HTLCs to the channel we + * shouldn't look for a route through that channel */ + return false; + + return true; +} + +static bool payment_route_can_carry(const struct gossmap *map, + const struct gossmap_chan *c, + int dir, + struct amount_msat amount, + struct payment *p) +{ + if (!route_can_carry(map, c, dir, amount, p)) + return false; + + return payment_route_check(map, c, dir, amount, p); +} + +static bool payment_route_can_carry_even_disabled(const struct gossmap *map, + const struct gossmap_chan *c, + int dir, + struct amount_msat amount, + struct payment *p) +{ + if (!route_can_carry_even_disabled(map, c, dir, amount, p)) + return false; + + return payment_route_check(map, c, dir, amount, p); +} + +static struct route_hop *route_hops_from_route(const tal_t *ctx, + struct payment *p, + struct route **r) +{ + struct route_hop *hops = tal_arr(ctx, struct route_hop, tal_count(r)); + struct amount_msat amt; + u32 delay; + + for (size_t i = 0; i < tal_count(hops); i++) { + const struct gossmap_node *dst; + + hops[i].channel_id = gossmap_chan_scid(gossmap, r[i]->c); + hops[i].direction = r[i]->dir; + hops[i].blinding = NULL; + + /* nodeid is nodeid of *dst* */ + dst = gossmap_nth_node(gossmap, r[i]->c, !r[i]->dir); + gossmap_node_get_id(gossmap, dst, &hops[i].nodeid); + if (gossmap_node_has_feature(gossmap, dst, OPT_VAR_ONION) != -1) + hops[i].style = ROUTE_HOP_TLV; + else + hops[i].style = ROUTE_HOP_LEGACY; + } + + /* Now iterate backwards to derive amount and delay. */ + amt = p->getroute->amount; + delay = p->getroute->cltv; + for (int i = tal_count(hops) - 1; i >= 0; i--) { + const struct half_chan *h = &r[i]->c->half[r[i]->dir]; + + hops[i].amount = amt; + hops[i].delay = delay; + + if (!amount_msat_add_fee(&amt, + h->base_fee, h->proportional_fee)) + abort(); + delay += h->delay; + } + + return hops; +} + +static struct command_result *payment_getroute(struct payment *p) +{ + const struct dijkstra *dij; + const struct gossmap_node *dst, *src; + struct route **r; + struct amount_msat fee; + bool (*can_carry)(const struct gossmap *, + const struct gossmap_chan *, + int, + struct amount_msat, + struct payment *); + + /* Make sure we're up-to-date with any new entries */ + gossmap_refresh(gossmap); + + dst = gossmap_find_node(gossmap, p->getroute->destination); + if (!dst) { + payment_fail( + p, "Unknown destination %s", + type_to_string(tmpctx, struct node_id, + p->getroute->destination)); + + /* Let payment_finished_ handle this, so we mark it as pending */ + return command_still_pending(p->cmd); + } + + /* If we don't exist in gossip, routing can't happen. */ + src = gossmap_find_node(gossmap, p->local_id); + if (!src) { + payment_fail(p, "We don't have any channels"); + + /* Let payment_finished_ handle this, so we mark it as pending */ + return command_still_pending(p->cmd); + } + + can_carry = payment_route_can_carry; + dij = dijkstra(tmpctx, gossmap, dst, p->getroute->amount, + p->getroute->riskfactorppm / 1000000.0, + can_carry, route_score_cheaper, p); + r = route_from_dijkstra(tmpctx, gossmap, dij, src); + if (!r) { + /* Try using disabled channels too */ + /* FIXME: is there somewhere we can annotate this for paystatus? */ + can_carry = payment_route_can_carry_even_disabled; + dij = dijkstra(tmpctx, gossmap, dst, p->getroute->amount, + p->getroute->riskfactorppm / 1000000.0, + can_carry, route_score_cheaper, p); + r = route_from_dijkstra(tmpctx, gossmap, dij, src); + if (!r) { + payment_fail(p, "No path found"); + return command_still_pending(p->cmd); + } + } + + /* If it's too far, fall back to using shortest path. */ + if (tal_count(r) > p->getroute->max_hops) { + /* FIXME: is there somewhere we can annotate this for paystatus? */ + dij = dijkstra(tmpctx, gossmap, dst, p->getroute->amount, + p->getroute->riskfactorppm / 1000000.0, + can_carry, route_score_shorter, p); + r = route_from_dijkstra(tmpctx, gossmap, dij, src); + if (!r) { + payment_fail(p, "No path found"); + return command_still_pending(p->cmd); + } + + /* If it's still too far, fail. */ + if (tal_count(r) > p->getroute->max_hops) { + payment_fail(p, "Shortest path found was length %zu", + tal_count(p->route)); + return command_still_pending(p->cmd); + } + } + + /* OK, now we *have* a route */ + p->step = PAYMENT_STEP_GOT_ROUTE; + p->route = route_hops_from_route(p, p, r); + + fee = payment_route_fee(p); + + /* Ensure that our fee and CLTV budgets are respected. */ + if (amount_msat_greater(fee, p->constraints.fee_budget)) { + payment_exclude_most_expensive(p); + p->route = tal_free(p->route); + payment_fail( + p, "Fee exceeds our fee budget: %s > %s, discarding route", + type_to_string(tmpctx, struct amount_msat, &fee), + type_to_string(tmpctx, struct amount_msat, + &p->constraints.fee_budget)); + return command_still_pending(p->cmd); + } + + if (p->route[0].delay > p->constraints.cltv_budget) { + u32 delay = p->route[0].delay; + payment_exclude_longest_delay(p); + p->route = tal_free(p->route); + payment_fail(p, "CLTV delay exceeds our CLTV budget: %d > %d", + delay, p->constraints.cltv_budget); + return command_still_pending(p->cmd); + } + + /* Now update the constraints in fee_budget and cltv_budget so + * modifiers know what constraints they need to adhere to. */ + if (!payment_constraints_update(&p->constraints, fee, p->route[0].delay)) { + paymod_log(p, LOG_BROKEN, + "Could not update constraints."); + abort(); + } + + /* Allow modifiers to modify the route, before + * payment_compute_onion_payloads uses the route to generate the + * onion_payloads */ + payment_continue(p); + return command_still_pending(p->cmd); } static u8 *tal_towire_legacy_payload(const tal_t *ctx, const struct legacy_payload *payload) @@ -2144,30 +2283,25 @@ static u32 route_cltv(u32 cltv, * `excludes` parameter of `getroute`. */ static -const char **routehint_generate_exclusion_list(const tal_t *ctx, - struct route_info *routehint, - struct payment *payment) +struct node_id *routehint_generate_exclusion_list(const tal_t *ctx, + struct route_info *routehint, + struct payment *payment) { - const char **exc; - size_t i; + struct node_id *exc; if (!routehint || tal_count(routehint) == 0) /* Nothing to exclude. */ return NULL; - exc = tal_arr(ctx, const char *, 0); + exc = tal_arr(ctx, struct node_id, tal_count(routehint)); /* Exclude every node except the first, because the first is * the entry point to the routehint. */ - for (i = 1 /* Skip the first! */; i < tal_count(routehint); ++i) - tal_arr_expand(&exc, - type_to_string(exc, struct node_id, - &routehint[i].pubkey)); + for (size_t i = 1 /* Skip the first! */; i < tal_count(routehint); ++i) + exc[i-1] = routehint[i].pubkey; + /* Also exclude the destination, because it would be foolish to * pass through it and *then* go to the routehint entry point. */ - tal_arr_expand(&exc, - type_to_string(exc, struct node_id, - payment->destination)); - + exc[tal_count(routehint)-1] = *payment->destination; return exc; } diff --git a/plugins/libplugin-pay.h b/plugins/libplugin-pay.h index 773c17905..183de12c5 100644 --- a/plugins/libplugin-pay.h +++ b/plugins/libplugin-pay.h @@ -235,7 +235,7 @@ struct payment { struct node_id *excluded_nodes; /* Optional temporarily excluded channels/nodes (i.e. this routehint) */ - const char **temp_exclusion; + struct node_id *temp_exclusion; struct payment_result *result;