From df31af522160cad1187c9f9d42d32a68b1cbee40 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 8 Apr 2020 17:10:58 +0930 Subject: [PATCH] invoice: ignore dead-end heuristic on explicitly specified channels. This makes testing easier, and makes sense: lightningd might not *know* about other connected channels, depending on gossip, but if the user specifies it we should obey it. Signed-off-by: Rusty Russell Changelog-Changed: JSON: `invoice` `exposeprivatechannels` now includes explicitly named channels even if they seem like dead-ends. --- doc/lightning-invoice.7 | 2 +- doc/lightning-invoice.7.md | 2 +- lightningd/invoice.c | 6 +++++- tests/test_invoices.py | 26 ++++++++++++++++++++++---- 4 files changed, 29 insertions(+), 7 deletions(-) diff --git a/doc/lightning-invoice.7 b/doc/lightning-invoice.7 index f8464d98a..1a2d6c8e2 100644 --- a/doc/lightning-invoice.7 +++ b/doc/lightning-invoice.7 @@ -61,7 +61,7 @@ logic, which will use unpublished channels only if there are no published channels\. If \fItrue\fR unpublished channels are always considered as a route hint candidate; if \fIfalse\fR, never\. If it is a short channel id (e\.g\. \fI1x1x3\fR) or array of short channel ids, only those specific channels -will be considered candidates, even if they are public\. +will be considered candidates, even if they are public or dead-ends\. The route hint is selected from the set of incoming channels of which: diff --git a/doc/lightning-invoice.7.md b/doc/lightning-invoice.7.md index ddddbdca2..8721ddeef 100644 --- a/doc/lightning-invoice.7.md +++ b/doc/lightning-invoice.7.md @@ -56,7 +56,7 @@ logic, which will use unpublished channels only if there are no published channels. If *true* unpublished channels are always considered as a route hint candidate; if *false*, never. If it is a short channel id (e.g. *1x1x3*) or array of short channel ids, only those specific channels -will be considered candidates, even if they are public. +will be considered candidates, even if they are public or dead-ends. The route hint is selected from the set of incoming channels of which: peer’s balance minus their reserves is at least *msatoshi*, state is diff --git a/lightningd/invoice.c b/lightningd/invoice.c index 43153e256..dcb4ca73b 100644 --- a/lightningd/invoice.c +++ b/lightningd/invoice.c @@ -640,7 +640,11 @@ static void gossipd_incoming_channels_reply(struct subd *gossipd, &inchans[i].short_channel_id)) { tal_arr_remove(&inchans, i); tal_arr_remove(&inchan_deadends, i); - } + i--; + } else + /* If they specify directly, we don't + * care if it's a deadend */ + inchan_deadends[i] = false; } /* If they told us to use scids and we couldn't, fail. */ diff --git a/tests/test_invoices.py b/tests/test_invoices.py index 36ecd8d2a..6068a1ea5 100644 --- a/tests/test_invoices.py +++ b/tests/test_invoices.py @@ -284,13 +284,13 @@ def test_invoice_routeboost_private(node_factory, bitcoind): assert r['cltv_expiry_delta'] == 6 # Ask it explicitly to use a channel it can't (insufficient capacity) - inv = l2.rpc.invoice(msatoshi=10, label="inv5", description="?", exposeprivatechannels=scid2) - assert 'warning_deadends' in inv - assert 'warning_capacity' not in inv + inv = l2.rpc.invoice(msatoshi=(10**5) * 1000 + 1, label="inv5", description="?", exposeprivatechannels=scid2) + assert 'warning_deadends' not in inv + assert 'warning_capacity' in inv assert 'warning_offline' not in inv # Give it two options and it will pick one with suff capacity. - inv = l2.rpc.invoice(msatoshi=10, label="inv6", description="?", exposeprivatechannels=[scid2, scid]) + inv = l2.rpc.invoice(msatoshi=(10**5) * 1000 + 1, label="inv6", description="?", exposeprivatechannels=[scid2, scid]) assert 'warning_capacity' not in inv assert 'warning_offline' not in inv assert 'warning_deadends' not in inv @@ -302,6 +302,24 @@ def test_invoice_routeboost_private(node_factory, bitcoind): assert r['fee_proportional_millionths'] == 10 assert r['cltv_expiry_delta'] == 6 + # It will use an explicit exposeprivatechannels even if it thinks its a dead-end + l0.rpc.close(l1.info['id']) + l0.wait_for_channel_onchain(l1.info['id']) + bitcoind.generate_block(1) + wait_for(lambda: l2.rpc.listchannels(scid_dummy)['channels'] == []) + + inv = l2.rpc.invoice(msatoshi=123456, label="inv7", description="?", exposeprivatechannels=scid) + assert 'warning_capacity' not in inv + assert 'warning_offline' not in inv + assert 'warning_deadends' not in inv + # Route array has single route with single element. + r = only_one(only_one(l1.rpc.decodepay(inv['bolt11'])['routes'])) + assert r['pubkey'] == l1.info['id'] + assert r['short_channel_id'] == l1.rpc.listchannels()['channels'][0]['short_channel_id'] + assert r['fee_base_msat'] == 1 + assert r['fee_proportional_millionths'] == 10 + assert r['cltv_expiry_delta'] == 6 + def test_invoice_expiry(node_factory, executor): l1, l2 = node_factory.line_graph(2, fundchannel=True)