From 347967d0ea0292ef7fa8715b2d6afc977f9863a0 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 23 Sep 2021 22:36:09 +0930 Subject: [PATCH 1/3] common/dijkstra: hand channel direction to path_score callback. Signed-off-by: Rusty Russell --- common/dijkstra.c | 3 ++- common/dijkstra.h | 1 + common/route.c | 2 ++ common/route.h | 2 ++ plugins/topology.c | 1 + 5 files changed, 8 insertions(+), 1 deletion(-) diff --git a/common/dijkstra.c b/common/dijkstra.c index 31551a0db509..e4d9c0ead84b 100644 --- a/common/dijkstra.c +++ b/common/dijkstra.c @@ -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) { @@ -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; diff --git a/common/dijkstra.h b/common/dijkstra.h index 3672c59a81eb..788182cd66c9 100644 --- a/common/dijkstra.h +++ b/common/dijkstra.h @@ -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); diff --git a/common/route.c b/common/route.c index a24b26da4327..f204518b7e3c 100644 --- a/common/route.c +++ b/common/route.c @@ -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); @@ -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; diff --git a/common/route.h b/common/route.h index 5528d5390c14..1fc474234da3 100644 --- a/common/route.h +++ b/common/route.h @@ -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. */ diff --git a/plugins/topology.c b/plugins/topology.c index 11462fc22543..3d30582d621b 100644 --- a/plugins/topology.c +++ b/plugins/topology.c @@ -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 */ From bdcb9f6ddcc2756b769f8ffb8dc412531b6bff79 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 23 Sep 2021 22:37:09 +0930 Subject: [PATCH 2/3] libplugin-pay: bias towards larger channels. We bias by channel linearly by capacity, scaled by median fee. This means that we effectively double the fee if we would use the entire capacity, and only increase it by 50% if we would only use 1/2 the capacity. This should drive us towards larger channels. Signed-off-by: Rusty Russell Changelog-Changed: Plugins: `pay` now biases towards larger channels, improving success probability. --- plugins/libplugin-pay.c | 50 +++++++++++++++++++++++++++++-- plugins/test/run-route-overlong.c | 11 ++++--- 2 files changed, 53 insertions(+), 8 deletions(-) diff --git a/plugins/libplugin-pay.c b/plugins/libplugin-pay.c index ecbe072b38c1..fc33b85b0458 100644 --- a/plugins/libplugin-pay.c +++ b/plugins/libplugin-pay.c @@ -695,6 +695,52 @@ 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_msat fee; + struct amount_sat capacity; + + /* Median fees are 1000 base, 10 ppm, so scale capacity bias to that */ + /* Overflow is pretty-much impossible, so ignore. */ + if (!amount_msat_fee(&fee, amount, 1000, 10)) + return 0; + + /* Can fail in theory if gossmap changed underneath. */ + if (!gossmap_chan_get_capacity(map, c, &capacity)) + return 0; + + /* bias = fee * (amt / (c + 1)) */ + return fee.millisatoshis /* Raw: complex math & laziness */ + * amount.millisatoshis /* Raw: complex math & laziness */ + / (capacity.satoshis*1000 + 1); /* Raw: complex math & laziness */ +} + +/* Prioritize costs over distance, but bias to larger channels. */ +static u64 route_score(u32 distance, + struct amount_msat cost, + struct amount_msat risk, + int dir, + const struct gossmap_chan *c) +{ + u64 costs = cost.millisatoshis + risk.millisatoshis /* Raw: score */ + /* We use global_gossmap (can't still be NULL) + * *without* get_gossmap() which might change topology. */ + + capacity_bias(global_gossmap, c, dir, cost); + if (costs > 0xFFFFFFFF) + costs = 0xFFFFFFFF; + return costs; +} + static struct route_hop *route(const tal_t *ctx, struct gossmap *gossmap, const struct gossmap_node *src, @@ -716,14 +762,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) { diff --git a/plugins/test/run-route-overlong.c b/plugins/test/run-route-overlong.c index 8d24b8f16fb6..b11c647369cc 100644 --- a/plugins/test/run-route-overlong.c +++ b/plugins/test/run-route-overlong.c @@ -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"; @@ -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; @@ -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; @@ -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 From 8378ed28c89915effce1ec9590dd0e54b87911a5 Mon Sep 17 00:00:00 2001 From: Rene Pickhardt Date: Mon, 4 Oct 2021 15:30:29 +0200 Subject: [PATCH 3/3] pay: Use log probability based bias in channel selection Changelog-Changed: pay: The route selection will now use the log-propability-based channel selection to increase success rate and reduce time to completion --- plugins/libplugin-pay.c | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/plugins/libplugin-pay.c b/plugins/libplugin-pay.c index fc33b85b0458..01626e664daa 100644 --- a/plugins/libplugin-pay.c +++ b/plugins/libplugin-pay.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -707,22 +708,15 @@ static u64 capacity_bias(const struct gossmap *map, int dir, struct amount_msat amount) { - struct amount_msat fee; struct amount_sat capacity; - - /* Median fees are 1000 base, 10 ppm, so scale capacity bias to that */ - /* Overflow is pretty-much impossible, so ignore. */ - if (!amount_msat_fee(&fee, amount, 1000, 10)) - return 0; + 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; - /* bias = fee * (amt / (c + 1)) */ - return fee.millisatoshis /* Raw: complex math & laziness */ - * amount.millisatoshis /* Raw: complex math & laziness */ - / (capacity.satoshis*1000 + 1); /* Raw: complex math & laziness */ + capmsat = capacity.satoshis * 1000; /* Raw: lengthy math */ + return -log((capmsat + 1 - amtmsat) / (capmsat + 1)); } /* Prioritize costs over distance, but bias to larger channels. */ @@ -732,10 +726,13 @@ static u64 route_score(u32 distance, int dir, const struct gossmap_chan *c) { - u64 costs = cost.millisatoshis + risk.millisatoshis /* Raw: score */ - /* We use global_gossmap (can't still be NULL) - * *without* get_gossmap() which might change topology. */ - + capacity_bias(global_gossmap, c, dir, cost); + u64 cmsat = cost.millisatoshis; /* Raw: lengthy math */ + u64 rmsat = risk.millisatoshis; /* Raw: lengthy math */ + u64 bias = capacity_bias(global_gossmap, c, dir, cost); + + /* Smoothed harmonic mean to avoid division by 0 */ + u64 costs = (cmsat * rmsat * bias) / (cmsat + rmsat + bias + 1); + if (costs > 0xFFFFFFFF) costs = 0xFFFFFFFF; return costs;