askrene: refine: expose some of the refine API

that can be useful for us in the mcf.c main loop.

Changelog-None

Signed-off-by: Lagrang3 <lagrang3@protonmail.com>
This commit is contained in:
Lagrang3
2025-07-09 19:19:52 +01:00
committed by Rusty Russell
parent b479963c09
commit 3a0d0da5ce
2 changed files with 150 additions and 17 deletions

View File

@@ -6,6 +6,7 @@
#include <plugins/askrene/flow.h>
#include <plugins/askrene/refine.h>
#include <plugins/askrene/reserve.h>
#include <string.h>
/* Channel data for fast retrieval. */
struct channel_data {
@@ -35,8 +36,8 @@ static void destroy_reservations(struct reserve_hop *rhops, struct askrene *askr
reserve_remove(askrene->reserved, &rhops[i]);
}
static struct reserve_hop *new_reservations(const tal_t *ctx,
const struct route_query *rq)
struct reserve_hop *new_reservations(const tal_t *ctx,
const struct route_query *rq)
{
struct reserve_hop *rhops = tal_arr(ctx, struct reserve_hop, 0);
@@ -108,9 +109,9 @@ static void subtract_reservation(struct reserve_hop **reservations,
reserve_add(askrene->reserved, prev, rq->cmd->id);
}
static void create_flow_reservations(const struct route_query *rq,
struct reserve_hop **reservations,
const struct flow *flow)
void create_flow_reservations(const struct route_query *rq,
struct reserve_hop **reservations,
const struct flow *flow)
{
struct amount_msat msat;
@@ -170,6 +171,32 @@ static void change_flow_delivers(const struct route_query *rq,
create_flow_reservations(rq, reservations, flow);
}
bool create_flow_reservations_verify(const struct route_query *rq,
struct reserve_hop **reservations,
const struct flow *flow)
{
struct amount_msat msat;
msat = flow->delivers;
for (int i = tal_count(flow->path) - 1; i >= 0; i--) {
struct amount_msat known_min, known_max;
const struct half_chan *h = flow_edge(flow, i);
struct amount_msat amount_to_reserve = msat;
struct short_channel_id_dir scidd;
get_scidd(rq->gossmap, flow, i, &scidd);
get_constraints(rq, flow->path[i], flow->dirs[i], &known_min,
&known_max);
if (amount_msat_greater(amount_to_reserve, known_max))
return false;
if (!amount_msat_add_fee(&msat, h->base_fee,
h->proportional_fee))
abort();
}
create_flow_reservations(rq, reservations, flow);
return true;
}
/* We use an fp16_t approximatin for htlc_max/min: this gets the exact value. */
static struct amount_msat get_chan_htlc_max(const struct route_query *rq,
const struct gossmap_chan *c,
@@ -846,6 +873,24 @@ static struct amount_msat remove_excess(struct flow **flows,
return all_deliver;
}
static void write_selected_flows(const tal_t *ctx, size_t *flows_index,
struct flow ***flows)
{
struct flow **tmp_flows = tal_arr(ctx, struct flow *, 0);
for (size_t i = 0; i < tal_count(flows_index); i++) {
tal_arr_expand(&tmp_flows, (*flows)[flows_index[i]]);
(*flows)[flows_index[i]] = NULL;
}
for (size_t i = 0; i < tal_count(*flows); i++) {
(*flows)[i] = tal_free((*flows)[i]);
}
tal_resize(flows, 0);
for (size_t i = 0; i < tal_count(tmp_flows); i++) {
tal_arr_expand(flows, tmp_flows[i]);
}
tal_free(tmp_flows);
}
/* FIXME: on failure return error message */
const char *refine_flows(const tal_t *ctx, struct route_query *rq,
struct amount_msat deliver, struct flow ***flows)
@@ -911,18 +956,7 @@ const char *refine_flows(const tal_t *ctx, struct route_query *rq,
}
/* finally write the remaining flows */
struct flow **tmp_flows = tal_arr(working_ctx, struct flow *, 0);
for (size_t i = 0; i < tal_count(flows_index); i++) {
tal_arr_expand(&tmp_flows, (*flows)[flows_index[i]]);
(*flows)[flows_index[i]] = NULL;
}
for (size_t i = 0; i < tal_count(*flows); i++) {
(*flows)[i] = tal_free((*flows)[i]);
}
tal_resize(flows, 0);
for (size_t i = 0; i < tal_count(tmp_flows); i++) {
tal_arr_expand(flows, tmp_flows[i]);
}
write_selected_flows(working_ctx, flows_index, flows);
tal_free(working_ctx);
return NULL;
@@ -931,3 +965,80 @@ fail:
tal_free(working_ctx);
return error_message;
}
/* Order of flows according to path string */
static int cmppath_flows(const size_t *a, const size_t *b, char **paths_str)
{
return strcmp(paths_str[*a], paths_str[*b]);
}
void squash_flows(const tal_t *ctx, struct route_query *rq,
struct flow ***flows)
{
const tal_t *working_ctx = tal(ctx, tal_t);
size_t *flows_index = tal_arrz(working_ctx, size_t, tal_count(*flows));
char **paths_str = tal_arrz(working_ctx, char *, tal_count(*flows));
struct channel_data **channel_mpp_cache =
new_channel_mpp_cache(working_ctx, rq, *flows);
struct amount_msat *max_deliverable = tal_arrz(
working_ctx, struct amount_msat, tal_count(channel_mpp_cache));
for (size_t i = 0; i < tal_count(flows_index); i++) {
struct flow *flow = (*flows)[i];
struct short_channel_id_dir scidd;
flows_index[i] = i;
paths_str[i] = tal_strdup(working_ctx, "");
max_deliverable[i] = path_max_deliverable(channel_mpp_cache[i]);
for (size_t j = 0; j < tal_count(flow->path); j++) {
scidd.scid =
gossmap_chan_scid(rq->gossmap, flow->path[j]);
scidd.dir = flow->dirs[j];
tal_append_fmt(
&paths_str[i], "%s%s", j > 0 ? "->" : "",
fmt_short_channel_id_dir(working_ctx, &scidd));
}
}
asort(flows_index, tal_count(flows_index), cmppath_flows, paths_str);
for (size_t i = 0; i < tal_count(flows_index); i++) {
const size_t j = i + 1;
struct amount_msat combined;
struct amount_msat max = max_deliverable[flows_index[i]];
/* same path? We merge */
while (j < tal_count(flows_index) &&
cmppath_flows(&flows_index[i],
&flows_index[j],
paths_str) == 0) {
if (!amount_msat_add(
&combined, (*flows)[flows_index[i]]->delivers,
(*flows)[flows_index[j]]->delivers))
abort();
/* do we break any HTLC max limits */
if (amount_msat_greater(combined, max))
break;
(*flows)[flows_index[i]]->delivers = combined;
tal_arr_remove(&flows_index, j);
}
}
write_selected_flows(working_ctx, flows_index, flows);
tal_free(working_ctx);
}
double flows_probability(const tal_t *ctx, struct route_query *rq,
struct flow ***flows)
{
const tal_t *working_ctx = tal(ctx, tal_t);
struct reserve_hop *reservations = new_reservations(working_ctx, rq);
double probability = 1.0;
for (size_t i = 0; i < tal_count(*flows); i++) {
probability *= flow_probability((*flows)[i], rq);
create_flow_reservations(rq, &reservations, (*flows)[i]);
}
tal_free(working_ctx);
return probability;
}

View File

@@ -7,6 +7,13 @@ struct route_query;
struct amount_msat;
struct flow;
struct reserve_hop *new_reservations(const tal_t *ctx,
const struct route_query *rq);
void create_flow_reservations(const struct route_query *rq,
struct reserve_hop **reservations,
const struct flow *flow);
/* We got an answer from min-cost-flow, but we now need to:
* 1. Add fixup exact delivery amounts since MCF deals in larger granularity than msat.
* 2. Add fees which accumulate through the route.
@@ -24,8 +31,23 @@ refine_with_fees_and_limits(const tal_t *ctx,
struct flow ***flows,
double *flowset_probability);
/* create flow reservations, but first verify that the flow indeeds fits in the
* liquidity constraints. Takes into account reservations that include per HTLC
* extra amounts to pay for onchain fees. */
bool create_flow_reservations_verify(const struct route_query *rq,
struct reserve_hop **reservations,
const struct flow *flow);
/* Modify flows to meet HTLC min/max requirements.
* It takes into account the exact value of the fees expected at each hop. */
const char *refine_flows(const tal_t *ctx, struct route_query *rq,
struct amount_msat deliver, struct flow ***flows);
/* Duplicated flows are merged into one. This saves in base fee and HTLC fees.
*/
void squash_flows(const tal_t *ctx, struct route_query *rq,
struct flow ***flows);
double flows_probability(const tal_t *ctx, struct route_query *rq,
struct flow ***flows);
#endif /* LIGHTNING_PLUGINS_ASKRENE_REFINE_H */