From 33e2f0a47bc587609a30d107c9d0af29ba1431b1 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 16 Feb 2026 17:31:33 +1030 Subject: [PATCH] askrene: move fork() entry point into its own file. Now there's only one file clearly shared by both parent and child. Signed-off-by: Rusty Russell --- plugins/askrene/Makefile | 1 + plugins/askrene/askrene.c | 265 +------------------------------- plugins/askrene/askrene.h | 8 - plugins/askrene/child/entry.c | 279 ++++++++++++++++++++++++++++++++++ plugins/askrene/child/entry.h | 29 ++++ 5 files changed, 311 insertions(+), 271 deletions(-) create mode 100644 plugins/askrene/child/entry.c create mode 100644 plugins/askrene/child/entry.h diff --git a/plugins/askrene/Makefile b/plugins/askrene/Makefile index 3c3f5c4b7..fd8e9f516 100644 --- a/plugins/askrene/Makefile +++ b/plugins/askrene/Makefile @@ -5,6 +5,7 @@ PLUGIN_ASKRENE_PARENT_SRC := \ plugins/askrene/reserve.c \ PLUGIN_ASKRENE_CHILD_SRC := \ + plugins/askrene/child/entry.c \ plugins/askrene/child/mcf.c \ plugins/askrene/child/dijkstra.c \ plugins/askrene/child/flow.c \ diff --git a/plugins/askrene/askrene.c b/plugins/askrene/askrene.c index 8a4c009d6..bbbae51ec 100644 --- a/plugins/askrene/askrene.c +++ b/plugins/askrene/askrene.c @@ -8,9 +8,6 @@ */ #include "config.h" #include -#include -#include -#include #include #include #include @@ -26,17 +23,12 @@ #include #include #include -#include -#include +#include #include #include #include -#include #include -/* Temporary hack */ -static bool am_child = false; - /* "spendable" for a channel assumes a single HTLC: for additional HTLCs, * the need to pay for fees (if we're the owner) reduces it */ struct per_htlc_cost { @@ -340,27 +332,6 @@ const char *rq_log(const tal_t *ctx, return msg; } -static const char *fmt_route(const tal_t *ctx, - const struct route *route, - struct amount_msat delivers, - u32 final_cltv) -{ - char *str = tal_strdup(ctx, ""); - - for (size_t i = 0; i < tal_count(route->hops); i++) { - struct short_channel_id_dir scidd; - scidd.scid = route->hops[i].scid; - scidd.dir = route->hops[i].direction; - tal_append_fmt(&str, "%s/%u %s -> ", - fmt_amount_msat(tmpctx, route->hops[i].amount), - route->hops[i].delay, - fmt_short_channel_id_dir(tmpctx, &scidd)); - } - tal_append_fmt(&str, "%s/%u", - fmt_amount_msat(tmpctx, delivers), final_cltv); - return str; -} - enum algorithm { /* Min. Cost Flow by successive shortests paths. */ ALGO_DEFAULT, @@ -439,133 +410,6 @@ static void apply_layers(struct askrene *askrene, struct route_query *rq, } } -/* Convert back into routes, with delay and other information fixed */ -static struct route **convert_flows_to_routes(const tal_t *ctx, - struct route_query *rq, - u32 finalcltv, - struct flow **flows, - struct amount_msat **amounts, - bool include_fees) -{ - struct route **routes; - routes = tal_arr(ctx, struct route *, tal_count(flows)); - *amounts = tal_arr(ctx, struct amount_msat, tal_count(flows)); - - for (size_t i = 0; i < tal_count(flows); i++) { - struct route *r; - struct amount_msat msat; - u32 delay; - - routes[i] = r = tal(routes, struct route); - r->success_prob = flow_probability(flows[i], rq); - r->hops = tal_arr(r, struct route_hop, tal_count(flows[i]->path)); - - msat = flows[i]->delivers; - delay = finalcltv; - - if (!include_fees) { - /* Fill in backwards to calc amount and delay */ - for (int j = tal_count(flows[i]->path) - 1; j >= 0; - j--) { - struct route_hop *rh = &r->hops[j]; - struct gossmap_node *far_end; - const struct half_chan *h = - flow_edge(flows[i], j); - - if (!amount_msat_add_fee(&msat, h->base_fee, - h->proportional_fee)) - plugin_err(rq->plugin, - "Adding fee to amount"); - delay += h->delay; - - rh->scid = gossmap_chan_scid(rq->gossmap, - flows[i]->path[j]); - rh->direction = flows[i]->dirs[j]; - far_end = gossmap_nth_node(rq->gossmap, - flows[i]->path[j], - !flows[i]->dirs[j]); - gossmap_node_get_id(rq->gossmap, far_end, - &rh->node_id); - rh->amount = msat; - rh->delay = delay; - } - (*amounts)[i] = flows[i]->delivers; - } else { - /* Fill in backwards to calc delay */ - for (int j = tal_count(flows[i]->path) - 1; j >= 0; - j--) { - struct route_hop *rh = &r->hops[j]; - struct gossmap_node *far_end; - const struct half_chan *h = - flow_edge(flows[i], j); - - delay += h->delay; - - rh->scid = gossmap_chan_scid(rq->gossmap, - flows[i]->path[j]); - rh->direction = flows[i]->dirs[j]; - far_end = gossmap_nth_node(rq->gossmap, - flows[i]->path[j], - !flows[i]->dirs[j]); - gossmap_node_get_id(rq->gossmap, far_end, - &rh->node_id); - rh->delay = delay; - } - /* Compute fees forward */ - for (int j = 0; j < tal_count(flows[i]->path); j++) { - struct route_hop *rh = &r->hops[j]; - const struct half_chan *h = - flow_edge(flows[i], j); - - rh->amount = msat; - msat = amount_msat_sub_fee(msat, h->base_fee, - h->proportional_fee); - } - (*amounts)[i] = msat; - } - - rq_log(tmpctx, rq, LOG_INFORM, "Flow %zu/%zu: %s", - i, tal_count(flows), - fmt_route(tmpctx, r, (*amounts)[i], finalcltv)); - } - - return routes; -} - -static void json_add_getroutes(struct json_stream *js, - struct route **routes, - const struct amount_msat *amounts, - double probability, - u32 final_cltv) -{ - json_add_u64(js, "probability_ppm", (u64)(probability * 1000000)); - json_array_start(js, "routes"); - for (size_t i = 0; i < tal_count(routes); i++) { - json_object_start(js, NULL); - json_add_u64(js, "probability_ppm", - (u64)(routes[i]->success_prob * 1000000)); - json_add_amount_msat(js, "amount_msat", amounts[i]); - json_add_u32(js, "final_cltv", final_cltv); - json_array_start(js, "path"); - for (size_t j = 0; j < tal_count(routes[i]->hops); j++) { - struct short_channel_id_dir scidd; - const struct route_hop *r = &routes[i]->hops[j]; - json_object_start(js, NULL); - scidd.scid = r->scid; - scidd.dir = r->direction; - json_add_short_channel_id_dir( - js, "short_channel_id_dir", scidd); - json_add_node_id(js, "next_node_id", &r->node_id); - json_add_amount_msat(js, "amount_msat", r->amount); - json_add_u32(js, "delay", r->delay); - json_object_end(js); - } - json_array_end(js); - json_object_end(js); - } - json_array_end(js); -} - void get_constraints(const struct route_query *rq, const struct gossmap_chan *chan, int dir, @@ -602,111 +446,6 @@ void get_constraints(const struct route_query *rq, reserve_sub(rq->reserved, &scidd, rq->layers, max); } -/* Returns fd to child */ -static int fork_router_child(struct route_query *rq, - enum algorithm algo, - struct timemono deadline, - const struct gossmap_node *srcnode, - const struct gossmap_node *dstnode, - struct amount_msat amount, - struct amount_msat maxfee, - u32 finalcltv, u32 maxdelay, - bool include_fees, - const char *cmd_id, - struct json_filter *cmd_filter, - int *log_fd, - int *child_pid) -{ - int replyfds[2], logfds[2]; - double probability; - struct flow **flows; - struct route **routes; - struct amount_msat *amounts; - const char *err, *p; - size_t len; - - if (pipe(replyfds) != 0) - return -1; - if (pipe(logfds) != 0) { - close_noerr(replyfds[0]); - close_noerr(replyfds[1]); - return -1; - } - *child_pid = fork(); - if (*child_pid < 0) { - close_noerr(replyfds[0]); - close_noerr(replyfds[1]); - close_noerr(logfds[0]); - close_noerr(logfds[1]); - return -1; - } - if (*child_pid != 0) { - close(logfds[1]); - close(replyfds[1]); - *log_fd = logfds[0]; - return replyfds[0]; - } - - /* We are the child. Run the algo */ - close(logfds[0]); - close(replyfds[0]); - set_child_log_fd(logfds[1]); - am_child = true; - if (algo == ALGO_SINGLE_PATH) { - err = single_path_routes(rq, rq, deadline, srcnode, dstnode, - amount, maxfee, finalcltv, - maxdelay, &flows, &probability); - } else { - assert(algo == ALGO_DEFAULT); - err = default_routes(rq, rq, deadline, srcnode, dstnode, - amount, maxfee, finalcltv, maxdelay, - &flows, &probability); - } - if (err) { - write_all(replyfds[1], err, strlen(err)); - /* Non-zero exit tells parent this is an error string. */ - exit(1); - } - - /* otherwise we continue */ - assert(tal_count(flows) > 0); - rq_log(tmpctx, rq, LOG_DBG, "Final answer has %zu flows", - tal_count(flows)); - - /* convert flows to routes */ - routes = convert_flows_to_routes(rq, rq, finalcltv, flows, - &amounts, include_fees); - assert(tal_count(routes) == tal_count(flows)); - assert(tal_count(amounts) == tal_count(flows)); - - /* output the results */ - struct json_stream *js = new_json_stream(tmpctx, NULL, NULL); - json_object_start(js, NULL); - json_add_string(js, "jsonrpc", "2.0"); - json_add_id(js, cmd_id); - json_object_start(js, "result"); - if (cmd_filter) - json_stream_attach_filter(js, cmd_filter); - json_add_getroutes(js, routes, amounts, probability, finalcltv); - - /* Detach filter before it complains about closing object it never saw */ - if (cmd_filter) { - err = json_stream_detach_filter(tmpctx, js); - if (err) - json_add_string(js, "warning_parameter_filter", err); - } - /* "result" object */ - json_object_end(js); - /* Global object */ - json_object_end(js); - json_stream_close(js, NULL); - - p = json_out_contents(js->jout, &len); - if (!write_all(replyfds[1], p, len)) - abort(); - exit(0); -} - static void process_child_logs(struct route_query *rq, int log_fd) { u8 *msg; @@ -841,7 +580,7 @@ static struct command_result *do_getroutes(struct command *cmd, time_start = time_mono(); deadline = timemono_add(time_start, time_from_sec(askrene->route_seconds)); - child_fd = fork_router_child(rq, info->dev_algo, + child_fd = fork_router_child(rq, info->dev_algo == ALGO_SINGLE_PATH, deadline, srcnode, dstnode, info->amount, info->maxfee, info->finalcltv, info->maxdelay, include_fees, diff --git a/plugins/askrene/askrene.h b/plugins/askrene/askrene.h index c9b15e396..85acc6503 100644 --- a/plugins/askrene/askrene.h +++ b/plugins/askrene/askrene.h @@ -12,14 +12,6 @@ struct gossmap_chan; -/* A single route. */ -struct route { - /* Actual path to take */ - struct route_hop *hops; - /* Probability estimate (0-1) */ - double success_prob; -}; - /* Grab-bag of "globals" for this plugin */ struct askrene { struct plugin *plugin; diff --git a/plugins/askrene/child/entry.c b/plugins/askrene/child/entry.c new file mode 100644 index 000000000..246a4a51e --- /dev/null +++ b/plugins/askrene/child/entry.c @@ -0,0 +1,279 @@ +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Temporary hack */ +bool am_child = false; + +/* A single route. */ +struct route { + /* Actual path to take */ + struct route_hop *hops; + /* Probability estimate (0-1) */ + double success_prob; +}; + +static const char *fmt_route(const tal_t *ctx, + const struct route *route, + struct amount_msat delivers, + u32 final_cltv) +{ + char *str = tal_strdup(ctx, ""); + + for (size_t i = 0; i < tal_count(route->hops); i++) { + struct short_channel_id_dir scidd; + scidd.scid = route->hops[i].scid; + scidd.dir = route->hops[i].direction; + tal_append_fmt(&str, "%s/%u %s -> ", + fmt_amount_msat(tmpctx, route->hops[i].amount), + route->hops[i].delay, + fmt_short_channel_id_dir(tmpctx, &scidd)); + } + tal_append_fmt(&str, "%s/%u", + fmt_amount_msat(tmpctx, delivers), final_cltv); + return str; +} + +/* Convert back into routes, with delay and other information fixed */ +static struct route **convert_flows_to_routes(const tal_t *ctx, + struct route_query *rq, + u32 finalcltv, + struct flow **flows, + struct amount_msat **amounts, + bool include_fees) +{ + struct route **routes; + routes = tal_arr(ctx, struct route *, tal_count(flows)); + *amounts = tal_arr(ctx, struct amount_msat, tal_count(flows)); + + for (size_t i = 0; i < tal_count(flows); i++) { + struct route *r; + struct amount_msat msat; + u32 delay; + + routes[i] = r = tal(routes, struct route); + r->success_prob = flow_probability(flows[i], rq); + r->hops = tal_arr(r, struct route_hop, tal_count(flows[i]->path)); + + msat = flows[i]->delivers; + delay = finalcltv; + + if (!include_fees) { + /* Fill in backwards to calc amount and delay */ + for (int j = tal_count(flows[i]->path) - 1; j >= 0; + j--) { + struct route_hop *rh = &r->hops[j]; + struct gossmap_node *far_end; + const struct half_chan *h = + flow_edge(flows[i], j); + + if (!amount_msat_add_fee(&msat, h->base_fee, + h->proportional_fee)) + plugin_err(rq->plugin, + "Adding fee to amount"); + delay += h->delay; + + rh->scid = gossmap_chan_scid(rq->gossmap, + flows[i]->path[j]); + rh->direction = flows[i]->dirs[j]; + far_end = gossmap_nth_node(rq->gossmap, + flows[i]->path[j], + !flows[i]->dirs[j]); + gossmap_node_get_id(rq->gossmap, far_end, + &rh->node_id); + rh->amount = msat; + rh->delay = delay; + } + (*amounts)[i] = flows[i]->delivers; + } else { + /* Fill in backwards to calc delay */ + for (int j = tal_count(flows[i]->path) - 1; j >= 0; + j--) { + struct route_hop *rh = &r->hops[j]; + struct gossmap_node *far_end; + const struct half_chan *h = + flow_edge(flows[i], j); + + delay += h->delay; + + rh->scid = gossmap_chan_scid(rq->gossmap, + flows[i]->path[j]); + rh->direction = flows[i]->dirs[j]; + far_end = gossmap_nth_node(rq->gossmap, + flows[i]->path[j], + !flows[i]->dirs[j]); + gossmap_node_get_id(rq->gossmap, far_end, + &rh->node_id); + rh->delay = delay; + } + /* Compute fees forward */ + for (int j = 0; j < tal_count(flows[i]->path); j++) { + struct route_hop *rh = &r->hops[j]; + const struct half_chan *h = + flow_edge(flows[i], j); + + rh->amount = msat; + msat = amount_msat_sub_fee(msat, h->base_fee, + h->proportional_fee); + } + (*amounts)[i] = msat; + } + + child_log(tmpctx, LOG_INFORM, "Flow %zu/%zu: %s", + i, tal_count(flows), + fmt_route(tmpctx, r, (*amounts)[i], finalcltv)); + } + + return routes; +} + +static void json_add_getroutes(struct json_stream *js, + struct route **routes, + const struct amount_msat *amounts, + double probability, + u32 final_cltv) +{ + json_add_u64(js, "probability_ppm", (u64)(probability * 1000000)); + json_array_start(js, "routes"); + for (size_t i = 0; i < tal_count(routes); i++) { + json_object_start(js, NULL); + json_add_u64(js, "probability_ppm", + (u64)(routes[i]->success_prob * 1000000)); + json_add_amount_msat(js, "amount_msat", amounts[i]); + json_add_u32(js, "final_cltv", final_cltv); + json_array_start(js, "path"); + for (size_t j = 0; j < tal_count(routes[i]->hops); j++) { + struct short_channel_id_dir scidd; + const struct route_hop *r = &routes[i]->hops[j]; + json_object_start(js, NULL); + scidd.scid = r->scid; + scidd.dir = r->direction; + json_add_short_channel_id_dir( + js, "short_channel_id_dir", scidd); + json_add_node_id(js, "next_node_id", &r->node_id); + json_add_amount_msat(js, "amount_msat", r->amount); + json_add_u32(js, "delay", r->delay); + json_object_end(js); + } + json_array_end(js); + json_object_end(js); + } + json_array_end(js); +} + + +/* Returns fd to child */ +int fork_router_child(struct route_query *rq, + bool single_path, + struct timemono deadline, + const struct gossmap_node *srcnode, + const struct gossmap_node *dstnode, + struct amount_msat amount, struct amount_msat maxfee, + u32 finalcltv, u32 maxdelay, + bool include_fees, + const char *cmd_id, + struct json_filter *cmd_filter, + int *log_fd, + int *child_pid) +{ + int replyfds[2], logfds[2]; + double probability; + struct flow **flows; + struct route **routes; + struct amount_msat *amounts; + const char *err, *p; + size_t len; + + if (pipe(replyfds) != 0) + return -1; + if (pipe(logfds) != 0) { + close_noerr(replyfds[0]); + close_noerr(replyfds[1]); + return -1; + } + *child_pid = fork(); + if (*child_pid < 0) { + close_noerr(replyfds[0]); + close_noerr(replyfds[1]); + close_noerr(logfds[0]); + close_noerr(logfds[1]); + return -1; + } + if (*child_pid != 0) { + close(logfds[1]); + close(replyfds[1]); + *log_fd = logfds[0]; + return replyfds[0]; + } + + /* We are the child. Run the algo */ + close(logfds[0]); + close(replyfds[0]); + set_child_log_fd(logfds[1]); + am_child = true; + if (single_path) { + err = single_path_routes(rq, rq, deadline, srcnode, dstnode, + amount, maxfee, finalcltv, + maxdelay, &flows, &probability); + } else { + err = default_routes(rq, rq, deadline, srcnode, dstnode, + amount, maxfee, finalcltv, maxdelay, + &flows, &probability); + } + if (err) { + write_all(replyfds[1], err, strlen(err)); + /* Non-zero exit tells parent this is an error string. */ + exit(1); + } + + /* otherwise we continue */ + assert(tal_count(flows) > 0); + child_log(tmpctx, LOG_DBG, "Final answer has %zu flows", + tal_count(flows)); + + /* convert flows to routes */ + routes = convert_flows_to_routes(rq, rq, finalcltv, flows, + &amounts, include_fees); + assert(tal_count(routes) == tal_count(flows)); + assert(tal_count(amounts) == tal_count(flows)); + + /* output the results */ + struct json_stream *js = new_json_stream(tmpctx, NULL, NULL); + json_object_start(js, NULL); + json_add_string(js, "jsonrpc", "2.0"); + json_add_id(js, cmd_id); + json_object_start(js, "result"); + if (cmd_filter) + json_stream_attach_filter(js, cmd_filter); + json_add_getroutes(js, routes, amounts, probability, finalcltv); + + /* Detach filter before it complains about closing object it never saw */ + if (cmd_filter) { + err = json_stream_detach_filter(tmpctx, js); + if (err) + json_add_string(js, "warning_parameter_filter", err); + } + /* "result" object */ + json_object_end(js); + /* Global object */ + json_object_end(js); + json_stream_close(js, NULL); + + p = json_out_contents(js->jout, &len); + if (!write_all(replyfds[1], p, len)) + abort(); + exit(0); +} + diff --git a/plugins/askrene/child/entry.h b/plugins/askrene/child/entry.h new file mode 100644 index 000000000..2ea790a49 --- /dev/null +++ b/plugins/askrene/child/entry.h @@ -0,0 +1,29 @@ +#ifndef LIGHTNING_PLUGINS_ASKRENE_CHILD_ENTRY_H +#define LIGHTNING_PLUGINS_ASKRENE_CHILD_ENTRY_H +#include "config.h" +#include +#include +#include +#include + +struct route_query; +struct gossmap_node; +struct json_filter; + +/* Entry point to the child process. */ +int fork_router_child(struct route_query *rq, + bool single_path, + struct timemono deadline, + const struct gossmap_node *srcnode, + const struct gossmap_node *dstnode, + struct amount_msat amount, struct amount_msat maxfee, + u32 finalcltv, u32 maxdelay, + bool include_fees, + const char *cmd_id, + struct json_filter *cmd_filter, + int *log_fd, + int *child_pid); + +/* FIXME: Remove this */ +extern bool am_child; +#endif /* LIGHTNING_PLUGINS_ASKRENE_CHILD_ENTRY_H */