Skip to content

Commit 4fdd156

Browse files
committed
askrene: add a base probability cost
Add a base probability cost to simulate random channel outage and penalize for long paths or many routes solutions. Changelog-None Signed-off-by: Lagrang3 <[email protected]>
1 parent 9f75c99 commit 4fdd156

File tree

2 files changed

+50
-14
lines changed

2 files changed

+50
-14
lines changed

plugins/askrene/mcf.c

Lines changed: 46 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,7 @@ static const s64 MU_MAX = 100;
171171

172172
/* every payment under 1000sat will be routed through a single path */
173173
static const struct amount_msat SINGLE_PATH_THRESHOLD = AMOUNT_MSAT(1000000);
174+
static const double DEFAULT_BASE_PROBABILITY = 0.98;
174175

175176
/* Let's try this encoding of arcs:
176177
* Each channel `c` has two possible directions identified by a bit
@@ -300,6 +301,13 @@ struct pay_parameters {
300301

301302
double delay_feefactor;
302303
double base_fee_penalty;
304+
/* base_prob is understood as global probability for channel
305+
* availability, ie. a channel chosen at random will be able to forward
306+
* 1msat with a probability given by this value. We don't know the
307+
* actual value for this quantity but we can still use it to penalize
308+
* the use of many channels per payment, ie. reducing this number we
309+
* tend to obtaine less routes and/or shorter paths. */
310+
double base_prob;
303311
};
304312

305313
/* Helper function.
@@ -359,6 +367,7 @@ static void linearize_channel(const struct pay_parameters *params,
359367
const struct gossmap_chan *c, const int dir,
360368
s64 *capacity, double *cost)
361369
{
370+
const double base_prob_cost = -log(params->base_prob);
362371
struct amount_msat mincap, maxcap;
363372

364373
/* This takes into account any payments in progress. */
@@ -391,9 +400,23 @@ static void linearize_channel(const struct pay_parameters *params,
391400
{
392401
set_capacity(&capacity[i], params->cap_fraction[i]*(b-a), &cap_on_capacity);
393402

394-
cost[i] = params->cost_fraction[i] * 1000
395-
* amount_msat_ratio(params->amount, params->accuracy)
396-
/ (b - a);
403+
/* It is conceptually wrong to add a base cost to every arc,
404+
* that would mean we might pay several times for the same base
405+
* cost. The linearization of the base cost is another corner
406+
* that needs improvement.
407+
* For a cost function of the form
408+
* C(x) = x*k1 + k0
409+
* we produce a linear variation of the form
410+
* C_lin(x) = x*(k1 + k0/T)
411+
* where T is the entire payment amount.
412+
* Since we also scale up the probability cost function by
413+
* 1000*T the resulting linear approximation becomes:
414+
* C_lin(x) = x*1000*(k1*T + k0)
415+
**/
416+
cost[i] = 1000 * (params->cost_fraction[i] / (b - a) *
417+
amount_msat_ratio(params->amount,
418+
params->accuracy) +
419+
base_prob_cost);
397420
}
398421
}
399422

@@ -970,7 +993,8 @@ struct flow **minflow(const tal_t *ctx,
970993
const struct gossmap_node *target,
971994
struct amount_msat amount,
972995
u32 mu,
973-
double delay_feefactor)
996+
double delay_feefactor,
997+
double base_prob)
974998
{
975999
struct flow **flow_paths;
9761000
/* We allocate everything off this, and free it at the end,
@@ -991,6 +1015,7 @@ struct flow **minflow(const tal_t *ctx,
9911015
* */
9921016
params->accuracy =
9931017
amount_msat_max(AMOUNT_MSAT(1), amount_msat_div_ceil(amount, 1000));
1018+
params->base_prob = base_prob;
9941019

9951020
// template the channel partition into linear arcs
9961021
params->cap_fraction[0]=0;
@@ -1149,7 +1174,8 @@ static void init_linear_network_single_path(
11491174
(*arc_capacity)[arc.idx] = 1;
11501175
(*arc_prob_cost)[arc.idx] =
11511176
(-1.0) * log(pickhardt_richter_probability(
1152-
mincap, maxcap, params->amount));
1177+
mincap, maxcap, params->amount) *
1178+
params->base_prob);
11531179

11541180
struct amount_msat fee;
11551181
if (!amount_msat_fee(&fee, params->amount,
@@ -1171,7 +1197,8 @@ struct flow **single_path_flow(const tal_t *ctx, const struct route_query *rq,
11711197
const struct gossmap_node *source,
11721198
const struct gossmap_node *target,
11731199
struct amount_msat amount, u32 mu,
1174-
double delay_feefactor)
1200+
double delay_feefactor,
1201+
double base_prob)
11751202
{
11761203
struct flow **flow_paths;
11771204
/* We allocate everything off this, and free it at the end,
@@ -1188,6 +1215,7 @@ struct flow **single_path_flow(const tal_t *ctx, const struct route_query *rq,
11881215
params->accuracy = amount;
11891216
params->delay_feefactor = delay_feefactor;
11901217
params->base_fee_penalty = base_fee_penalty_estimate(amount);
1218+
params->base_prob = base_prob;
11911219

11921220
struct graph *graph;
11931221
double *arc_prob_cost;
@@ -1352,7 +1380,7 @@ linear_routes(const tal_t *ctx, struct route_query *rq,
13521380
struct flow **(*solver)(const tal_t *, const struct route_query *,
13531381
const struct gossmap_node *,
13541382
const struct gossmap_node *,
1355-
struct amount_msat, u32, double))
1383+
struct amount_msat, u32, double, double))
13561384
{
13571385
const tal_t *working_ctx = tal(ctx, tal_t);
13581386
const char *error_message;
@@ -1373,6 +1401,7 @@ linear_routes(const tal_t *ctx, struct route_query *rq,
13731401

13741402
/* Re-use the reservation system to make flows aware of each other. */
13751403
struct reserve_hop *reservations = new_reservations(working_ctx, rq);
1404+
double base_prob = DEFAULT_BASE_PROBABILITY;
13761405

13771406
while (!amount_msat_is_zero(amount_to_deliver)) {
13781407
size_t num_parts, parts_slots, excess_parts;
@@ -1409,11 +1438,13 @@ linear_routes(const tal_t *ctx, struct route_query *rq,
14091438
parts_slots == 1) {
14101439
new_flows = single_path_flow(working_ctx, rq, srcnode,
14111440
dstnode, amount_to_deliver,
1412-
mu, delay_feefactor);
1441+
mu, delay_feefactor,
1442+
base_prob);
14131443
} else {
14141444
new_flows =
14151445
solver(working_ctx, rq, srcnode, dstnode,
1416-
amount_to_deliver, mu, delay_feefactor);
1446+
amount_to_deliver, mu, delay_feefactor,
1447+
base_prob);
14171448
}
14181449

14191450
if (!new_flows) {
@@ -1448,9 +1479,10 @@ linear_routes(const tal_t *ctx, struct route_query *rq,
14481479
}
14491480

14501481
if (tal_count(new_flows) > parts_slots) {
1451-
/* Remove the excees of parts and leave one slot for the
1452-
* next round of computations. */
1453-
excess_parts = 1 + tal_count(new_flows) - parts_slots;
1482+
/* try again but with a bigger activation cost per
1483+
* channel */
1484+
base_prob *= DEFAULT_BASE_PROBABILITY;
1485+
continue;
14541486
} else if (tal_count(new_flows) == parts_slots &&
14551487
amount_msat_less(all_deliver, amount_to_deliver)) {
14561488
/* Leave exactly 1 slot for the next round of
@@ -1467,6 +1499,8 @@ linear_routes(const tal_t *ctx, struct route_query *rq,
14671499
tal_count(new_flows));
14681500
goto fail;
14691501
}
1502+
1503+
// FIXME: if too many parts -> decrease the base prob
14701504

14711505
/* Is this set of flows too expensive?
14721506
* We can check if the new flows are within the fee budget,

plugins/askrene/mcf.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ struct flow **minflow(const tal_t *ctx,
3131
const struct gossmap_node *target,
3232
struct amount_msat amount,
3333
u32 mu,
34-
double delay_feefactor);
34+
double delay_feefactor,
35+
double base_prob);
3536

3637
/**
3738
* API for min cost single path.
@@ -53,7 +54,8 @@ struct flow **single_path_flow(const tal_t *ctx, const struct route_query *rq,
5354
const struct gossmap_node *source,
5455
const struct gossmap_node *target,
5556
struct amount_msat amount, u32 mu,
56-
double delay_feefactor);
57+
double delay_feefactor,
58+
double base_prob);
5759

5860
/* To sanity check: this is the approximation mcf uses for the cost
5961
* of each channel. */

0 commit comments

Comments
 (0)