From 22a70339debd4032708030ee9ff516ea43406445 Mon Sep 17 00:00:00 2001 From: Lagrang3 Date: Wed, 27 Mar 2024 13:02:16 +0100 Subject: [PATCH] renepay: limit arc capacities by htlc_max In the Min. Cost Flow solver we put a constraint on arcs capacities, such that the total flow that can be allocated through a channel does not exceed htlc_max. This solves two previous problem of the htlc_max constraint at the MCF level. --- plugins/renepay/mcf.c | 9 +++++++- plugins/renepay/pay.c | 4 ++-- tests/test_renepay.py | 50 +++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 58 insertions(+), 5 deletions(-) diff --git a/plugins/renepay/mcf.c b/plugins/renepay/mcf.c index 8537b2cae8e7..deea4d66c5d0 100644 --- a/plugins/renepay/mcf.c +++ b/plugins/renepay/mcf.c @@ -483,11 +483,18 @@ static bool linearize_channel(const struct pay_parameters *params, a = MAX(a,0); b = MAX(a+1,b); + /* An extra bound on capacity, here we use it to reduce the flow such + * that it does not exceed htlcmax. */ + s64 cap_on_capacity = + channel_htlc_max(c, dir).millisatoshis/1000; /* Raw: linearize_channel */ + capacity[0]=a; cost[0]=0; for(size_t i=1;icap_fraction[i]*(b-a); + 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] *params->amount.millisatoshis /* Raw: linearize_channel */ diff --git a/plugins/renepay/pay.c b/plugins/renepay/pay.c index 3eb0ea4a17ce..c0b62398b667 100644 --- a/plugins/renepay/pay.c +++ b/plugins/renepay/pay.c @@ -483,9 +483,9 @@ static void gossmod_cb(struct gossmap_localmods *mods, } /* FIXME: features? */ - gossmap_local_addchan(mods, self, peer, &scidd->scid, NULL); + gossmap_local_addchan(mods, self, peer, scidd->scid, NULL); - gossmap_local_updatechan(mods, &scidd->scid, min, max, + gossmap_local_updatechan(mods, scidd->scid, min, max, fee_base.millisatoshis, /* Raw: gossmap */ fee_proportional, cltv_delta, diff --git a/tests/test_renepay.py b/tests/test_renepay.py index d223a039ebbd..99f654a9a859 100644 --- a/tests/test_renepay.py +++ b/tests/test_renepay.py @@ -489,12 +489,12 @@ def test_htlc_max(node_factory): ] ) - inv = l6.rpc.invoice("1800000sat", "inv", "description") + inv = l6.rpc.invoice("800000sat", "inv", "description") l1.rpc.call("renepay", {"invstring": inv["bolt11"]}) l1.wait_for_htlcs() invoice = only_one(l6.rpc.listinvoices("inv")["invoices"]) - assert invoice["amount_received_msat"] >= Millisatoshi("1800000sat") + assert invoice["amount_received_msat"] >= Millisatoshi("800000sat") def test_previous_sendpays(node_factory, bitcoind): @@ -635,3 +635,49 @@ def test_local_htlcmax0(node_factory): assert details["status"] == "complete" assert details["amount_msat"] == Millisatoshi(123000) assert details["destination"] == l3.info["id"] + + +def test_htlcmax0(node_factory): + """ + Topology: + 1----2----4 + | | + 3----5----6 + Tests the plugin when some routes have htlc_max=0. + """ + opts = [ + {"disable-mpp": None, "fee-base": 0, "fee-per-satoshi": 0}, + {"disable-mpp": None, "fee-base": 0, "fee-per-satoshi": 0}, + {"disable-mpp": None, "fee-base": 0, "fee-per-satoshi": 0}, + { + "disable-mpp": None, + "fee-base": 0, + "fee-per-satoshi": 0, + "htlc-maximum-msat": 0, + }, + { + "disable-mpp": None, + "fee-base": 0, + "fee-per-satoshi": 0, + "htlc-maximum-msat": 800000000, + }, + {"disable-mpp": None, "fee-base": 0, "fee-per-satoshi": 0}, + ] + l1, l2, l3, l4, l5, l6 = node_factory.get_nodes(6, opts=opts) + start_channels( + [ + (l1, l2, 10000000), + (l2, l4, 1000000), + (l4, l6, 1000000), + (l1, l3, 10000000), + (l3, l5, 1000000), + (l5, l6, 1000000), + ] + ) + + inv = l6.rpc.invoice("600000sat", "inv", "description") + + l1.rpc.call("renepay", {"invstring": inv["bolt11"]}) + l1.wait_for_htlcs() + invoice = only_one(l6.rpc.listinvoices("inv")["invoices"]) + assert invoice["amount_received_msat"] >= Millisatoshi("600000sat")