#include "config.h"
#include <assert.h>
#include <ccan/list/list.h>
#include <ccan/lqueue/lqueue.h>
#include <ccan/tal/str/str.h>
#include <ccan/tal/tal.h>
#include <common/pseudorand.h>
#include <common/utils.h>
#include <math.h>
#include <plugins/renepay/dijkstra.h>
#include <plugins/renepay/flow.h>
#include <plugins/renepay/mcf.h>
#include <stdint.h>
#define PARTS_BITS 2
#define CHANNEL_PARTS (1 << PARTS_BITS)
static const double CHANNEL_PIVOTS[]={0,0.5,0.8,0.95};
static const s64 INFINITE = INT64_MAX;
static const u64 INFINITE_MSAT = UINT64_MAX;
static const u32 INVALID_INDEX = 0xffffffff;
static const s64 MU_MAX = 128;
struct arc {
u32 idx;
};
#define ARC_DUAL_BITOFF (0)
#define ARC_PART_BITOFF (1)
#define ARC_CHANDIR_BITOFF (1 + PARTS_BITS)
#define ARC_CHANIDX_BITOFF (1 + PARTS_BITS + 1)
#define ARC_CHANIDX_BITS (32 - ARC_CHANIDX_BITOFF)
#define ARCS_PER_CHANNEL ((size_t)1 << (PARTS_BITS + 1 + 1))
static inline void arc_to_parts(struct arc arc,
u32 *chanidx,
int *chandir,
u32 *part,
bool *dual)
{
if (chanidx)
*chanidx = (arc.idx >> ARC_CHANIDX_BITOFF);
if (chandir)
*chandir = (arc.idx >> ARC_CHANDIR_BITOFF) & 1;
if (part)
*part = (arc.idx >> ARC_PART_BITOFF) & ((1 << PARTS_BITS)-1);
if (dual)
*dual = (arc.idx >> ARC_DUAL_BITOFF) & 1;
}
static inline struct arc arc_from_parts(u32 chanidx, int chandir, u32 part, bool dual)
{
struct arc arc;
assert(part < CHANNEL_PARTS);
assert(chandir == 0 || chandir == 1);
assert(chanidx < (1U << ARC_CHANIDX_BITS));
arc.idx = ((u32)dual << ARC_DUAL_BITOFF)
| (part << ARC_PART_BITOFF)
| ((u32)chandir << ARC_CHANDIR_BITOFF)
| (chanidx << ARC_CHANIDX_BITOFF);
return arc;
}
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
#define MIN(x, y) (((x) < (y)) ? (x) : (y))
struct pay_parameters {
struct gossmap *gossmap;
const struct gossmap_node *source;
const struct gossmap_node *target;
struct chan_extra_map *chan_extra_map;
const bitmap *disabled;
struct amount_msat amount;
double cap_fraction[CHANNEL_PARTS],
cost_fraction[CHANNEL_PARTS];
struct amount_msat max_fee;
double min_probability;
double base_probability;
double delay_feefactor;
double base_fee_penalty;
u32 prob_cost_factor;
};
struct linear_network
{
u32 *arc_tail_node;
struct arc *node_adjacency_next_arc;
struct arc *node_adjacency_first_arc;
s64 *arc_prob_cost, *arc_fee_cost;
s64 *capacity;
size_t max_num_arcs,max_num_nodes;
};
struct residual_network {
s64 *cap;
s64 *cost;
s64 *potential;
};
static struct arc arc_dual(struct arc arc)
{
arc.idx ^= (1U << ARC_DUAL_BITOFF);
return arc;
}
static bool arc_is_dual(struct arc arc)
{
bool dual;
arc_to_parts(arc, NULL, NULL, NULL, &dual);
return dual;
}
static s64 get_arc_flow(
const struct residual_network *network,
const struct arc arc)
{
assert(!arc_is_dual(arc));
assert(arc_dual(arc).idx < tal_count(network->cap));
return network->cap[ arc_dual(arc).idx ];
}
static u32 arc_tail(const struct linear_network *linear_network,
const struct arc arc)
{
assert(arc.idx < tal_count(linear_network->arc_tail_node));
return linear_network->arc_tail_node[ arc.idx ];
}
static u32 arc_head(const struct linear_network *linear_network,
const struct arc arc)
{
const struct arc dual = arc_dual(arc);
assert(dual.idx < tal_count(linear_network->arc_tail_node));
return linear_network->arc_tail_node[dual.idx];
}
static struct arc node_adjacency_begin(
const struct linear_network * linear_network,
const u32 node)
{
assert(node < tal_count(linear_network->node_adjacency_first_arc));
return linear_network->node_adjacency_first_arc[node];
}
static bool node_adjacency_end(const struct arc arc)
{
return arc.idx == INVALID_INDEX;
}
static struct arc node_adjacency_next(
const struct linear_network *linear_network,
const struct arc arc)
{
assert(arc.idx < tal_count(linear_network->node_adjacency_next_arc));
return linear_network->node_adjacency_next_arc[arc.idx];
}
static bool channel_is_available(const struct gossmap_chan *c, int dir,
const struct gossmap *gossmap,
const bitmap *disabled)
{
if (!gossmap_chan_set(c, dir))
return false;
const u32 chan_idx = gossmap_chan_idx(gossmap, c);
return !bitmap_test_bit(disabled, chan_idx * 2 + dir);
}
static bool linearize_channel(const struct pay_parameters *params,
const struct gossmap_chan *c, const int dir,
s64 *capacity, s64 *cost)
{
struct chan_extra_half *extra_half = get_chan_extra_half_by_chan(
params->gossmap,
params->chan_extra_map,
c,
dir);
if (!extra_half) {
return false;
}
assert(
amount_msat_less_eq(extra_half->known_min, extra_half->known_max));
s64 h = (extra_half->htlc_total.millisatoshis+999)/1000;
s64 a = extra_half->known_min.millisatoshis/1000,
b = 1 + extra_half->known_max.millisatoshis/1000;
a -= h;
b -= h;
a = MAX(a,0);
b = MAX(a+1,b);
s64 cap_on_capacity =
gossmap_chan_htlc_max(c, dir).millisatoshis/1000;
capacity[0]=a;
cost[0]=0;
assert(params->base_probability > 5e-7);
assert(params->base_probability <= 1.0);
const double base_prob_factor = -log(params->base_probability);
for(size_t i=1;i<CHANNEL_PARTS;++i)
{
capacity[i] = MIN(params->cap_fraction[i]*(b-a), cap_on_capacity);
cap_on_capacity -= capacity[i];
assert(cap_on_capacity>=0);
cost[i] = (params->cost_fraction[i]*1.0/(b-a) + base_prob_factor)
*params->amount.millisatoshis
*params->prob_cost_factor;
}
return true;
}
static struct residual_network *
alloc_residual_network(const tal_t *ctx, const size_t max_num_nodes,
const size_t max_num_arcs)
{
struct residual_network *residual_network =
tal(ctx, struct residual_network);
if (!residual_network)
goto function_fail;
residual_network->cap = tal_arrz(residual_network, s64, max_num_arcs);
residual_network->cost = tal_arrz(residual_network, s64, max_num_arcs);
residual_network->potential =
tal_arrz(residual_network, s64, max_num_nodes);
if (!residual_network->cap || !residual_network->cost ||
!residual_network->potential) {
goto function_fail;
}
return residual_network;
function_fail:
return tal_free(residual_network);
}
static void init_residual_network(
const struct linear_network * linear_network,
struct residual_network* residual_network)
{
const size_t max_num_arcs = linear_network->max_num_arcs;
const size_t max_num_nodes = linear_network->max_num_nodes;
for(struct arc arc = {0};arc.idx < max_num_arcs; ++arc.idx)
{
if(arc_is_dual(arc))
continue;
struct arc dual = arc_dual(arc);
residual_network->cap[arc.idx]=linear_network->capacity[arc.idx];
residual_network->cap[dual.idx]=0;
residual_network->cost[arc.idx]=residual_network->cost[dual.idx]=0;
}
for(u32 i=0;i<max_num_nodes;++i)
{
residual_network->potential[i]=0;
}
}
static void combine_cost_function(
const struct linear_network* linear_network,
struct residual_network *residual_network,
s64 mu)
{
for(struct arc arc = {0};arc.idx < linear_network->max_num_arcs; ++arc.idx)
{
if(arc_tail(linear_network,arc)==INVALID_INDEX)
continue;
const s64 pcost = linear_network->arc_prob_cost[arc.idx],
fcost = linear_network->arc_fee_cost[arc.idx];
const s64 combined = pcost==INFINITE || fcost==INFINITE ? INFINITE :
mu*fcost + (MU_MAX-1-mu)*pcost;
residual_network->cost[arc.idx]
= mu==0 ? pcost :
(mu==(MU_MAX-1) ? fcost : combined);
}
}
static void linear_network_add_adjacenct_arc(
struct linear_network *linear_network,
const u32 node_idx,
const struct arc arc)
{
assert(arc.idx < tal_count(linear_network->arc_tail_node));
linear_network->arc_tail_node[arc.idx] = node_idx;
assert(node_idx < tal_count(linear_network->node_adjacency_first_arc));
const struct arc first_arc = linear_network->node_adjacency_first_arc[node_idx];
assert(arc.idx < tal_count(linear_network->node_adjacency_next_arc));
linear_network->node_adjacency_next_arc[arc.idx]=first_arc;
assert(node_idx < tal_count(linear_network->node_adjacency_first_arc));
linear_network->node_adjacency_first_arc[node_idx]=arc;
}
static s64 linear_fee_cost(
const struct gossmap_chan *c,
const int dir,
double base_fee_penalty,
double delay_feefactor)
{
assert(c);
assert(dir==0 || dir==1);
s64 pfee = c->half[dir].proportional_fee,
bfee = c->half[dir].base_fee,
delay = c->half[dir].delay;
return pfee + bfee* base_fee_penalty+ delay*delay_feefactor;
}
static struct linear_network *
init_linear_network(const tal_t *ctx, const struct pay_parameters *params,
char **fail)
{
tal_t *this_ctx = tal(ctx,tal_t);
struct linear_network * linear_network = tal(ctx, struct linear_network);
if (!linear_network) {
if (fail)
*fail = tal_fmt(ctx, "bad allocation of linear_network");
goto function_fail;
}
const size_t max_num_chans = gossmap_max_chan_idx(params->gossmap);
const size_t max_num_arcs = max_num_chans * ARCS_PER_CHANNEL;
const size_t max_num_nodes = gossmap_max_node_idx(params->gossmap);
linear_network->max_num_arcs = max_num_arcs;
linear_network->max_num_nodes = max_num_nodes;
linear_network->arc_tail_node = tal_arr(linear_network,u32,max_num_arcs);
if(!linear_network->arc_tail_node)
{
if (fail)
*fail = tal_fmt(ctx, "bad allocation of arc_tail_node");
goto function_fail;
}
for(size_t i=0;i<tal_count(linear_network->arc_tail_node);++i)
linear_network->arc_tail_node[i]=INVALID_INDEX;
linear_network->node_adjacency_next_arc = tal_arr(linear_network,struct arc,max_num_arcs);
if(!linear_network->node_adjacency_next_arc)
{
if (fail)
*fail = tal_fmt(ctx, "bad allocation of node_adjacency_next_arc");
goto function_fail;
}
for(size_t i=0;i<tal_count(linear_network->node_adjacency_next_arc);++i)
linear_network->node_adjacency_next_arc[i].idx=INVALID_INDEX;
linear_network->node_adjacency_first_arc = tal_arr(linear_network,struct arc,max_num_nodes);
if(!linear_network->node_adjacency_first_arc)
{
if (fail)
*fail = tal_fmt(ctx, "bad allocation of node_adjacency_first_arc");
goto function_fail;
}
for(size_t i=0;i<tal_count(linear_network->node_adjacency_first_arc);++i)
linear_network->node_adjacency_first_arc[i].idx=INVALID_INDEX;
linear_network->arc_prob_cost = tal_arr(linear_network,s64,max_num_arcs);
if(!linear_network->arc_prob_cost)
{
if (fail)
*fail = tal_fmt(ctx, "bad allocation of arc_prob_cost");
goto function_fail;
}
for(size_t i=0;i<tal_count(linear_network->arc_prob_cost);++i)
linear_network->arc_prob_cost[i]=INFINITE;
linear_network->arc_fee_cost = tal_arr(linear_network,s64,max_num_arcs);
if(!linear_network->arc_fee_cost)
{
if (fail)
*fail = tal_fmt(ctx, "bad allocation of arc_fee_cost");
goto function_fail;
}
for(size_t i=0;i<tal_count(linear_network->arc_fee_cost);++i)
linear_network->arc_fee_cost[i]=INFINITE;
linear_network->capacity = tal_arrz(linear_network,s64,max_num_arcs);
if(!linear_network->capacity)
{
if (fail)
*fail = tal_fmt(ctx, "bad allocation of capacity");
goto function_fail;
}
for(struct gossmap_node *node = gossmap_first_node(params->gossmap);
node;
node=gossmap_next_node(params->gossmap,node))
{
const u32 node_id = gossmap_node_idx(params->gossmap,node);
for(size_t j=0;j<node->num_chans;++j)
{
int half;
const struct gossmap_chan *c = gossmap_nth_chan(params->gossmap,
node, j, &half);
if (!channel_is_available(c, half, params->gossmap,
params->disabled))
continue;
const u32 chan_id = gossmap_chan_idx(params->gossmap, c);
const struct gossmap_node *next = gossmap_nth_node(params->gossmap,
c,!half);
const u32 next_id = gossmap_node_idx(params->gossmap,next);
if(node_id==next_id)
continue;
s64 prob_cost[CHANNEL_PARTS], capacity[CHANNEL_PARTS];
if (!linearize_channel(params, c, half,
capacity, prob_cost)) {
if(fail)
*fail =
tal_fmt(ctx, "linearize_channel failed");
goto function_fail;
}
const s64 fee_cost = linear_fee_cost(c,half,
params->base_fee_penalty,
params->delay_feefactor);
for(size_t k=0;k<CHANNEL_PARTS;++k)
{
struct arc arc = arc_from_parts(chan_id, half, k, false);
linear_network_add_adjacenct_arc(linear_network,node_id,arc);
linear_network->capacity[arc.idx] = capacity[k];
linear_network->arc_prob_cost[arc.idx] = prob_cost[k];
linear_network->arc_fee_cost[arc.idx] = fee_cost;
struct arc dual = arc_dual(arc);
linear_network_add_adjacenct_arc(linear_network,next_id,dual);
linear_network->capacity[dual.idx] = 0;
linear_network->arc_prob_cost[dual.idx] = -prob_cost[k];
linear_network->arc_fee_cost[dual.idx] = -fee_cost;
}
}
}
tal_free(this_ctx);
return linear_network;
function_fail:
tal_free(this_ctx);
return tal_free(linear_network);
}
struct queue_data
{
u32 idx;
struct lqueue_link ql;
};
static bool
find_admissible_path(const tal_t *ctx,
const struct linear_network *linear_network,
const struct residual_network *residual_network,
const u32 source, const u32 target, struct arc *prev)
{
tal_t *this_ctx = tal(ctx,tal_t);
bool target_found = false;
for(size_t i=0;i<tal_count(prev);++i)
prev[i].idx=INVALID_INDEX;
LQUEUE(struct queue_data,ql) myqueue = LQUEUE_INIT;
struct queue_data *qdata;
qdata = tal(this_ctx,struct queue_data);
qdata->idx = source;
lqueue_enqueue(&myqueue,qdata);
while(!lqueue_empty(&myqueue))
{
qdata = lqueue_dequeue(&myqueue);
u32 cur = qdata->idx;
tal_free(qdata);
if(cur==target)
{
target_found = true;
break;
}
for(struct arc arc = node_adjacency_begin(linear_network,cur);
!node_adjacency_end(arc);
arc = node_adjacency_next(linear_network,arc))
{
if(residual_network->cap[arc.idx] <= 0)
continue;
u32 next = arc_head(linear_network,arc);
assert(next < tal_count(prev));
if(prev[next].idx!=INVALID_INDEX)
continue;
prev[next] = arc;
qdata = tal(this_ctx,struct queue_data);
qdata->idx = next;
lqueue_enqueue(&myqueue,qdata);
}
}
tal_free(this_ctx);
return target_found;
}
static s64 get_augmenting_flow(
const struct linear_network* linear_network,
const struct residual_network *residual_network,
const u32 source,
const u32 target,
const struct arc *prev)
{
s64 flow = INFINITE;
u32 cur = target;
while(cur!=source)
{
assert(cur<tal_count(prev));
const struct arc arc = prev[cur];
flow = MIN(flow , residual_network->cap[arc.idx]);
cur = arc_tail(linear_network,arc);
}
assert(flow<INFINITE && flow>0);
return flow;
}
static void augment_flow(
const struct linear_network *linear_network,
struct residual_network *residual_network,
const u32 source,
const u32 target,
const struct arc *prev,
s64 flow)
{
u32 cur = target;
while(cur!=source)
{
assert(cur < tal_count(prev));
const struct arc arc = prev[cur];
const struct arc dual = arc_dual(arc);
assert(arc.idx < tal_count(residual_network->cap));
assert(dual.idx < tal_count(residual_network->cap));
residual_network->cap[arc.idx] -= flow;
residual_network->cap[dual.idx] += flow;
assert(residual_network->cap[arc.idx] >=0 );
cur = arc_tail(linear_network,arc);
}
}
static bool find_feasible_flow(const tal_t *ctx,
const struct linear_network *linear_network,
struct residual_network *residual_network,
const u32 source, const u32 target, s64 amount,
char **fail)
{
assert(amount>=0);
tal_t *this_ctx = tal(ctx,tal_t);
struct arc *prev = tal_arr(this_ctx,struct arc,linear_network->max_num_nodes);
if(!prev)
{
if(fail)
*fail = tal_fmt(ctx, "bad allocation of prev");
goto function_fail;
}
while(amount>0)
{
if (!find_admissible_path(this_ctx, linear_network,
residual_network, source, target,
prev))
{
if(fail)
*fail = tal_fmt(ctx, "find_admissible_path failed");
goto function_fail;
}
s64 delta = get_augmenting_flow(linear_network,
residual_network,
source,target,prev);
delta = MIN(amount,delta);
assert(delta>0 && delta<=amount);
augment_flow(linear_network,residual_network,source,target,prev,delta);
amount -= delta;
}
tal_free(this_ctx);
return true;
function_fail:
tal_free(this_ctx);
return false;
}
static bool find_optimal_path(const tal_t *ctx, struct dijkstra *dijkstra,
const struct linear_network *linear_network,
const struct residual_network *residual_network,
const u32 source, const u32 target,
struct arc *prev, char **fail)
{
tal_t *this_ctx = tal(ctx,tal_t);
bool target_found = false;
bitmap *visited = tal_arrz(this_ctx, bitmap,
BITMAP_NWORDS(linear_network->max_num_nodes));
if(!visited)
{
if(fail)
*fail = tal_fmt(ctx, "bad allocation of visited");
goto finish;
}
for(size_t i=0;i<tal_count(prev);++i)
prev[i].idx=INVALID_INDEX;
const s64 *const distance=dijkstra_distance_data(dijkstra);
dijkstra_init(dijkstra);
dijkstra_update(dijkstra,source,0);
while(!dijkstra_empty(dijkstra))
{
u32 cur = dijkstra_top(dijkstra);
dijkstra_pop(dijkstra);
if(bitmap_test_bit(visited,cur))
continue;
bitmap_set_bit(visited,cur);
if(cur==target)
{
target_found = true;
break;
}
for(struct arc arc = node_adjacency_begin(linear_network,cur);
!node_adjacency_end(arc);
arc = node_adjacency_next(linear_network,arc))
{
if(residual_network->cap[arc.idx] <= 0)
continue;
u32 next = arc_head(linear_network,arc);
s64 cij = residual_network->cost[arc.idx]
- residual_network->potential[cur]
+ residual_network->potential[next];
assert(cij>=0);
if(distance[next]<=distance[cur]+cij)
continue;
dijkstra_update(dijkstra,next,distance[cur]+cij);
prev[next]=arc;
}
}
if (!target_found && fail)
*fail = tal_fmt(ctx, "no route to destination");
finish:
tal_free(this_ctx);
return target_found;
}
static void zero_flow(
const struct linear_network *linear_network,
struct residual_network *residual_network)
{
for(u32 node=0;node<linear_network->max_num_nodes;++node)
{
residual_network->potential[node]=0;
for(struct arc arc=node_adjacency_begin(linear_network,node);
!node_adjacency_end(arc);
arc = node_adjacency_next(linear_network,arc))
{
if(arc_is_dual(arc))continue;
struct arc dual = arc_dual(arc);
residual_network->cap[arc.idx] = linear_network->capacity[arc.idx];
residual_network->cap[dual.idx] = 0;
}
}
}
static bool optimize_mcf(const tal_t *ctx, struct dijkstra *dijkstra,
const struct linear_network *linear_network,
struct residual_network *residual_network,
const u32 source, const u32 target, const s64 amount,
char **fail)
{
assert(amount>=0);
tal_t *this_ctx = tal(ctx,tal_t);
char *errmsg;
zero_flow(linear_network,residual_network);
struct arc *prev = tal_arr(this_ctx,struct arc,linear_network->max_num_nodes);
const s64 *const distance = dijkstra_distance_data(dijkstra);
s64 remaining_amount = amount;
while(remaining_amount>0)
{
if (!find_optimal_path(this_ctx, dijkstra, linear_network,
residual_network, source, target, prev,
&errmsg)) {
if (fail)
*fail =
tal_fmt(ctx, "find_optimal_path failed: %s",
errmsg);
goto function_fail;
}
s64 delta = get_augmenting_flow(linear_network,residual_network,source,target,prev);
delta = MIN(remaining_amount,delta);
assert(delta>0 && delta<=remaining_amount);
augment_flow(linear_network,residual_network,source,target,prev,delta);
remaining_amount -= delta;
for(u32 n=0;n<linear_network->max_num_nodes;++n)
{
residual_network->potential[n] -= MIN(distance[target],distance[n]);
}
}
tal_free(this_ctx);
return true;
function_fail:
tal_free(this_ctx);
return false;
}
struct chan_flow
{
s64 half[2];
};
static u32 find_positive_balance(
const struct gossmap *gossmap,
const bitmap *disabled,
const struct chan_flow *chan_flow,
const u32 start_idx,
const s64 *balance,
const struct gossmap_chan **prev_chan,
int *prev_dir,
u32 *prev_idx)
{
u32 final_idx = start_idx;
while(balance[final_idx]<=0)
{
u32 updated_idx=INVALID_INDEX;
struct gossmap_node *cur
= gossmap_node_byidx(gossmap,final_idx);
for(size_t i=0;i<cur->num_chans;++i)
{
int dir;
const struct gossmap_chan *c
= gossmap_nth_chan(gossmap,
cur,i,&dir);
if (!channel_is_available(c, dir, gossmap, disabled))
continue;
const u32 c_idx = gossmap_chan_idx(gossmap,c);
if(chan_flow[c_idx].half[dir]>0)
{
const struct gossmap_node *next
= gossmap_nth_node(gossmap,c,!dir);
u32 next_idx = gossmap_node_idx(gossmap,next);
prev_dir[next_idx] = dir;
prev_chan[next_idx] = c;
prev_idx[next_idx] = final_idx;
updated_idx = next_idx;
break;
}
}
assert(updated_idx!=INVALID_INDEX);
assert(updated_idx!=final_idx);
final_idx = updated_idx;
}
return final_idx;
}
struct list_data
{
struct list_node list;
struct flow *flow_path;
};
static inline uint64_t pseudorand_interval(uint64_t a, uint64_t b)
{
if (a == b)
return b;
assert(b > a);
return a + pseudorand(b - a);
}
static struct flow **
get_flow_paths(const tal_t *ctx, const struct gossmap *gossmap,
const bitmap *disabled,
struct chan_extra_map *chan_extra_map,
const struct linear_network *linear_network,
const struct residual_network *residual_network,
struct amount_msat excess,
const double base_probability,
char **fail)
{
tal_t *this_ctx = tal(ctx,tal_t);
struct flow **flows = tal_arr(ctx,struct flow*,0);
assert(amount_msat_less(excess, AMOUNT_MSAT(1000)));
const size_t max_num_chans = gossmap_max_chan_idx(gossmap);
struct chan_flow *chan_flow = tal_arrz(this_ctx,struct chan_flow,max_num_chans);
const size_t max_num_nodes = gossmap_max_node_idx(gossmap);
s64 *balance = tal_arrz(this_ctx,s64,max_num_nodes);
const struct gossmap_chan **prev_chan
= tal_arr(this_ctx,const struct gossmap_chan *,max_num_nodes);
int *prev_dir = tal_arr(this_ctx,int,max_num_nodes);
u32 *prev_idx = tal_arr(this_ctx,u32,max_num_nodes);
if (!chan_flow || !balance || !prev_chan || !prev_idx || !prev_dir) {
if (fail)
*fail = tal_fmt(ctx, "bad allocation");
goto function_fail;
}
for(u32 n = 0;n<max_num_nodes;++n)
{
for(struct arc arc = node_adjacency_begin(linear_network,n);
!node_adjacency_end(arc);
arc = node_adjacency_next(linear_network,arc))
{
if(arc_is_dual(arc))
continue;
u32 m = arc_head(linear_network,arc);
s64 flow = get_arc_flow(residual_network,arc);
u32 chanidx;
int chandir;
balance[n] -= flow;
balance[m] += flow;
arc_to_parts(arc, &chanidx, &chandir, NULL, NULL);
chan_flow[chanidx].half[chandir] +=flow;
}
}
for(u32 node_idx=0;node_idx<max_num_nodes;++node_idx)
{
while(balance[node_idx]<0)
{
prev_chan[node_idx]=NULL;
u32 final_idx = find_positive_balance(
gossmap, disabled, chan_flow, node_idx, balance,
prev_chan, prev_dir, prev_idx);
struct amount_msat sup_htlc_min = AMOUNT_MSAT_INIT(0),
inf_htlc_max =
AMOUNT_MSAT_INIT(INFINITE_MSAT);
s64 delta=-balance[node_idx];
int length = 0;
delta = MIN(delta,balance[final_idx]);
for(u32 cur_idx = final_idx;
cur_idx!=node_idx;
cur_idx=prev_idx[cur_idx])
{
assert(cur_idx!=INVALID_INDEX);
const int dir = prev_dir[cur_idx];
const struct gossmap_chan *const c = prev_chan[cur_idx];
const u32 c_idx = gossmap_chan_idx(gossmap,c);
delta=MIN(delta,chan_flow[c_idx].half[dir]);
length++;
sup_htlc_min = amount_msat_max(
sup_htlc_min, gossmap_chan_htlc_min(c, dir));
inf_htlc_max = amount_msat_min(
inf_htlc_max, gossmap_chan_htlc_max(c, dir));
}
s64 htlc_max=inf_htlc_max.millisatoshis/1000;
s64 htlc_min=(sup_htlc_min.millisatoshis+999)/1000;
if (htlc_min > htlc_max) {
htlc_min = 0;
}
if (delta > htlc_max) {
delta = pseudorand_interval(
MAX(htlc_min, (htlc_max * 50) / 100),
htlc_max);
}
struct flow *fp = tal(this_ctx,struct flow);
fp->path = tal_arr(fp,const struct gossmap_chan *,length);
fp->dirs = tal_arr(fp,int,length);
balance[node_idx] += delta;
balance[final_idx]-= delta;
for(u32 cur_idx = final_idx;
cur_idx!=node_idx;
cur_idx=prev_idx[cur_idx])
{
assert(cur_idx!=INVALID_INDEX);
const int dir = prev_dir[cur_idx];
const struct gossmap_chan *const c = prev_chan[cur_idx];
const u32 c_idx = gossmap_chan_idx(gossmap,c);
length--;
fp->path[length]=c;
fp->dirs[length]=dir;
chan_flow[c_idx].half[prev_dir[cur_idx]]-=delta;
}
assert(delta>0);
struct amount_msat delivered = amount_msat(delta*1000);
if (!amount_msat_deduct(&delivered, excess)) {
if (fail)
*fail = tal_fmt(
ctx, "unable to substract excess");
goto function_fail;
}
excess = amount_msat(0);
fp->amount = delivered;
fp->success_prob =
flow_probability(fp, gossmap, chan_extra_map, false)
* pow(base_probability, tal_count(fp->path));
if (fp->success_prob < 0) {
if (fail)
*fail =
tal_fmt(ctx, "failed to compute "
"flow probability");
goto function_fail;
}
tal_arr_expand(&flows, fp);
}
}
for(size_t i=0;i<tal_count(flows);++i)
{
flows[i] = tal_steal(flows,flows[i]);
assert(flows[i]);
}
if (fail)
*fail = NULL;
tal_free(this_ctx);
return flows;
function_fail:
tal_free(this_ctx);
return tal_free(flows);
}
static bool is_better(
struct amount_msat max_fee,
double min_probability,
struct amount_msat A_fee,
double A_prob,
struct amount_msat B_fee,
double B_prob)
{
bool A_fee_pass = amount_msat_less_eq(A_fee,max_fee);
bool B_fee_pass = amount_msat_less_eq(B_fee,max_fee);
bool A_prob_pass = A_prob >= min_probability;
bool B_prob_pass = B_prob >= min_probability;
if(A_fee_pass && B_fee_pass && A_prob_pass && B_prob_pass)
{
goto fees_or_prob;
}
if(!(A_fee_pass && A_prob_pass) && (B_fee_pass && B_prob_pass))
{
return false;
}
if((A_fee_pass && A_prob_pass) && !(B_fee_pass && B_prob_pass))
{
return true;
}
if(A_fee_pass && B_fee_pass)
{
return A_prob > B_prob;
}
if(A_prob_pass && B_prob_pass)
{
goto fees_or_prob;
}
if(A_fee_pass && !B_fee_pass)
{
return true;
}
if(B_fee_pass && !A_fee_pass)
{
return false;
}
if(A_prob_pass && !B_prob_pass)
{
return true;
}
if(B_prob_pass && !A_prob_pass)
{
return true;
}
fees_or_prob:
if(amount_msat_eq(A_fee,B_fee))
{
return A_prob > B_prob;
}
return amount_msat_less_eq(A_fee,B_fee);
}
static bool check_disabled(const bitmap *disabled,
const struct gossmap *gossmap,
const struct chan_extra_map *chan_extra_map)
{
assert(disabled);
assert(gossmap);
assert(chan_extra_map);
if (tal_bytelen(disabled) !=
2 * bitmap_sizeof(gossmap_max_chan_idx(gossmap)))
return false;
for (struct gossmap_chan *chan = gossmap_first_chan(gossmap); chan;
chan = gossmap_next_chan(gossmap, chan)) {
const u32 chan_idx = gossmap_chan_idx(gossmap, chan);
if (bitmap_test_bit(disabled, chan_idx * 2 + 0) &&
bitmap_test_bit(disabled, chan_idx * 2 + 1))
continue;
struct short_channel_id scid = gossmap_chan_scid(gossmap, chan);
struct chan_extra *ce =
chan_extra_map_get(chan_extra_map, scid);
if (!ce)
return false;
}
return true;
}
struct flow **minflow(const tal_t *ctx, struct gossmap *gossmap,
const struct gossmap_node *source,
const struct gossmap_node *target,
struct chan_extra_map *chan_extra_map,
const bitmap *disabled, struct amount_msat amount,
struct amount_msat max_fee, double min_probability,
double base_probability,
double delay_feefactor, double base_fee_penalty,
u32 prob_cost_factor, char **fail)
{
tal_t *this_ctx = tal(ctx,tal_t);
char *errmsg;
struct flow **best_flow_paths = NULL;
struct pay_parameters *params = tal(this_ctx,struct pay_parameters);
struct dijkstra *dijkstra;
params->gossmap = gossmap;
params->source = source;
params->target = target;
params->chan_extra_map = chan_extra_map;
params->disabled = disabled;
if (!check_disabled(disabled, gossmap, chan_extra_map)) {
if (fail)
*fail = tal_fmt(ctx, "Invalid disabled bitmap.");
goto function_fail;
}
params->amount = amount;
params->cap_fraction[0]=0;
params->cost_fraction[0]=0;
for(size_t i =1;i<CHANNEL_PARTS;++i)
{
params->cap_fraction[i]=CHANNEL_PIVOTS[i]-CHANNEL_PIVOTS[i-1];
params->cost_fraction[i]=
log((1-CHANNEL_PIVOTS[i-1])/(1-CHANNEL_PIVOTS[i]))
/params->cap_fraction[i];
}
params->max_fee = max_fee;
params->min_probability = min_probability;
params->delay_feefactor = delay_feefactor;
params->base_fee_penalty = base_fee_penalty;
params->prob_cost_factor = prob_cost_factor;
params->base_probability = base_probability;
struct linear_network *linear_network= init_linear_network(this_ctx, params, &errmsg);
if (!linear_network) {
if(fail)
*fail = tal_fmt(ctx, "init_linear_network failed: %s",
errmsg);
goto function_fail;
}
struct residual_network *residual_network =
alloc_residual_network(this_ctx, linear_network->max_num_nodes,
linear_network->max_num_arcs);
if (!residual_network) {
if (fail)
*fail = tal_fmt(
ctx, "failed to allocate the residual network");
goto function_fail;
}
dijkstra = dijkstra_new(this_ctx, gossmap_max_node_idx(params->gossmap));
const u32 target_idx = gossmap_node_idx(params->gossmap,target);
const u32 source_idx = gossmap_node_idx(params->gossmap,source);
init_residual_network(linear_network,residual_network);
struct amount_msat best_fee;
double best_prob_success;
const u64 pay_amount_msats = params->amount.millisatoshis % 1000;
const u64 pay_amount_sats = params->amount.millisatoshis/1000
+ (pay_amount_msats ? 1 : 0);
const struct amount_msat excess
= amount_msat(pay_amount_msats ? 1000 - pay_amount_msats : 0);
if (!find_feasible_flow(this_ctx, linear_network, residual_network,
source_idx, target_idx, pay_amount_sats,
&errmsg)) {
if(fail)
*fail = tal_fmt(ctx, "failed to find a feasible flow: %s", errmsg);
goto function_fail;
}
best_flow_paths = get_flow_paths(
this_ctx, params->gossmap, params->disabled, params->chan_extra_map,
linear_network, residual_network, excess, params->base_probability,
&errmsg);
if (!best_flow_paths) {
if (fail)
*fail =
tal_fmt(ctx, "get_flow_paths failed: %s", errmsg);
goto function_fail;
}
best_flow_paths = tal_steal(ctx, best_flow_paths);
best_prob_success =
flowset_probability(this_ctx, best_flow_paths, params->gossmap,
params->chan_extra_map, false, &errmsg)
* pow(params->base_probability, flowset_size(best_flow_paths));
if (best_prob_success < 0) {
if (fail)
*fail = tal_fmt(
ctx,
"flowset_probability failed on MaxFlow phase: %s",
errmsg);
goto function_fail;
}
if (!flowset_fee(&best_fee, best_flow_paths)) {
if (fail)
*fail =
tal_fmt(ctx, "flowset_fee failed on MaxFlow phase");
goto function_fail;
}
s64 mu_left = 0, mu_right = MU_MAX;
while(mu_left<mu_right)
{
s64 mu = (mu_left + mu_right)/2;
combine_cost_function(linear_network,residual_network,mu);
if(!optimize_mcf(this_ctx, dijkstra,linear_network,residual_network,
source_idx,target_idx,pay_amount_sats, &errmsg))
{
if (fail)
*fail =
tal_fmt(ctx, "optimize_mcf failed: %s", errmsg);
goto function_fail;
}
struct flow **flow_paths;
flow_paths =
get_flow_paths(this_ctx, params->gossmap, params->disabled,
params->chan_extra_map, linear_network,
residual_network, excess, params->base_probability,
&errmsg);
if(!flow_paths)
{
if (fail)
*fail =
tal_fmt(ctx, "get_flow_paths failed: %s", errmsg);
goto function_fail;
}
double prob_success =
flowset_probability(this_ctx, flow_paths, params->gossmap,
params->chan_extra_map, false, &errmsg)
* pow(params->base_probability, flowset_size(flow_paths));
if (prob_success < 0) {
if (fail)
*fail =
tal_fmt(ctx, "flowset_probability: %s", errmsg);
goto function_fail;
}
struct amount_msat fee;
if (!flowset_fee(&fee, flow_paths)) {
if (fail)
*fail =
tal_fmt(ctx, "flowset_fee failed evaluating MinCostFlow candidate");
goto function_fail;
}
if(!best_flow_paths ||
is_better(params->max_fee,params->min_probability,
fee,prob_success,
best_fee, best_prob_success))
{
best_flow_paths = tal_free(best_flow_paths);
best_flow_paths = tal_steal(ctx,flow_paths);
best_fee = fee;
best_prob_success=prob_success;
flow_paths = NULL;
}
else
tal_free(flow_paths);
if(amount_msat_greater(fee,params->max_fee))
{
mu_left = mu+1;
}else if(prob_success < params->min_probability)
{
mu_right = mu;
}else
{
mu_left = mu+1;
}
}
tal_free(this_ctx);
return best_flow_paths;
function_fail:
tal_free(this_ctx);
return tal_free(best_flow_paths);
}