Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .msggen.json
Original file line number Diff line number Diff line change
Expand Up @@ -2249,6 +2249,7 @@
"GetRoutes.layers[]": 4,
"GetRoutes.maxdelay": 8,
"GetRoutes.maxfee_msat": 5,
"GetRoutes.maxhops": 10,
"GetRoutes.maxparts": 9,
"GetRoutes.source": 1
},
Expand Down Expand Up @@ -8816,6 +8817,10 @@
"added": "v24.08",
"deprecated": null
},
"GetRoutes.maxhops": {
"added": "v26.09",
"deprecated": null
},
"GetRoutes.maxparts": {
"added": "v25.09",
"deprecated": null
Expand Down
1 change: 1 addition & 0 deletions cln-grpc/proto/node.proto

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions cln-grpc/src/convert.rs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions cln-rpc/src/model.rs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 9 additions & 1 deletion contrib/msggen/msggen/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -17251,7 +17251,7 @@
"title": "Command for routing a payment",
"added": "v24.08",
"description": [
"The **getroutes** RPC command attempts to find the best set of paths for the payment from *source* to *destination* of *amount_msat*, using the given *layers* on top of the gossip information. The result is constrained by *maxfee*, and will arrive at the destination with *final_cltv*.",
"The **getroutes** RPC command attempts to find the best set of paths for the payment from *source* to *destination* of *amount_msat*, using the given *layers* on top of the gossip information. The result is constrained by *maxfee*, and will arrive at the destination with *final_cltv*. If *maxhops* is set, no returned path is longer than that.",
"",
"NOTE: The returned paths are a different format then *getroute*, being more appropriate for creating intermediary onion layers.",
"",
Expand Down Expand Up @@ -17327,6 +17327,14 @@
"Maximum number of routes in the solution."
],
"default": "100"
},
"maxhops": {
"type": "u32",
"added": "v26.09",
"description": [
"Maximum hops in any returned path. 0 means no limit."
],
"default": "0"
}
}
},
Expand Down
536 changes: 268 additions & 268 deletions contrib/pyln-grpc-proto/pyln/grpc/node_pb2.py

Large diffs are not rendered by default.

10 changes: 9 additions & 1 deletion doc/schemas/getroutes.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"title": "Command for routing a payment",
"added": "v24.08",
"description": [
"The **getroutes** RPC command attempts to find the best set of paths for the payment from *source* to *destination* of *amount_msat*, using the given *layers* on top of the gossip information. The result is constrained by *maxfee*, and will arrive at the destination with *final_cltv*.",
"The **getroutes** RPC command attempts to find the best set of paths for the payment from *source* to *destination* of *amount_msat*, using the given *layers* on top of the gossip information. The result is constrained by *maxfee*, and will arrive at the destination with *final_cltv*. If *maxhops* is set, no returned path is longer than that.",
"",
"NOTE: The returned paths are a different format then *getroute*, being more appropriate for creating intermediary onion layers.",
"",
Expand Down Expand Up @@ -81,6 +81,14 @@
"Maximum number of routes in the solution."
],
"default": "100"
},
"maxhops": {
"type": "u32",
"added": "v26.09",
"description": [
"Maximum hops in any returned path. 0 means no limit."
],
"default": "0"
}
}
},
Expand Down
8 changes: 6 additions & 2 deletions plugins/askrene/askrene.c
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,8 @@ struct getroutes_info {
/* Non-NULL if we are told to use "auto.localchans" */
struct layer *local_layer;
u32 maxparts;
/* 0 means no hop limit. */
size_t maxhops;
};

static void add_layer(const struct layer ***layers,
Expand Down Expand Up @@ -717,7 +719,7 @@ static struct command_result *do_getroutes(struct command *cmd,
info->dev_algo == ALGO_SINGLE_PATH,
deadline, srcnode, dstnode, info->amount,
info->maxfee, info->finalcltv, info->maxdelay, info->maxparts,
include_fees,
info->maxhops, include_fees,
cmd->idstr, cmd->filter,
include_next_node_id,
include_amount_msat,
Expand Down Expand Up @@ -907,7 +909,7 @@ static struct command_result *json_getroutes(struct command *cmd,
struct amount_msat *amount, *maxfee;
u32 *finalcltv, *maxdelay;
enum algorithm *dev_algo;
u32 *maxparts;
u32 *maxparts, *maxhops;

if (!param_check(cmd, buffer, params,
p_req("source", param_node_id, &source),
Expand All @@ -920,6 +922,7 @@ static struct command_result *json_getroutes(struct command *cmd,
maxdelay_allowed),
p_opt_def("maxparts", param_u32, &maxparts,
default_maxparts),
p_opt_def("maxhops", param_u32, &maxhops, 0),
p_opt_dev("dev_algorithm", param_algorithm,
&dev_algo, ALGO_DEFAULT),
NULL))
Expand Down Expand Up @@ -961,6 +964,7 @@ static struct command_result *json_getroutes(struct command *cmd,
info->dev_algo = *dev_algo;
info->additional_costs = new_htable(info, additional_cost_htable);
info->maxparts = *maxparts;
info->maxhops = *maxhops;

if (askrene->num_live_requests >= askrene->max_children) {
cmd_log(tmpctx, cmd, LOG_INFORM,
Expand Down
16 changes: 15 additions & 1 deletion plugins/askrene/child/algorithm.c
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,10 @@ bool dijkstra_path(const tal_t *ctx, const struct graph *graph,
const struct node source, const struct node destination,
bool prune, const s64 *capacity, const s64 cap_threshold,
const s64 *cost, const s64 *potential, struct arc *prev,
s64 *distance)
s64 *distance, size_t max_hops)
{
bool target_found = false;
u16 *hops;
assert(graph);
const size_t max_num_arcs = graph_max_num_arcs(graph);
const size_t max_num_nodes = graph_max_num_nodes(graph);
Expand Down Expand Up @@ -105,6 +106,14 @@ bool dijkstra_path(const tal_t *ctx, const struct graph *graph,
for (size_t i = 0; i < max_num_nodes; ++i)
prev[i].idx = INVALID_INDEX;

hops = NULL;
if (max_hops) {
hops = tal_arr(this_ctx, u16, max_num_nodes);
for (size_t i = 0; i < max_num_nodes; ++i)
hops[i] = UINT16_MAX;
hops[source.idx] = 0;
}

struct priorityqueue *q;
q = priorityqueue_new(this_ctx, max_num_nodes);
const s64 *const dijkstra_distance = priorityqueue_value(q);
Expand Down Expand Up @@ -143,13 +152,18 @@ bool dijkstra_path(const tal_t *ctx, const struct graph *graph,
/* Dijkstra only works with non-negative weights */
assert(cij >= 0);

if (max_hops && hops[cur] >= max_hops)
continue;

if (dijkstra_distance[next.idx] <=
dijkstra_distance[cur] + cij)
continue;

priorityqueue_update(q, next.idx,
dijkstra_distance[cur] + cij);
prev[next.idx] = arc;
if (max_hops)
hops[next.idx] = hops[cur] + 1;
}
}
for (size_t i = 0; i < max_num_nodes; i++)
Expand Down
3 changes: 2 additions & 1 deletion plugins/askrene/child/algorithm.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,12 @@ bool BFS_path(const tal_t *ctx, const struct graph *graph,
* cost[i]>=0
* if prune is true the destination must be valid
* */
/* max_hops is the arc limit on the reconstructed path. 0 means none. */
bool dijkstra_path(const tal_t *ctx, const struct graph *graph,
const struct node source, const struct node destination,
bool prune, const s64 *capacity, const s64 cap_threshold,
const s64 *cost, const s64 *potential, struct arc *prev,
s64 *distance);
s64 *distance, size_t max_hops);


/* Finds any flow that satisfy the capacity constraints:
Expand Down
10 changes: 6 additions & 4 deletions plugins/askrene/child/child.c
Original file line number Diff line number Diff line change
Expand Up @@ -219,8 +219,8 @@ void run_child(const struct gossmap *gossmap,
const struct gossmap_node *srcnode,
const struct gossmap_node *dstnode,
struct amount_msat amount, struct amount_msat maxfee,
u32 finalcltv, u32 maxdelay, size_t maxparts,
bool include_fees,
u32 finalcltv, u32 maxdelay, size_t maxparts, size_t maxhops,
bool include_fees,
const char *cmd_id,
struct json_filter *cmd_filter,
bool include_next_node_id,
Expand All @@ -243,11 +243,13 @@ void run_child(const struct gossmap *gossmap,
if (single_path) {
err = single_path_routes(rq, rq, deadline, srcnode, dstnode,
amount, maxfee, finalcltv,
maxdelay, &flows, &probability, &ecode);
maxdelay, maxhops, &flows,
&probability, &ecode);
} else {
err = default_routes(rq, rq, deadline, srcnode, dstnode,
amount, maxfee, finalcltv, maxdelay,
maxparts, &flows, &probability, &ecode);
maxparts, maxhops, &flows, &probability,
&ecode);
}
if (err) {
write_all(replyfd, &ecode, sizeof(ecode));
Expand Down
2 changes: 1 addition & 1 deletion plugins/askrene/child/child.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ void run_child(const struct gossmap *gossmap,
const struct gossmap_node *srcnode,
const struct gossmap_node *dstnode,
struct amount_msat amount, struct amount_msat maxfee,
u32 finalcltv, u32 maxdelay, size_t maxparts,
u32 finalcltv, u32 maxdelay, size_t maxparts, size_t maxhops,
bool include_fees,
const char *cmd_id,
struct json_filter *cmd_filter,
Expand Down
53 changes: 39 additions & 14 deletions plugins/askrene/child/mcf.c
Original file line number Diff line number Diff line change
Expand Up @@ -951,7 +951,8 @@ static struct flow **minflow(const tal_t *ctx,
const struct gossmap_node *target,
struct amount_msat amount,
u32 mu,
double delay_feefactor)
double delay_feefactor,
size_t maxhops)
{
struct flow **flow_paths;
/* We allocate everything off this, and free it at the end,
Expand Down Expand Up @@ -986,6 +987,7 @@ static struct flow **minflow(const tal_t *ctx,

params->delay_feefactor = delay_feefactor;
params->base_fee_penalty = base_fee_penalty_estimate(amount);
(void)maxhops;

// build the uncertainty network with linearization and residual arcs
struct graph *graph;
Expand Down Expand Up @@ -1171,7 +1173,7 @@ static struct flow **single_path_flow(const tal_t *ctx, const struct route_query
const struct gossmap_node *source,
const struct gossmap_node *target,
struct amount_msat amount, u32 mu,
double delay_feefactor)
double delay_feefactor, size_t maxhops)
{
struct flow **flow_paths;
/* We allocate everything off this, and free it at the end,
Expand Down Expand Up @@ -1216,7 +1218,7 @@ static struct flow **single_path_flow(const tal_t *ctx, const struct route_query
if (!dijkstra_path(working_ctx, graph, src, dst,
/* prune = */ true, arc_capacity,
/*threshold = */ 1, arc_cost, potential, prev,
distance)) {
distance, maxhops)) {
/* This might fail if we are unable to find a suitable route, it
* doesn't mean the plugin is broken, that's why we LOG_INFORM. */
child_log(tmpctx, LOG_INFORM,
Expand Down Expand Up @@ -1348,13 +1350,14 @@ 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,
size_t maxparts,
size_t maxparts, size_t maxhops,
struct flow ***flows, double *probability,
enum jsonrpc_errcode *ecode,
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,
size_t))
{
const tal_t *working_ctx = tal(ctx, tal_t);
const char *error_message;
Expand Down Expand Up @@ -1399,11 +1402,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,
maxhops);
} else {
new_flows =
solver(working_ctx, rq, srcnode, dstnode,
amount_to_deliver, mu, delay_feefactor);
amount_to_deliver, mu, delay_feefactor,
maxhops);
}

if (!new_flows) {
Expand Down Expand Up @@ -1505,6 +1510,26 @@ linear_routes(const tal_t *ctx, struct route_query *rq,
}
}

/* Caller asked for a hop cap. Disable the first hop and retry. */
if (maxhops) {
bool too_long = false;
for (size_t i = 0; i < tal_count(new_flows); i++) {
if (tal_count(new_flows[i]->path) <= maxhops)
continue;
too_long = true;
bitmap_set_bit(rq->disabled_chans,
gossmap_chan_idx(rq->gossmap,
new_flows[i]->path[0]) * 2
+ new_flows[i]->dirs[0]);
}
if (too_long) {
child_log(tmpctx, LOG_INFORM,
"A flow exceeds maxhops %zu, disabling its first hop and retrying",
maxhops);
continue;
}
}

/* Too much delay? */
if (finalcltv + flows_worst_delay(new_flows) > maxdelay) {
if (delay_feefactor > 10) {
Expand Down Expand Up @@ -1639,13 +1664,13 @@ 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, size_t maxparts,
struct flow ***flows,
size_t maxhops, struct flow ***flows,
double *probability,
enum jsonrpc_errcode *ecode)
{
return linear_routes(ctx, rq, deadline, srcnode, dstnode, amount, maxfee,
finalcltv, maxdelay, maxparts, flows, probability, ecode,
minflow);
finalcltv, maxdelay, maxparts, maxhops, flows,
probability, ecode, minflow);
}

const char *single_path_routes(const tal_t *ctx, struct route_query *rq,
Expand All @@ -1654,11 +1679,11 @@ 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, size_t maxhops,
struct flow ***flows, double *probability,
enum jsonrpc_errcode *ecode)
{
return linear_routes(ctx, rq, deadline, srcnode, dstnode, amount, maxfee,
finalcltv, maxdelay, 1, flows, probability, ecode,
single_path_flow);
finalcltv, maxdelay, 1, maxhops, flows, probability,
ecode, single_path_flow);
}
8 changes: 4 additions & 4 deletions plugins/askrene/child/mcf.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,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, size_t maxparts, struct flow ***flows,
double *probability,
u32 maxdelay, size_t maxparts, size_t maxhops,
struct flow ***flows, double *probability,
enum jsonrpc_errcode *ecode);

/* A wrapper to the single-path constrained solver. */
Expand All @@ -29,8 +29,8 @@ 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, size_t maxhops,
struct flow ***flows, double *probability,
enum jsonrpc_errcode *ecode);

#endif /* LIGHTNING_PLUGINS_ASKRENE_CHILD_MCF_H */
2 changes: 1 addition & 1 deletion plugins/askrene/test/run-dijkstra.c
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ int main(int argc, char *argv[])
struct node dst = {.idx = 6};

bool result = dijkstra_path(ctx, graph, src, dst, false, capacity, 1,
cost, potential, prev, distance);
cost, potential, prev, distance, 0);
CHECK(result);

int pathlen = 0;
Expand Down
Loading
Loading