Skip to content

Pay: take channel capacity into account for route selection #4771

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion common/dijkstra.c
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ dijkstra_(const tal_t *ctx,
u64 (*path_score)(u32 distance,
struct amount_msat cost,
struct amount_msat risk,
int dir,
const struct gossmap_chan *c),
void *arg)
{
Expand Down Expand Up @@ -246,7 +247,7 @@ dijkstra_(const tal_t *ctx,
risk = risk_price(cost, riskfactor,
cur_d->total_delay
+ c->half[!which_half].delay);
score = path_score(cur_d->distance + 1, cost, risk, c);
score = path_score(cur_d->distance + 1, cost, risk, !which_half, c);
if (score >= d->score)
continue;

Expand Down
1 change: 1 addition & 0 deletions common/dijkstra.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ dijkstra_(const tal_t *ctx,
u64 (*path_score)(u32 distance,
struct amount_msat cost,
struct amount_msat risk,
int dir,
const struct gossmap_chan *c),
void *arg);

Expand Down
2 changes: 2 additions & 0 deletions common/route.c
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ static u32 costs_to_score(struct amount_msat cost,
u64 route_score_shorter(u32 distance,
struct amount_msat cost,
struct amount_msat risk,
int dir UNUSED,
const struct gossmap_chan *c UNUSED)
{
return costs_to_score(cost, risk) + ((u64)distance << 32);
Expand All @@ -53,6 +54,7 @@ u64 route_score_shorter(u32 distance,
u64 route_score_cheaper(u32 distance,
struct amount_msat cost,
struct amount_msat risk,
int dir UNUSED,
const struct gossmap_chan *c UNUSED)
{
return ((u64)costs_to_score(cost, risk) << 32) + distance;
Expand Down
2 changes: 2 additions & 0 deletions common/route.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,12 +57,14 @@ bool route_can_carry_even_disabled(const struct gossmap *map,
u64 route_score_shorter(u32 distance,
struct amount_msat cost,
struct amount_msat risk,
int dir UNUSED,
const struct gossmap_chan *c UNUSED);

/* Cheapest path, with shorter path tiebreak */
u64 route_score_cheaper(u32 distance,
struct amount_msat cost,
struct amount_msat risk,
int dir UNUSED,
const struct gossmap_chan *c UNUSED);

/* Extract route tal_arr from completed dijkstra: NULL if none. */
Expand Down
47 changes: 45 additions & 2 deletions plugins/libplugin-pay.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <common/random_select.h>
#include <common/type_to_string.h>
#include <errno.h>
#include <math.h>
#include <plugins/libplugin-pay.h>
#include <sys/types.h>
#include <wire/peer_wire.h>
Expand Down Expand Up @@ -695,6 +696,48 @@ static bool payment_route_can_carry_even_disabled(const struct gossmap *map,
return payment_route_check(map, c, dir, amount, p);
}

/* Rene Pickhardt:
*
* Btw the linear term of the Taylor series of -log((c+1-x)/(c+1)) is 1/(c+1)
* meaning that another suitable Weight for Dijkstra would be amt/(c+1) +
* \mu*fee(amt) which is the linearized version which for small amounts and
* suitable value of \mu should be good enough)
*/
static u64 capacity_bias(const struct gossmap *map,
const struct gossmap_chan *c,
int dir,
struct amount_msat amount)
{
struct amount_sat capacity;
u64 capmsat, amtmsat = amount.millisatoshis; /* Raw: lengthy math */

/* Can fail in theory if gossmap changed underneath. */
if (!gossmap_chan_get_capacity(map, c, &capacity))
return 0;

capmsat = capacity.satoshis * 1000; /* Raw: lengthy math */
return -log((capmsat + 1 - amtmsat) / (capmsat + 1));
}

/* Prioritize costs over distance, but bias to larger channels. */
static u64 route_score(u32 distance,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: maybe rename to cost instead of score just to be closer to terms used in the literature (c.f. min-cost-flow instead of minimum score flow)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think channel_cost or cost_function would be a better name also at the point where you make the dijsktra call

struct amount_msat cost,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: here the same this cost I would actually name fees as the fees are just a feature in the final cost function.

struct amount_msat risk,
int dir,
const struct gossmap_chan *c)
{
u64 cmsat = cost.millisatoshis; /* Raw: lengthy math */
u64 rmsat = risk.millisatoshis; /* Raw: lengthy math */
u64 bias = capacity_bias(global_gossmap, c, dir, cost);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understand that historically this was thought of as a capacity bias but I would actually for future redability rename this to negative_uniform_logprob


/* Smoothed harmonic mean to avoid division by 0 */
u64 costs = (cmsat * rmsat * bias) / (cmsat + rmsat + bias + 1);

if (costs > 0xFFFFFFFF)
costs = 0xFFFFFFFF;
return costs;
}

static struct route_hop *route(const tal_t *ctx,
struct gossmap *gossmap,
const struct gossmap_node *src,
Expand All @@ -716,14 +759,14 @@ static struct route_hop *route(const tal_t *ctx,

can_carry = payment_route_can_carry;
dij = dijkstra(tmpctx, gossmap, dst, amount, riskfactor,
can_carry, route_score_cheaper, p);
can_carry, route_score, p);
r = route_from_dijkstra(ctx, gossmap, dij, src, amount, final_delay);
if (!r) {
/* Try using disabled channels too */
/* FIXME: is there somewhere we can annotate this for paystatus? */
can_carry = payment_route_can_carry_even_disabled;
dij = dijkstra(ctx, gossmap, dst, amount, riskfactor,
can_carry, route_score_cheaper, p);
can_carry, route_score, p);
r = route_from_dijkstra(ctx, gossmap, dij, src,
amount, final_delay);
if (!r) {
Expand Down
11 changes: 5 additions & 6 deletions plugins/test/run-route-overlong.c
Original file line number Diff line number Diff line change
Expand Up @@ -332,7 +332,6 @@ int main(void)
int store_fd;
struct payment *p;
struct payment_modifier **mods;
struct gossmap *gossmap;
char gossip_version = GOSSIP_STORE_VERSION;
char gossipfilename[] = "/tmp/run-route-overlong.XXXXXX";

Expand All @@ -344,7 +343,7 @@ int main(void)
assert(write(store_fd, &gossip_version, sizeof(gossip_version))
== sizeof(gossip_version));

gossmap = gossmap_load(tmpctx, gossipfilename, NULL);
global_gossmap = gossmap_load(tmpctx, gossipfilename, NULL);

for (size_t i = 0; i < NUM_NODES; i++) {
struct privkey tmp;
Expand Down Expand Up @@ -383,7 +382,7 @@ int main(void)
1 << i);
}

assert(gossmap_refresh(gossmap, NULL));
assert(gossmap_refresh(global_gossmap, NULL));
for (size_t i = ROUTING_MAX_HOPS; i > 2; i--) {
struct gossmap_node *dst, *src;
struct route_hop *r;
Expand All @@ -392,9 +391,9 @@ int main(void)
type_to_string(tmpctx, struct node_id, &ids[0]),
type_to_string(tmpctx, struct node_id, &ids[NUM_NODES-1]));

src = gossmap_find_node(gossmap, &ids[0]);
dst = gossmap_find_node(gossmap, &ids[NUM_NODES-1]);
r = route(tmpctx, gossmap, src, dst, AMOUNT_MSAT(1000), 0, 0.0,
src = gossmap_find_node(global_gossmap, &ids[0]);
dst = gossmap_find_node(global_gossmap, &ids[NUM_NODES-1]);
r = route(tmpctx, global_gossmap, src, dst, AMOUNT_MSAT(1000), 0, 0.0,
i - 1, p, &errmsg);
assert(r);
/* FIXME: We naively fall back on shortest, rather
Expand Down
1 change: 1 addition & 0 deletions plugins/topology.c
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ struct exclude_entry {
static u64 route_score_fuzz(u32 distance,
struct amount_msat cost,
struct amount_msat risk,
int dir UNUSED,
const struct gossmap_chan *c)
{
u64 costs = cost.millisatoshis + risk.millisatoshis; /* Raw: score */
Expand Down