diff --git a/plugins/askrene/askrene.c b/plugins/askrene/askrene.c index 23e541d75911..0bfa34babdaa 100644 --- a/plugins/askrene/askrene.c +++ b/plugins/askrene/askrene.c @@ -399,6 +399,7 @@ struct getroutes_info { struct node_id source, dest; struct amount_msat amount, maxfee; u32 finalcltv, maxdelay; + u64 needed_features; /* algorithm selection, only dev */ enum algorithm dev_algo; const char **layers; @@ -630,6 +631,12 @@ static struct command_result *do_getroutes(struct command *cmd, goto fail; } + if (!node_has_needed_features(askrene->gossmap, srcnode, info->needed_features)) { + err = rq_log(tmpctx, rq, LOG_INFORM, "Source node %s does not have the needed features", + fmt_node_id(tmpctx, &info->source)); + goto fail; + } + /* checkout the destination */ const struct gossmap_node *dstnode = gossmap_find_node(askrene->gossmap, &info->dest); @@ -640,6 +647,12 @@ static struct command_result *do_getroutes(struct command *cmd, goto fail; } + if (!node_has_needed_features(askrene->gossmap, dstnode, info->needed_features)) { + err = rq_log(tmpctx, rq, LOG_INFORM, "Destination node %s does not have the needed features", + fmt_node_id(tmpctx, &info->dest)); + goto fail; + } + /* auto.no_mpp_support layer overrides any choice of algorithm. */ if (have_layer(info->layers, "auto.no_mpp_support") && info->dev_algo != ALGO_SINGLE_PATH) { @@ -663,12 +676,12 @@ static struct command_result *do_getroutes(struct command *cmd, if (info->dev_algo == ALGO_SINGLE_PATH) { err = single_path_routes(rq, rq, deadline, srcnode, dstnode, info->amount, info->maxfee, info->finalcltv, - info->maxdelay, &flows, &probability); + info->maxdelay, info->needed_features, &flows, &probability); } else { assert(info->dev_algo == ALGO_DEFAULT); err = default_routes(rq, rq, deadline, srcnode, dstnode, info->amount, info->maxfee, info->finalcltv, - info->maxdelay, &flows, &probability); + info->maxdelay, info->needed_features, &flows, &probability); } struct timerel time_delta = timemono_between(time_mono(), time_start); @@ -821,6 +834,7 @@ static struct command_result *json_getroutes(struct command *cmd, u32 *finalcltv, *maxdelay; enum algorithm *dev_algo; u32 *maxparts; + u64* needed_features; if (!param_check(cmd, buffer, params, p_req("source", param_node_id, &source), @@ -835,6 +849,8 @@ static struct command_result *json_getroutes(struct command *cmd, default_maxparts), p_opt_dev("dev_algorithm", param_algorithm, &dev_algo, ALGO_DEFAULT), + p_opt_def("needed_features", param_u64, &needed_features, + 0), NULL)) return command_param_failed(); plugin_log(cmd->plugin, LOG_TRACE, "%s called: %.*s", __func__, @@ -870,6 +886,7 @@ static struct command_result *json_getroutes(struct command *cmd, info->additional_costs = tal(info, struct additional_cost_htable); additional_cost_htable_init(info->additional_costs); info->maxparts = *maxparts; + info->needed_features = *needed_features; if (have_layer(info->layers, "auto.localchans")) { struct out_req *req; diff --git a/plugins/askrene/mcf.c b/plugins/askrene/mcf.c index a59c6feb6b05..b84131f03b14 100644 --- a/plugins/askrene/mcf.c +++ b/plugins/askrene/mcf.c @@ -2,9 +2,11 @@ #include #include #include +#include #include #include #include +#include #include #include #include @@ -301,6 +303,8 @@ struct pay_parameters { double delay_feefactor; double base_fee_penalty; + + u64 needed_features; }; /* Helper function. @@ -575,6 +579,8 @@ static void init_linear_network(const tal_t *ctx, { const u32 node_id = gossmap_node_idx(gossmap,node); + if (!node_has_needed_features(gossmap, node, params->needed_features)) goto next; + for(size_t j=0;jnum_chans;++j) { int half; @@ -642,6 +648,7 @@ static void init_linear_network(const tal_t *ctx, (*arc_fee_cost)[dual.idx] = -fee_cost; } } + next:; } } @@ -971,7 +978,7 @@ struct flow **minflow(const tal_t *ctx, const struct gossmap_node *target, struct amount_msat amount, u32 mu, - double delay_feefactor) + double delay_feefactor, u64 needed_features) { struct flow **flow_paths; /* We allocate everything off this, and free it at the end, @@ -1006,6 +1013,7 @@ struct flow **minflow(const tal_t *ctx, params->delay_feefactor = delay_feefactor; params->base_fee_penalty = base_fee_penalty_estimate(amount); + params->needed_features = needed_features; // build the uncertainty network with linearization and residual arcs struct graph *graph; @@ -1099,6 +1107,8 @@ static void init_linear_network_single_path( node = gossmap_next_node(gossmap, node)) { const u32 node_id = gossmap_node_idx(gossmap, node); + if (!node_has_needed_features(gossmap, node, params->needed_features)) goto next; + for (size_t j = 0; j < node->num_chans; ++j) { int half; const struct gossmap_chan *c = @@ -1164,6 +1174,7 @@ static void init_linear_network_single_path( fee_msat + params->delay_feefactor * c->half[half].delay; } + next:; } } @@ -1172,7 +1183,7 @@ struct flow **single_path_flow(const tal_t *ctx, const struct route_query *rq, const struct gossmap_node *source, const struct gossmap_node *target, struct amount_msat amount, u32 mu, - double delay_feefactor) + double delay_feefactor, u64 needed_features) { struct flow **flow_paths; /* We allocate everything off this, and free it at the end, @@ -1189,6 +1200,7 @@ struct flow **single_path_flow(const tal_t *ctx, const struct route_query *rq, params->accuracy = amount; params->delay_feefactor = delay_feefactor; params->base_fee_penalty = base_fee_penalty_estimate(amount); + params->needed_features = needed_features; struct graph *graph; double *arc_prob_cost; @@ -1350,11 +1362,12 @@ linear_routes(const tal_t *ctx, struct route_query *rq, const struct gossmap_node *srcnode, const struct gossmap_node *dstnode, struct amount_msat amount, struct amount_msat maxfee, u32 finalcltv, u32 maxdelay, - struct flow ***flows, double *probability, + u64 needed_features, struct flow ***flows, double *probability, struct flow **(*solver)(const tal_t *, const struct route_query *, const struct gossmap_node *, const struct gossmap_node *, - struct amount_msat, u32, double)) + struct amount_msat, u32, double, + u64)) { const tal_t *working_ctx = tal(ctx, tal_t); const char *error_message; @@ -1398,11 +1411,13 @@ linear_routes(const tal_t *ctx, struct route_query *rq, SINGLE_PATH_THRESHOLD)) { new_flows = single_path_flow(working_ctx, rq, srcnode, dstnode, amount_to_deliver, - mu, delay_feefactor); + mu, delay_feefactor, + needed_features); } else { new_flows = solver(working_ctx, rq, srcnode, dstnode, - amount_to_deliver, mu, delay_feefactor); + amount_to_deliver, mu, delay_feefactor, + needed_features); } if (!new_flows) { @@ -1630,11 +1645,11 @@ const char *default_routes(const tal_t *ctx, struct route_query *rq, const struct gossmap_node *srcnode, const struct gossmap_node *dstnode, struct amount_msat amount, struct amount_msat maxfee, - u32 finalcltv, u32 maxdelay, struct flow ***flows, - double *probability) + u32 finalcltv, u32 maxdelay, u64 needed_features, + struct flow ***flows, double *probability) { return linear_routes(ctx, rq, deadline, srcnode, dstnode, amount, maxfee, - finalcltv, maxdelay, flows, probability, minflow); + finalcltv, maxdelay, needed_features, flows, probability, minflow); } const char *single_path_routes(const tal_t *ctx, struct route_query *rq, @@ -1643,10 +1658,25 @@ const char *single_path_routes(const tal_t *ctx, struct route_query *rq, const struct gossmap_node *dstnode, struct amount_msat amount, struct amount_msat maxfee, u32 finalcltv, - u32 maxdelay, struct flow ***flows, - double *probability) + u32 maxdelay, u64 needed_features, + struct flow ***flows, double *probability) { return linear_routes(ctx, rq, deadline, srcnode, dstnode, amount, maxfee, - finalcltv, maxdelay, flows, probability, + finalcltv, maxdelay, needed_features, flows, probability, single_path_flow); } + +bool node_has_needed_features(const struct gossmap *map, + const struct gossmap_node *n, + u64 needed_features) +{ + /* It must have all the features we need */ + while (needed_features) { + int feature = bitops_ls64(needed_features); + if (gossmap_node_get_feature(map, n, feature) == -1) + return false; + needed_features &= ~(1ULL << feature); + } + return true; +} + diff --git a/plugins/askrene/mcf.h b/plugins/askrene/mcf.h index 7d60159063d6..2babaa2b2fbb 100644 --- a/plugins/askrene/mcf.h +++ b/plugins/askrene/mcf.h @@ -20,6 +20,7 @@ struct route_query; * @single_part: don't do MCF at all, just create a single flow. * * @delay_feefactor converts 1 block delay into msat, as if it were an additional + * @needed_features: needed features for the nodes to be considered * fee. So if a CLTV delay on a node is 5 blocks, that's treated as if it * were a fee of 5 * @delay_feefactor. * @@ -31,7 +32,7 @@ struct flow **minflow(const tal_t *ctx, const struct gossmap_node *target, struct amount_msat amount, u32 mu, - double delay_feefactor); + double delay_feefactor, u64 needed_features); /** * API for min cost single path. @@ -44,6 +45,7 @@ struct flow **minflow(const tal_t *ctx, * @delay_feefactor: convert 1 block delay into msat. * * @delay_feefactor converts 1 block delay into msat, as if it were an additional + * @needed_features: needed features for the nodes to be considered * fee. So if a CLTV delay on a node is 5 blocks, that's treated as if it * were a fee of 5 * @delay_feefactor. * @@ -53,7 +55,7 @@ struct flow **single_path_flow(const tal_t *ctx, const struct route_query *rq, const struct gossmap_node *source, const struct gossmap_node *target, struct amount_msat amount, u32 mu, - double delay_feefactor); + double delay_feefactor, u64 needed_features); /* To sanity check: this is the approximation mcf uses for the cost * of each channel. */ @@ -69,8 +71,8 @@ const char *default_routes(const tal_t *ctx, struct route_query *rq, const struct gossmap_node *dstnode, struct amount_msat amount, struct amount_msat maxfee, u32 finalcltv, - u32 maxdelay, struct flow ***flows, - double *probability); + u32 maxdelay, u64 needed_features, + struct flow ***flows, double *probability); /* A wrapper to the single-path constrained solver. */ const char *single_path_routes(const tal_t *ctx, struct route_query *rq, @@ -79,7 +81,12 @@ const char *single_path_routes(const tal_t *ctx, struct route_query *rq, const struct gossmap_node *dstnode, struct amount_msat amount, struct amount_msat maxfee, u32 finalcltv, - u32 maxdelay, struct flow ***flows, - double *probability); + u32 maxdelay, u64 needed_features, + struct flow ***flows, double *probability); + +bool node_has_needed_features(const struct gossmap *map, + const struct gossmap_node *n, + u64 needed_features); + #endif /* LIGHTNING_PLUGINS_ASKRENE_MCF_H */