From 661412e52b0d805e9d647e166ec673866a6f1efd Mon Sep 17 00:00:00 2001 From: Lagrang3 Date: Wed, 4 Sep 2024 15:19:07 +0100 Subject: [PATCH] askrene: fixup Changelog-EXPERIMENTAL: fixup to askrene to compute fees on routes correctly. Signed-off-by: Lagrang3 --- plugins/askrene/askrene.c | 2 +- tests/test_askrene.py | 125 ++++++++++++++++++++++++++++++++++---- 2 files changed, 114 insertions(+), 13 deletions(-) diff --git a/plugins/askrene/askrene.c b/plugins/askrene/askrene.c index 0952680f60c8..1cf51020eb4e 100644 --- a/plugins/askrene/askrene.c +++ b/plugins/askrene/askrene.c @@ -382,6 +382,7 @@ static const char *get_routes(const tal_t *ctx, struct gossmap_node *far_end; const struct half_chan *h = flow_edge(flows[i], j); + rh->amount = msat; if (!amount_msat_add_fee(&msat, h->base_fee, h->proportional_fee)) plugin_err(plugin, "Adding fee to amount"); delay += h->delay; @@ -390,7 +391,6 @@ static const char *get_routes(const tal_t *ctx, rh->direction = flows[i]->dirs[j]; far_end = gossmap_nth_node(rq->gossmap, flows[i]->path[j], !flows[i]->dirs[j]); gossmap_node_get_id(rq->gossmap, far_end, &rh->node_id); - rh->amount = msat; rh->delay = delay; } (*amounts)[i] = flow_delivers(flows[i]); diff --git a/tests/test_askrene.py b/tests/test_askrene.py index e68fe1831339..1ec9364db95c 100644 --- a/tests/test_askrene.py +++ b/tests/test_askrene.py @@ -176,7 +176,7 @@ def test_getroutes(node_factory): 'path': [{'short_channel_id': '0x1x0', 'direction': 1, 'next_node_id': nodemap[1], - 'amount_msat': 1010, + 'amount_msat': 1000, 'delay': 99 + 6}]}]} # Two hop, still easy. assert l1.rpc.getroutes(source=nodemap[0], @@ -191,12 +191,12 @@ def test_getroutes(node_factory): 'path': [{'short_channel_id': '0x1x0', 'direction': 1, 'next_node_id': nodemap[1], - 'amount_msat': 103020, + 'amount_msat': 102000, 'delay': 99 + 6 + 6}, {'short_channel_id': '1x3x2', 'direction': 1, 'next_node_id': nodemap[3], - 'amount_msat': 102000, + 'amount_msat': 100000, 'delay': 99 + 6} ]}]} @@ -237,7 +237,7 @@ def test_getroutes(node_factory): 'path': [{'short_channel_id': '0x2x3', 'direction': 1, 'next_node_id': nodemap[2], - 'amount_msat': 1000001, + 'amount_msat': 1000000, 'delay': 99 + 6}]}]} # For 10000 sats, we will split. @@ -251,7 +251,7 @@ def test_getroutes(node_factory): 'delay': 99 + 6}], [{'short_channel_id': '0x2x3', 'next_node_id': nodemap[2], - 'amount_msat': 9495009, + 'amount_msat': 9495000, 'delay': 99 + 6}]]) @@ -340,7 +340,7 @@ def test_getroutes_auto_sourcefree(node_factory): {'short_channel_id': '1x3x2', 'direction': 1, 'next_node_id': nodemap[3], - 'amount_msat': 102000, + 'amount_msat': 100000, 'delay': 99 + 6} ]}]} @@ -401,9 +401,9 @@ def test_getroutes_auto_localchans(node_factory): 100000, maxfee_msat=100000, layers=['auto.localchans'], - paths=[[{'short_channel_id': scid12, 'amount_msat': 102012, 'delay': 99 + 6 + 6 + 6}, - {'short_channel_id': '0x1x0', 'amount_msat': 102010, 'delay': 99 + 6 + 6}, - {'short_channel_id': '1x2x1', 'amount_msat': 101000, 'delay': 99 + 6}]]) + paths=[[{'short_channel_id': scid12, 'amount_msat': 102010, 'delay': 99 + 6 + 6 + 6}, + {'short_channel_id': '0x1x0', 'amount_msat': 101000, 'delay': 99 + 6 + 6}, + {'short_channel_id': '1x2x1', 'amount_msat': 100000, 'delay': 99 + 6}]]) # This should get self-discount correct check_getroute_paths(l1, @@ -412,9 +412,9 @@ def test_getroutes_auto_localchans(node_factory): 100000, maxfee_msat=100000, layers=['auto.localchans', 'auto.sourcefree'], - paths=[[{'short_channel_id': scid12, 'amount_msat': 102010, 'delay': 99 + 6 + 6}, - {'short_channel_id': '0x1x0', 'amount_msat': 102010, 'delay': 99 + 6 + 6}, - {'short_channel_id': '1x2x1', 'amount_msat': 101000, 'delay': 99 + 6}]]) + paths=[[{'short_channel_id': scid12, 'amount_msat': 102010, 'delay': 99 + 6 + 6}, + {'short_channel_id': '0x1x0', 'amount_msat': 101000, 'delay': 99 + 6 + 6}, + {'short_channel_id': '1x2x1', 'amount_msat': 100000, 'delay': 99 + 6}]]) def test_fees_dont_exceed_constraints(node_factory): @@ -500,3 +500,104 @@ def test_live_spendable(node_factory, bitcoind): exceeded[scidd] = f"Path total {path_total[scidd]} > spendable {maxes[scidd]}" assert exceeded == {} + + +def test_forwarding_fees(node_factory): + """Tests if getroutes gets the fees right.""" + + def fees(amt, propfee, basefee): + return basefee + (amt * propfee) // 1000000 + + l1 = node_factory.get_node(start=False) + + basefee = 7 + propfee = 10000 + msat = 123000 + capacity = 1000000 + # 0 has to use two paths (1 and 2) to reach 3. But we tell it 0->1 has limited capacity. + gsfile, nodemap = generate_gossip_store( + [ + GenChannel( + 0, + 1, + capacity_sats=capacity, + forward=GenChannel.Half(propfee=propfee, basefee=basefee), + ), + GenChannel( + 1, + 2, + capacity_sats=capacity, + forward=GenChannel.Half(propfee=propfee, basefee=basefee), + ), + GenChannel( + 2, + 3, + capacity_sats=capacity, + forward=GenChannel.Half(propfee=propfee, basefee=basefee), + ), + ] + ) + + # Set up l1 with this as the gossip_store + shutil.copy( + gsfile.name, os.path.join(l1.daemon.lightning_dir, TEST_NETWORK, "gossip_store") + ) + l1.start() + + chan = only_one( + [ + c + for c in l1.rpc.listchannels(source=nodemap[0])["channels"] + if c["destination"] == nodemap[1] + ] + ) + + routes = l1.rpc.getroutes( + source=nodemap[0], + destination=nodemap[3], + amount_msat=msat, + layers=["test_layers"], + maxfee_msat=msat, + final_cltv=99, + )["routes"] + assert len(routes) == 1 + assert len(routes[0]["path"]) == 3 + assert routes[0]["path"][-1]["amount_msat"] >= msat + msat = routes[0]["path"][-1]["amount_msat"] + msat = msat + fees(msat, propfee, basefee) + assert routes[0]["path"][-2]["amount_msat"] >= msat + msat = routes[0]["path"][-2]["amount_msat"] + msat = msat + fees(msat, propfee, basefee) + assert routes[0]["path"][-3]["amount_msat"] >= msat + + +def test_forwarding_fees2(node_factory): + """Make sure we use correct delay and fees for the direction we're going.""" + + def fees(amt, propfee, basefee): + return basefee + (amt * propfee) // 1000000 + + l1, l2, l3 = node_factory.line_graph( + 3, + wait_for_announce=True, + opts=[ + {}, + {"fee-base": 2000, "fee-per-satoshi": 20, "cltv-delta": 20}, + {"fee-base": 3000, "fee-per-satoshi": 30, "cltv-delta": 30}, + ], + ) + msat = 123000 + routes = l1.rpc.getroutes( + source=l1.info["id"], + destination=l3.info["id"], + layers=["auto.localchans"], + maxfee_msat=msat, + amount_msat=msat, + final_cltv=99, + )["routes"] + assert len(routes) == 1 + assert len(routes[0]["path"]) == 2 + assert routes[0]["path"][-1]["amount_msat"] >= msat + msat = routes[0]["path"][-1]["amount_msat"] + msat = msat + fees(msat, 20, 2000) + assert routes[0]["path"][-2]["amount_msat"] >= msat