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/libplugin-pay.c b/plugins/libplugin-pay.c index ecbe072b38c1..01626e664daa 100644 --- a/plugins/libplugin-pay.c +++ b/plugins/libplugin-pay.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -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, + struct amount_msat cost, + 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); + + /* 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, @@ -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) { 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 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 */