Skip to content

Commit 0581f8e

Browse files
committed
askrene: refactor get_routes ...
Move the feature tuning algorithm to mcf.c, ie. the loops for searching a good mu and delay_feefactor to satisfy the problem constraints. We are looking to set the stage for an execution logic that allows for multiple choices of routing algorithms, mainly for experimenting without breaking the default working code. Changelog-None Signed-off-by: Lagrang3 <[email protected]>
1 parent 4a840c2 commit 0581f8e

File tree

3 files changed

+172
-121
lines changed

3 files changed

+172
-121
lines changed

plugins/askrene/askrene.c

+7-121
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,9 @@
1919
#include <errno.h>
2020
#include <math.h>
2121
#include <plugins/askrene/askrene.h>
22-
#include <plugins/askrene/explain_failure.h>
2322
#include <plugins/askrene/flow.h>
2423
#include <plugins/askrene/layer.h>
2524
#include <plugins/askrene/mcf.h>
26-
#include <plugins/askrene/refine.h>
2725
#include <plugins/askrene/reserve.h>
2826

2927
/* "spendable" for a channel assumes a single HTLC: for additional HTLCs,
@@ -333,22 +331,6 @@ const char *fmt_flow_full(const tal_t *ctx,
333331
return str;
334332
}
335333

336-
static struct amount_msat linear_flows_cost(struct flow **flows,
337-
struct amount_msat total_amount,
338-
double delay_feefactor)
339-
{
340-
struct amount_msat total = AMOUNT_MSAT(0);
341-
342-
for (size_t i = 0; i < tal_count(flows); i++) {
343-
if (!amount_msat_accumulate(&total,
344-
linear_flow_cost(flows[i],
345-
total_amount,
346-
delay_feefactor)))
347-
abort();
348-
}
349-
return total;
350-
}
351-
352334
/* Returns an error message, or sets *routes */
353335
static const char *get_routes(const tal_t *ctx,
354336
struct command *cmd,
@@ -371,8 +353,6 @@ static const char *get_routes(const tal_t *ctx,
371353
struct route_query *rq = tal(ctx, struct route_query);
372354
struct flow **flows;
373355
const struct gossmap_node *srcnode, *dstnode;
374-
double delay_feefactor;
375-
u32 mu;
376356
const char *ret;
377357
struct timerel time_delta;
378358
struct timemono time_start = time_mono();
@@ -446,109 +426,15 @@ static const char *get_routes(const tal_t *ctx,
446426
goto fail;
447427
}
448428

449-
delay_feefactor = 1.0/1000000;
450-
451-
/* First up, don't care about fees (well, just enough to tiebreak!) */
452-
mu = 1;
453-
flows = minflow(rq, rq, srcnode, dstnode, amount,
454-
mu, delay_feefactor, single_path);
455-
if (!flows) {
456-
ret = explain_failure(ctx, rq, srcnode, dstnode, amount);
457-
goto fail;
458-
}
459-
460-
/* Too much delay? */
461-
while (finalcltv + flows_worst_delay(flows) > maxdelay) {
462-
delay_feefactor *= 2;
463-
rq_log(tmpctx, rq, LOG_UNUSUAL,
464-
"The worst flow delay is %"PRIu64" (> %i), retrying with delay_feefactor %f...",
465-
flows_worst_delay(flows), maxdelay - finalcltv, delay_feefactor);
466-
flows = minflow(rq, rq, srcnode, dstnode, amount,
467-
mu, delay_feefactor, single_path);
468-
if (!flows || delay_feefactor > 10) {
469-
ret = rq_log(ctx, rq, LOG_UNUSUAL,
470-
"Could not find route without excessive delays");
471-
goto fail;
472-
}
473-
}
474-
475-
/* Too expensive? */
476-
too_expensive:
477-
while (amount_msat_greater(flowset_fee(rq->plugin, flows), maxfee)) {
478-
struct flow **new_flows;
479-
480-
if (mu == 1)
481-
mu = 10;
482-
else
483-
mu += 10;
484-
rq_log(tmpctx, rq, LOG_UNUSUAL,
485-
"The flows had a fee of %s, greater than max of %s, retrying with mu of %u%%...",
486-
fmt_amount_msat(tmpctx, flowset_fee(rq->plugin, flows)),
487-
fmt_amount_msat(tmpctx, maxfee),
488-
mu);
489-
new_flows = minflow(rq, rq, srcnode, dstnode, amount,
490-
mu > 100 ? 100 : mu, delay_feefactor, single_path);
491-
if (!flows || mu >= 100) {
492-
ret = rq_log(ctx, rq, LOG_UNUSUAL,
493-
"Could not find route without excessive cost");
494-
goto fail;
495-
}
496-
497-
/* This is possible, because MCF's linear fees are not the same. */
498-
if (amount_msat_greater(flowset_fee(rq->plugin, new_flows),
499-
flowset_fee(rq->plugin, flows))) {
500-
struct amount_msat old_cost = linear_flows_cost(flows, amount, delay_feefactor);
501-
struct amount_msat new_cost = linear_flows_cost(new_flows, amount, delay_feefactor);
502-
if (amount_msat_greater_eq(new_cost, old_cost)) {
503-
rq_log(tmpctx, rq, LOG_BROKEN, "Old flows cost %s:",
504-
fmt_amount_msat(tmpctx, old_cost));
505-
for (size_t i = 0; i < tal_count(flows); i++) {
506-
rq_log(tmpctx, rq, LOG_BROKEN,
507-
"Flow %zu/%zu: %s (linear cost %s)", i, tal_count(flows),
508-
fmt_flow_full(tmpctx, rq, flows[i]),
509-
fmt_amount_msat(tmpctx, linear_flow_cost(flows[i],
510-
amount,
511-
delay_feefactor)));
512-
}
513-
rq_log(tmpctx, rq, LOG_BROKEN, "Old flows cost %s:",
514-
fmt_amount_msat(tmpctx, new_cost));
515-
for (size_t i = 0; i < tal_count(new_flows); i++) {
516-
rq_log(tmpctx, rq, LOG_BROKEN,
517-
"Flow %zu/%zu: %s (linear cost %s)", i, tal_count(new_flows),
518-
fmt_flow_full(tmpctx, rq, new_flows[i]),
519-
fmt_amount_msat(tmpctx, linear_flow_cost(new_flows[i],
520-
amount,
521-
delay_feefactor)));
522-
}
523-
}
524-
}
525-
tal_free(flows);
526-
flows = new_flows;
527-
}
528-
529-
if (finalcltv + flows_worst_delay(flows) > maxdelay) {
530-
ret = rq_log(ctx, rq, LOG_UNUSUAL,
531-
"Could not find route without excessive cost or delays");
532-
goto fail;
533-
}
534-
535-
/* The above did not take into account the extra funds to pay
536-
* fees, so we try to adjust now. We could re-run MCF if this
537-
* fails, but failure basically never happens where payment is
538-
* still possible */
539-
ret = refine_with_fees_and_limits(ctx, rq, amount, &flows, probability);
540-
if (ret)
429+
/* FIXME: single_path should signal a change in algorithm. */
430+
ret = default_routes(rq, rq, srcnode, dstnode, amount, single_path,
431+
maxfee, finalcltv, maxdelay, &flows, probability);
432+
if (ret) {
541433
goto fail;
542-
543-
/* Again, a tiny corner case: refine step can make us exceed maxfee */
544-
if (amount_msat_greater(flowset_fee(rq->plugin, flows), maxfee)) {
545-
rq_log(tmpctx, rq, LOG_UNUSUAL,
546-
"After final refinement, fee was excessive: retrying");
547-
goto too_expensive;
548434
}
549-
550-
rq_log(tmpctx, rq, LOG_DBG, "Final answer has %zu flows with mu=%u",
551-
tal_count(flows), mu);
435+
assert(tal_count(flows) > 0);
436+
rq_log(tmpctx, rq, LOG_DBG, "Final answer has %zu flows",
437+
tal_count(flows));
552438

553439
/* Convert back into routes, with delay and other information fixed */
554440
*routes = tal_arr(ctx, struct route *, tal_count(flows));

plugins/askrene/mcf.c

+155
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,11 @@
1111
#include <plugins/askrene/algorithm.h>
1212
#include <plugins/askrene/askrene.h>
1313
#include <plugins/askrene/dijkstra.h>
14+
#include <plugins/askrene/explain_failure.h>
1415
#include <plugins/askrene/flow.h>
1516
#include <plugins/askrene/graph.h>
1617
#include <plugins/askrene/mcf.h>
18+
#include <plugins/askrene/refine.h>
1719
#include <plugins/libplugin.h>
1820
#include <stdint.h>
1921

@@ -1080,3 +1082,156 @@ struct flow **minflow(const tal_t *ctx,
10801082
tal_free(working_ctx);
10811083
return NULL;
10821084
}
1085+
1086+
static struct amount_msat linear_flows_cost(struct flow **flows,
1087+
struct amount_msat total_amount,
1088+
double delay_feefactor)
1089+
{
1090+
struct amount_msat total = AMOUNT_MSAT(0);
1091+
1092+
for (size_t i = 0; i < tal_count(flows); i++) {
1093+
if (!amount_msat_accumulate(&total,
1094+
linear_flow_cost(flows[i],
1095+
total_amount,
1096+
delay_feefactor)))
1097+
abort();
1098+
}
1099+
return total;
1100+
}
1101+
1102+
1103+
const char *default_routes(const tal_t *ctx, struct route_query *rq,
1104+
const struct gossmap_node *srcnode,
1105+
const struct gossmap_node *dstnode,
1106+
struct amount_msat amount, bool single_path,
1107+
struct amount_msat maxfee, u32 finalcltv,
1108+
u32 maxdelay, struct flow ***flows,
1109+
double *probability)
1110+
{
1111+
const char *ret;
1112+
double delay_feefactor = 1.0 / 1000000;
1113+
1114+
/* First up, don't care about fees (well, just enough to tiebreak!) */
1115+
u32 mu = 1;
1116+
*flows = minflow(ctx, rq, srcnode, dstnode, amount, mu, delay_feefactor,
1117+
single_path);
1118+
if (!*flows) {
1119+
ret = explain_failure(ctx, rq, srcnode, dstnode, amount);
1120+
goto fail;
1121+
}
1122+
1123+
/* Too much delay? */
1124+
while (finalcltv + flows_worst_delay(*flows) > maxdelay) {
1125+
delay_feefactor *= 2;
1126+
rq_log(tmpctx, rq, LOG_UNUSUAL,
1127+
"The worst flow delay is %" PRIu64
1128+
" (> %i), retrying with delay_feefactor %f...",
1129+
flows_worst_delay(*flows), maxdelay - finalcltv,
1130+
delay_feefactor);
1131+
*flows = minflow(ctx, rq, srcnode, dstnode, amount, mu,
1132+
delay_feefactor, single_path);
1133+
if (!*flows || delay_feefactor > 10) {
1134+
ret = rq_log(
1135+
ctx, rq, LOG_UNUSUAL,
1136+
"Could not find route without excessive delays");
1137+
goto fail;
1138+
}
1139+
}
1140+
1141+
/* Too expensive? */
1142+
too_expensive:
1143+
while (amount_msat_greater(flowset_fee(rq->plugin, *flows), maxfee)) {
1144+
struct flow **new_flows;
1145+
1146+
if (mu == 1)
1147+
mu = 10;
1148+
else
1149+
mu += 10;
1150+
rq_log(tmpctx, rq, LOG_UNUSUAL,
1151+
"The flows had a fee of %s, greater than max of %s, "
1152+
"retrying with mu of %u%%...",
1153+
fmt_amount_msat(tmpctx, flowset_fee(rq->plugin, *flows)),
1154+
fmt_amount_msat(tmpctx, maxfee), mu);
1155+
new_flows =
1156+
minflow(ctx, rq, srcnode, dstnode, amount,
1157+
mu > 100 ? 100 : mu, delay_feefactor, single_path);
1158+
if (!*flows || mu >= 100) {
1159+
ret = rq_log(
1160+
ctx, rq, LOG_UNUSUAL,
1161+
"Could not find route without excessive cost");
1162+
goto fail;
1163+
}
1164+
1165+
/* This is possible, because MCF's linear fees are not the same.
1166+
*/
1167+
if (amount_msat_greater(flowset_fee(rq->plugin, new_flows),
1168+
flowset_fee(rq->plugin, *flows))) {
1169+
struct amount_msat old_cost =
1170+
linear_flows_cost(*flows, amount, delay_feefactor);
1171+
struct amount_msat new_cost = linear_flows_cost(
1172+
new_flows, amount, delay_feefactor);
1173+
if (amount_msat_greater_eq(new_cost, old_cost)) {
1174+
rq_log(tmpctx, rq, LOG_BROKEN,
1175+
"Old flows cost %s:",
1176+
fmt_amount_msat(tmpctx, old_cost));
1177+
for (size_t i = 0; i < tal_count(*flows); i++) {
1178+
rq_log(
1179+
tmpctx, rq, LOG_BROKEN,
1180+
"Flow %zu/%zu: %s (linear cost %s)",
1181+
i, tal_count(*flows),
1182+
fmt_flow_full(tmpctx, rq, *flows[i]),
1183+
fmt_amount_msat(
1184+
tmpctx, linear_flow_cost(
1185+
*flows[i], amount,
1186+
delay_feefactor)));
1187+
}
1188+
rq_log(tmpctx, rq, LOG_BROKEN,
1189+
"Old flows cost %s:",
1190+
fmt_amount_msat(tmpctx, new_cost));
1191+
for (size_t i = 0; i < tal_count(new_flows);
1192+
i++) {
1193+
rq_log(
1194+
tmpctx, rq, LOG_BROKEN,
1195+
"Flow %zu/%zu: %s (linear cost %s)",
1196+
i, tal_count(new_flows),
1197+
fmt_flow_full(tmpctx, rq,
1198+
new_flows[i]),
1199+
fmt_amount_msat(
1200+
tmpctx,
1201+
linear_flow_cost(
1202+
new_flows[i], amount,
1203+
delay_feefactor)));
1204+
}
1205+
}
1206+
}
1207+
tal_free(*flows);
1208+
*flows = new_flows;
1209+
}
1210+
1211+
if (finalcltv + flows_worst_delay(*flows) > maxdelay) {
1212+
ret = rq_log(
1213+
ctx, rq, LOG_UNUSUAL,
1214+
"Could not find route without excessive cost or delays");
1215+
goto fail;
1216+
}
1217+
1218+
/* The above did not take into account the extra funds to pay
1219+
* fees, so we try to adjust now. We could re-run MCF if this
1220+
* fails, but failure basically never happens where payment is
1221+
* still possible */
1222+
ret = refine_with_fees_and_limits(ctx, rq, amount, flows, probability);
1223+
if (ret)
1224+
goto fail;
1225+
1226+
/* Again, a tiny corner case: refine step can make us exceed maxfee */
1227+
if (amount_msat_greater(flowset_fee(rq->plugin, *flows), maxfee)) {
1228+
rq_log(tmpctx, rq, LOG_UNUSUAL,
1229+
"After final refinement, fee was excessive: retrying");
1230+
goto too_expensive;
1231+
}
1232+
1233+
return NULL;
1234+
fail:
1235+
assert(ret != NULL);
1236+
return ret;
1237+
}

plugins/askrene/mcf.h

+10
Original file line numberDiff line numberDiff line change
@@ -40,4 +40,14 @@ struct amount_msat linear_flow_cost(const struct flow *flow,
4040
struct amount_msat total_amount,
4141
double delay_feefactor);
4242

43+
/* A wrapper to the min. cost flow solver that actually takes into consideration
44+
* the extra msats per channel needed to pay for fees. */
45+
const char *default_routes(const tal_t *ctx, struct route_query *rq,
46+
const struct gossmap_node *srcnode,
47+
const struct gossmap_node *dstnode,
48+
struct amount_msat amount, bool single_path,
49+
struct amount_msat maxfee, u32 finalcltv,
50+
u32 maxdelay, struct flow ***flows,
51+
double *probability);
52+
4353
#endif /* LIGHTNING_PLUGINS_ASKRENE_MCF_H */

0 commit comments

Comments
 (0)