Skip to content

Commit bdcb9f6

Browse files
rustyrussellcdecker
authored andcommitted
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 <[email protected]> Changelog-Changed: Plugins: `pay` now biases towards larger channels, improving success probability.
1 parent 347967d commit bdcb9f6

File tree

2 files changed

+53
-8
lines changed

2 files changed

+53
-8
lines changed

plugins/libplugin-pay.c

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -695,6 +695,52 @@ static bool payment_route_can_carry_even_disabled(const struct gossmap *map,
695695
return payment_route_check(map, c, dir, amount, p);
696696
}
697697

698+
/* Rene Pickhardt:
699+
*
700+
* Btw the linear term of the Taylor series of -log((c+1-x)/(c+1)) is 1/(c+1)
701+
* meaning that another suitable Weight for Dijkstra would be amt/(c+1) +
702+
* \mu*fee(amt) which is the linearized version which for small amounts and
703+
* suitable value of \mu should be good enough)
704+
*/
705+
static u64 capacity_bias(const struct gossmap *map,
706+
const struct gossmap_chan *c,
707+
int dir,
708+
struct amount_msat amount)
709+
{
710+
struct amount_msat fee;
711+
struct amount_sat capacity;
712+
713+
/* Median fees are 1000 base, 10 ppm, so scale capacity bias to that */
714+
/* Overflow is pretty-much impossible, so ignore. */
715+
if (!amount_msat_fee(&fee, amount, 1000, 10))
716+
return 0;
717+
718+
/* Can fail in theory if gossmap changed underneath. */
719+
if (!gossmap_chan_get_capacity(map, c, &capacity))
720+
return 0;
721+
722+
/* bias = fee * (amt / (c + 1)) */
723+
return fee.millisatoshis /* Raw: complex math & laziness */
724+
* amount.millisatoshis /* Raw: complex math & laziness */
725+
/ (capacity.satoshis*1000 + 1); /* Raw: complex math & laziness */
726+
}
727+
728+
/* Prioritize costs over distance, but bias to larger channels. */
729+
static u64 route_score(u32 distance,
730+
struct amount_msat cost,
731+
struct amount_msat risk,
732+
int dir,
733+
const struct gossmap_chan *c)
734+
{
735+
u64 costs = cost.millisatoshis + risk.millisatoshis /* Raw: score */
736+
/* We use global_gossmap (can't still be NULL)
737+
* *without* get_gossmap() which might change topology. */
738+
+ capacity_bias(global_gossmap, c, dir, cost);
739+
if (costs > 0xFFFFFFFF)
740+
costs = 0xFFFFFFFF;
741+
return costs;
742+
}
743+
698744
static struct route_hop *route(const tal_t *ctx,
699745
struct gossmap *gossmap,
700746
const struct gossmap_node *src,
@@ -716,14 +762,14 @@ static struct route_hop *route(const tal_t *ctx,
716762

717763
can_carry = payment_route_can_carry;
718764
dij = dijkstra(tmpctx, gossmap, dst, amount, riskfactor,
719-
can_carry, route_score_cheaper, p);
765+
can_carry, route_score, p);
720766
r = route_from_dijkstra(ctx, gossmap, dij, src, amount, final_delay);
721767
if (!r) {
722768
/* Try using disabled channels too */
723769
/* FIXME: is there somewhere we can annotate this for paystatus? */
724770
can_carry = payment_route_can_carry_even_disabled;
725771
dij = dijkstra(ctx, gossmap, dst, amount, riskfactor,
726-
can_carry, route_score_cheaper, p);
772+
can_carry, route_score, p);
727773
r = route_from_dijkstra(ctx, gossmap, dij, src,
728774
amount, final_delay);
729775
if (!r) {

plugins/test/run-route-overlong.c

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -332,7 +332,6 @@ int main(void)
332332
int store_fd;
333333
struct payment *p;
334334
struct payment_modifier **mods;
335-
struct gossmap *gossmap;
336335
char gossip_version = GOSSIP_STORE_VERSION;
337336
char gossipfilename[] = "/tmp/run-route-overlong.XXXXXX";
338337

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

347-
gossmap = gossmap_load(tmpctx, gossipfilename, NULL);
346+
global_gossmap = gossmap_load(tmpctx, gossipfilename, NULL);
348347

349348
for (size_t i = 0; i < NUM_NODES; i++) {
350349
struct privkey tmp;
@@ -383,7 +382,7 @@ int main(void)
383382
1 << i);
384383
}
385384

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

395-
src = gossmap_find_node(gossmap, &ids[0]);
396-
dst = gossmap_find_node(gossmap, &ids[NUM_NODES-1]);
397-
r = route(tmpctx, gossmap, src, dst, AMOUNT_MSAT(1000), 0, 0.0,
394+
src = gossmap_find_node(global_gossmap, &ids[0]);
395+
dst = gossmap_find_node(global_gossmap, &ids[NUM_NODES-1]);
396+
r = route(tmpctx, global_gossmap, src, dst, AMOUNT_MSAT(1000), 0, 0.0,
398397
i - 1, p, &errmsg);
399398
assert(r);
400399
/* FIXME: We naively fall back on shortest, rather

0 commit comments

Comments
 (0)