routeboost: expose private channel in invoice iff we have no public ones.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
Rusty Russell 2019-01-15 14:26:27 +10:30 committed by Christian Decker
parent f321b1d35f
commit 547d6ab878
3 changed files with 46 additions and 15 deletions

View File

@ -40,6 +40,7 @@ This release named by @molxyz and [@ctrlbreak](https://twitter.com/ctrlbreak).
- JSON API: use `\n\n` to terminate responses, for simplified parsing (pylightning now relies on this)
- JSON API: `fundchannel` now includes an `announce` option, when false it will keep channel private. Defaults to true.
- JSON API: `listpeers`'s `channels` now includes a `private` flag to indicate if channel is announced or not.
- JSON API: `invoice` route hints may now include private channels if you have no public ones.
- Plugins: experimental plugin support for `lightningd`, including option passthrough and JSON-RPC passthrough.
### Changed

View File

@ -2114,7 +2114,8 @@ static struct io_plan *get_incoming_channels(struct io_conn *conn,
const u8 *msg)
{
struct node *node;
struct route_info *r = tal_arr(tmpctx, struct route_info, 0);
struct route_info *public = tal_arr(tmpctx, struct route_info, 0);
struct route_info *private = tal_arr(tmpctx, struct route_info, 0);
if (!fromwire_gossip_get_incoming_channels(msg))
master_badmsg(WIRE_GOSSIP_GET_INCOMING_CHANNELS, msg);
@ -2126,10 +2127,6 @@ static struct io_plan *get_incoming_channels(struct io_conn *conn,
const struct half_chan *hc;
struct route_info ri;
/* Don't leak private channels. */
if (!is_chan_public(c))
continue;
hc = &c->half[half_chan_to(node, c)];
if (!is_halfchan_enabled(hc))
@ -2140,11 +2137,19 @@ static struct io_plan *get_incoming_channels(struct io_conn *conn,
ri.fee_base_msat = hc->base_fee;
ri.fee_proportional_millionths = hc->proportional_fee;
ri.cltv_expiry_delta = hc->delay;
tal_arr_expand(&r, ri);
if (is_chan_public(c))
tal_arr_expand(&public, ri);
else
tal_arr_expand(&private, ri);
}
}
msg = towire_gossip_get_incoming_channels_reply(NULL, r);
/* If no public channels, share private ones. */
if (tal_count(public) == 0)
msg = towire_gossip_get_incoming_channels_reply(NULL, private);
else
msg = towire_gossip_get_incoming_channels_reply(NULL, public);
daemon_conn_send(daemon->master, take(msg));
return daemon_conn_read_next(conn, daemon->master);

View File

@ -120,14 +120,7 @@ def test_invoice_preimage(node_factory):
def test_invoice_routeboost(node_factory, bitcoind):
"""Test routeboost 'r' hint in bolt11 invoice.
"""
l1, l2 = node_factory.line_graph(2, fundamount=2 * (10**4))
# Won't get reference to route until channel is public.
inv = l2.rpc.invoice(msatoshi=123456, label="inv0", description="?")
assert 'warning_capacity' in inv
bitcoind.generate_block(5)
wait_for(lambda: [c['public'] for c in l2.rpc.listchannels()['channels']] == [True, True])
l1, l2 = node_factory.line_graph(2, fundamount=2 * (10**4), wait_for_announce=True)
# Check routeboost.
# Make invoice and pay it
@ -162,6 +155,38 @@ def test_invoice_routeboost(node_factory, bitcoind):
assert 'warning_offline' in inv
def test_invoice_routeboost_private(node_factory, bitcoind):
"""Test routeboost 'r' hint in bolt11 invoice for private channels
"""
l1, l2 = node_factory.line_graph(2, fundamount=10**6, announce_channels=False)
# Since there's only one route, it will reluctantly hint that even
# though it's private
inv = l2.rpc.invoice(msatoshi=123456, label="inv0", description="?")
assert 'warning_capacity' not in inv
assert 'warning_offline' 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
# The existence of a public channel, even without capacity, will suppress
# the exposure of private channels.
l3 = node_factory.get_node()
l3.rpc.connect(l2.info['id'], 'localhost', l2.port)
scid = l3.fund_channel(l2, 2 * (10**4))
bitcoind.generate_block(5)
# Make sure channel is totally public.
wait_for(lambda: [c['public'] for c in l3.rpc.listchannels(scid)['channels']] == [True, True])
inv = l2.rpc.invoice(msatoshi=123456000, label="inv1", description="?")
assert 'warning_capacity' in inv
def test_invoice_expiry(node_factory, executor):
l1, l2 = node_factory.line_graph(2, fundchannel=True)