Skip to content

Commit cf15ed8

Browse files
committed
askrene: add arbitrary precision flow unit
Changelog-none: askrene: add arbitrary precision flow unit Signed-off-by: Lagrang3 <[email protected]>
1 parent 798a706 commit cf15ed8

File tree

1 file changed

+41
-37
lines changed

1 file changed

+41
-37
lines changed

plugins/askrene/mcf.c

Lines changed: 41 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,9 @@ struct pay_parameters {
286286
// how much we pay
287287
struct amount_msat amount;
288288

289+
/* base unit for computation, ie. accuracy */
290+
struct amount_msat accuracy;
291+
289292
// channel linearization parameters
290293
double cap_fraction[CHANNEL_PARTS],
291294
cost_fraction[CHANNEL_PARTS];
@@ -360,22 +363,23 @@ static void linearize_channel(const struct pay_parameters *params,
360363
if (amount_msat_greater(mincap, maxcap))
361364
mincap = maxcap;
362365

363-
u64 a = mincap.millisatoshis/1000, /* Raw: linearize_channel */
364-
b = 1 + maxcap.millisatoshis/1000; /* Raw: linearize_channel */
366+
u64 a = amount_msat_ratio_floor(mincap, params->accuracy),
367+
b = 1 + amount_msat_ratio_floor(maxcap, params->accuracy);
365368

366369
/* An extra bound on capacity, here we use it to reduce the flow such
367370
* that it does not exceed htlcmax. */
368-
u64 cap_on_capacity = fp16_to_u64(c->half[dir].htlc_max) / 1000;
371+
u64 cap_on_capacity =
372+
amount_msat_ratio_floor(gossmap_chan_htlc_max(c, dir), params->accuracy);
369373

370374
set_capacity(&capacity[0], a, &cap_on_capacity);
371375
cost[0]=0;
372376
for(size_t i=1;i<CHANNEL_PARTS;++i)
373377
{
374378
set_capacity(&capacity[i], params->cap_fraction[i]*(b-a), &cap_on_capacity);
375379

376-
cost[i] = params->cost_fraction[i]
377-
*params->amount.millisatoshis /* Raw: linearize_channel */
378-
/(b-a);
380+
cost[i] = params->cost_fraction[i] * 1000
381+
* amount_msat_ratio(params->amount, params->accuracy)
382+
/ (b - a);
379383
}
380384
}
381385

@@ -509,7 +513,7 @@ static void combine_cost_function(
509513
*
510514
* into
511515
*
512-
* fee_microsat = c_fee * x_sat
516+
* fee = c_fee/10^6 * x
513517
*
514518
* use `base_fee_penalty` to weight the base fee and `delay_feefactor` to
515519
* weight the CLTV delay.
@@ -538,21 +542,24 @@ struct amount_msat linear_flow_cost(const struct flow *flow,
538542
double delay_feefactor)
539543
{
540544
struct amount_msat msat_cost;
541-
s64 cost = 0;
545+
s64 cost_ppm = 0;
542546
double base_fee_penalty = base_fee_penalty_estimate(total_amount);
543547

544548
for (size_t i = 0; i < tal_count(flow->path); i++) {
545549
const struct half_chan *h = &flow->path[i]->half[flow->dirs[i]];
546550

547-
cost += linear_fee_cost(h->base_fee, h ->proportional_fee, h->delay,
548-
base_fee_penalty, delay_feefactor);
551+
cost_ppm +=
552+
linear_fee_cost(h->base_fee, h->proportional_fee, h->delay,
553+
base_fee_penalty, delay_feefactor);
549554
}
550-
551-
if (!amount_msat_mul(&msat_cost, flow->delivers, cost))
555+
if (!amount_msat_fee(&msat_cost, flow->delivers, 0, cost_ppm))
552556
abort();
553557
return msat_cost;
554558
}
555559

560+
/* FIXME: Instead of mapping one-to-one the indexes in the gossmap, try to
561+
* reduce the number of nodes and arcs used by taking only those that are
562+
* enabled. We might save some cpu if the work with a pruned network. */
556563
static struct linear_network *
557564
init_linear_network(const tal_t *ctx, const struct pay_parameters *params)
558565
{
@@ -610,6 +617,7 @@ init_linear_network(const tal_t *ctx, const struct pay_parameters *params)
610617
// that are outgoing to `node`
611618
linearize_channel(params, c, half, capacity, prob_cost);
612619

620+
/* linear fee_cost per unit of flow */
613621
const s64 fee_cost = linear_fee_cost(
614622
c->half[half].base_fee,
615623
c->half[half].proportional_fee,
@@ -732,13 +740,14 @@ struct list_data
732740
* balance, compute the bigest flow and substract it from the nodes balance and
733741
* the channels allocation. */
734742
static struct flow *substract_flow(const tal_t *ctx,
735-
const struct gossmap *gossmap,
743+
const struct pay_parameters *params,
736744
const struct node source,
737745
const struct node sink,
738746
s64 *balance, struct chan_flow *chan_flow,
739747
const u32 *prev_idx, const int *prev_dir,
740748
const struct gossmap_chan *const *prev_chan)
741749
{
750+
const struct gossmap *gossmap = params->rq->gossmap;
742751
assert(balance[source.idx] < 0);
743752
assert(balance[sink.idx] > 0);
744753
s64 delta = -balance[source.idx];
@@ -784,7 +793,8 @@ static struct flow *substract_flow(const tal_t *ctx,
784793

785794
chan_flow[chan_idx].half[dir] -= delta;
786795
}
787-
f->delivers = amount_msat(delta * 1000);
796+
if (!amount_msat_mul(&f->delivers, params->accuracy, delta))
797+
abort();
788798
return f;
789799
}
790800

@@ -837,16 +847,16 @@ static void substract_cycle(const struct gossmap *gossmap,
837847
static struct flow **
838848
get_flow_paths(const tal_t *ctx,
839849
const tal_t *working_ctx,
840-
const struct route_query *rq,
850+
const struct pay_parameters *params,
841851
const struct linear_network *linear_network,
842852
const struct residual_network *residual_network)
843853
{
844854
struct flow **flows = tal_arr(ctx,struct flow*,0);
845855

846-
const size_t max_num_chans = gossmap_max_chan_idx(rq->gossmap);
856+
const size_t max_num_chans = gossmap_max_chan_idx(params->rq->gossmap);
847857
struct chan_flow *chan_flow = tal_arrz(working_ctx,struct chan_flow,max_num_chans);
848858

849-
const size_t max_num_nodes = gossmap_max_node_idx(rq->gossmap);
859+
const size_t max_num_nodes = gossmap_max_node_idx(params->rq->gossmap);
850860
s64 *balance = tal_arrz(working_ctx,s64,max_num_nodes);
851861

852862
const struct gossmap_chan **prev_chan
@@ -892,21 +902,21 @@ get_flow_paths(const tal_t *ctx,
892902
while (balance[source.idx] < 0) {
893903
prev_chan[source.idx] = NULL;
894904
struct node sink = find_path_or_cycle(
895-
working_ctx, rq->gossmap, chan_flow, source,
905+
working_ctx, params->rq->gossmap, chan_flow, source,
896906
balance, prev_chan, prev_dir, prev_idx);
897907

898908
if (balance[sink.idx] > 0)
899909
/* case 1. found a path */
900910
{
901911
struct flow *fp = substract_flow(
902-
flows, rq->gossmap, source, sink, balance,
912+
flows, params, source, sink, balance,
903913
chan_flow, prev_idx, prev_dir, prev_chan);
904914

905915
tal_arr_expand(&flows, fp);
906916
} else
907917
/* case 2. found a cycle */
908918
{
909-
substract_cycle(rq->gossmap, sink, chan_flow,
919+
substract_cycle(params->rq->gossmap, sink, chan_flow,
910920
prev_idx, prev_dir, prev_chan);
911921
}
912922
}
@@ -944,6 +954,10 @@ struct flow **minflow(const tal_t *ctx,
944954
params->source = source;
945955
params->target = target;
946956
params->amount = amount;
957+
params->accuracy = AMOUNT_MSAT(1000);
958+
/* FIXME: params->accuracy = amount_msat_max(amount_msat_div(amount,
959+
* 1000), AMOUNT_MSAT(1));
960+
* */
947961

948962
// template the channel partition into linear arcs
949963
params->cap_fraction[0]=0;
@@ -972,24 +986,14 @@ struct flow **minflow(const tal_t *ctx,
972986

973987
init_residual_network(linear_network,residual_network);
974988

975-
/* TODO(eduardo):
976-
* Some MCF algorithms' performance depend on the size of maxflow. If we
977-
* were to work in units of msats we 1. risking overflow when computing
978-
* costs and 2. we risk a performance overhead for no good reason.
979-
*
980-
* Working in units of sats was my first choice, but maybe working in
981-
* units of 10, or 100 sats could be even better.
982-
*
983-
* IDEA: define the size of our precision as some parameter got at
984-
* runtime that depends on the size of the payment and adjust the MCF
985-
* accordingly.
986-
* For example if we are trying to pay 1M sats our precision could be
987-
* set to 1000sat, then channels that had capacity for 3M sats become 3k
988-
* flow units. */
989-
const u64 pay_amount_sats = (params->amount.millisatoshis + 999)/1000; /* Raw: minflow */
989+
/* Since we have constraint accuracy, ask to find a payment solution
990+
* that can pay a bit more than the actual value rathen than undershoot it.
991+
* That's why we use the ceil function here. */
992+
const u64 pay_amount =
993+
amount_msat_ratio_ceil(params->amount, params->accuracy);
990994

991995
if (!simple_feasibleflow(working_ctx, linear_network->graph, src, dst,
992-
residual_network->cap, pay_amount_sats)) {
996+
residual_network->cap, pay_amount)) {
993997
rq_log(tmpctx, rq, LOG_INFORM,
994998
"%s failed: unable to find a feasible flow.", __func__);
995999
goto fail;
@@ -1012,7 +1016,7 @@ struct flow **minflow(const tal_t *ctx,
10121016
/* We dissect the solution of the MCF into payment routes.
10131017
* Actual amounts considering fees are computed for every
10141018
* channel in the routes. */
1015-
flow_paths = get_flow_paths(ctx, working_ctx, rq,
1019+
flow_paths = get_flow_paths(ctx, working_ctx, params,
10161020
linear_network, residual_network);
10171021
if(!flow_paths){
10181022
rq_log(tmpctx, rq, LOG_BROKEN,

0 commit comments

Comments
 (0)