diff --git a/doc/lightning-invoice.7.md b/doc/lightning-invoice.7.md index 36fc45572..77d445fdc 100644 --- a/doc/lightning-invoice.7.md +++ b/doc/lightning-invoice.7.md @@ -54,7 +54,7 @@ If specified, *exposeprivatechannels* overrides the default route hint 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 +(e.g. *1x1x3*) or array of short channel ids (or a remote alias), only those specific channels 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/lightningd/routehint.c b/lightningd/routehint.c index ce15ee27c..f6c5fb960 100644 --- a/lightningd/routehint.c +++ b/lightningd/routehint.c @@ -171,7 +171,10 @@ routehint_candidates(const tal_t *ctx, /* Consider only hints they gave */ if (hints) { log_debug(ld->log, "We have hints!"); - if (!scid_in_arr(hints, &r->short_channel_id)) { + /* Allow specification by alias, too */ + if (!scid_in_arr(hints, &r->short_channel_id) + && (!candidate.c->alias[REMOTE] + || !scid_in_arr(hints, candidate.c->alias[REMOTE]))) { log_debug(ld->log, "scid %s not in hints", type_to_string(tmpctx, struct short_channel_id, @@ -204,6 +207,22 @@ routehint_candidates(const tal_t *ctx, continue; } + /* BOLT-channel-type #2: + * - if `channel_type` has `option_scid_alias` set: + * - MUST NOT use the real `short_channel_id` in + * BOLT 11 `r` fields. + */ + /* FIXME: We don't remember the type explicitly, so + * we just assume all private channels negotiated since + * we had alias support want this. */ + + /* Note explicit flag test here: if we're told to expose all + * private channels, then "is_public" is forced true */ + if (!(candidate.c->channel_flags & CHANNEL_FLAGS_ANNOUNCE_CHANNEL) + && candidate.c->alias[REMOTE]) { + r->short_channel_id = *candidate.c->alias[REMOTE]; + } + /* OK, finish it and append to one of the arrays. */ if (is_public) { log_debug(ld->log, "%s: added to public", diff --git a/tests/test_invoices.py b/tests/test_invoices.py index 9cfd63ade..84315bc42 100644 --- a/tests/test_invoices.py +++ b/tests/test_invoices.py @@ -226,6 +226,7 @@ def test_invoice_routeboost_private(node_factory, bitcoind): # Make sure channel is totally public. wait_for(lambda: [c['public'] for c in l2.rpc.listchannels(scid_dummy)['channels']] == [True, True]) + alias = only_one(only_one(l1.rpc.listpeers(l2.info['id'])['peers'])['channels'])['alias']['local'] # Since there's only one route, it will reluctantly hint that even # though it's private inv = l2.rpc.invoice(amount_msat=123456, label="inv0", description="?") @@ -237,7 +238,9 @@ def test_invoice_routeboost_private(node_factory, bitcoind): # 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'] + # It uses our private alias! + assert r['short_channel_id'] != l1.rpc.listchannels()['channels'][0]['short_channel_id'] + assert r['short_channel_id'] == alias assert r['fee_base_msat'] == 1 assert r['fee_proportional_millionths'] == 10 assert r['cltv_expiry_delta'] == 6 @@ -261,7 +264,7 @@ def test_invoice_routeboost_private(node_factory, bitcoind): # 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['short_channel_id'] == alias assert r['fee_base_msat'] == 1 assert r['fee_proportional_millionths'] == 10 assert r['cltv_expiry_delta'] == 6 @@ -276,7 +279,7 @@ def test_invoice_routeboost_private(node_factory, bitcoind): # 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['short_channel_id'] == alias assert r['fee_base_msat'] == 1 assert r['fee_proportional_millionths'] == 10 assert r['cltv_expiry_delta'] == 6 @@ -308,7 +311,7 @@ def test_invoice_routeboost_private(node_factory, bitcoind): # 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['short_channel_id'] == alias assert r['fee_base_msat'] == 1 assert r['fee_proportional_millionths'] == 10 assert r['cltv_expiry_delta'] == 6 @@ -322,7 +325,7 @@ def test_invoice_routeboost_private(node_factory, bitcoind): # 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'] == scid + assert r['short_channel_id'] == alias assert r['fee_base_msat'] == 1 assert r['fee_proportional_millionths'] == 10 assert r['cltv_expiry_delta'] == 6 @@ -345,7 +348,7 @@ def test_invoice_routeboost_private(node_factory, bitcoind): # 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'] == scid + assert r['short_channel_id'] == alias assert r['fee_base_msat'] == 1 assert r['fee_proportional_millionths'] == 10 assert r['cltv_expiry_delta'] == 6 @@ -365,7 +368,7 @@ def test_invoice_routeboost_private(node_factory, bitcoind): # 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['short_channel_id'] == alias assert r['fee_base_msat'] == 1 assert r['fee_proportional_millionths'] == 10 assert r['cltv_expiry_delta'] == 6 diff --git a/tests/test_pay.py b/tests/test_pay.py index eb15416ee..a35f2193f 100644 --- a/tests/test_pay.py +++ b/tests/test_pay.py @@ -5062,7 +5062,8 @@ gives a routehint straight to us causes an issue inv = l3.rpc.invoice(10, "test", "test")['bolt11'] decoded = l3.rpc.decodepay(inv) - assert(only_one(only_one(decoded['routes']))['short_channel_id'] == scid23) + assert(only_one(only_one(decoded['routes']))['short_channel_id'] + == only_one(only_one(l3.rpc.listpeers()['peers'])['channels'])['alias']['remote']) l3.stop() with pytest.raises(RpcError, match=r'Destination .* is not reachable directly and all routehints were unusable'):