json-invoice: add routeboost, warnings.

We split json_invoice(), as it now needs to round-trip to the gossipd,
and uniqueness checks need to happen *after* gossipd replies to avoid
a race.

For every candidate channel gossipd gives us, we check that it's in
state NORMAL (not shutting down, not still waiting for lockin), that
it's connected, and that it has capacity.  We then choose one with
probability weighted by excess capacity, so larger channels are more
likely.

As a side effect of this, we can tell if an invoice is unpayble (no
channels have sufficient incoming capacity) or difficuly (no *online*
channels have sufficient capacity), so we add those warnings.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
Rusty Russell
2018-09-27 14:59:17 +09:30
committed by Christian Decker
parent 2f667c5227
commit c8c3294a23
9 changed files with 886 additions and 77 deletions

View File

@@ -1,6 +1,6 @@
from fixtures import * # noqa: F401,F403
from lightning import RpcError
from utils import only_one, DEVELOPER
from utils import only_one, DEVELOPER, wait_for, wait_channel_quiescent
import pytest
@@ -29,6 +29,9 @@ def test_invoice(node_factory):
assert b11['fallbacks'][0]['type'] == 'P2WPKH'
assert b11['fallbacks'][1]['addr'] == addr2
assert b11['fallbacks'][1]['type'] == 'P2SH'
# There's no incoming channel, so no routeboost
assert 'routes' not in b11
assert 'warning_capacity' in inv
# Check pay_index is null
outputs = l1.db_query('SELECT pay_index IS NULL AS q FROM invoices WHERE label="label";')
@@ -45,6 +48,9 @@ def test_invoice(node_factory):
# to match the "lnbcrt1" above with the '1' digit as the
# separator, and not for example "lnbcrt1m1....".
assert b11.count('1') == 1
# There's no incoming channel, so no routeboost
assert 'routes' not in b11
assert 'warning_capacity' in inv
def test_invoice_weirdstring(node_factory):
@@ -111,6 +117,46 @@ def test_invoice_preimage(node_factory):
l2.rpc.invoice(123456, 'inv2', '?', preimage=invoice_preimage)
def test_invoice_routeboost(node_factory):
"""Test routeboost 'r' hint in bolt11 invoice.
"""
l1, l2 = node_factory.line_graph(2, announce=True, fundamount=10**4)
# Make sure we've seen channel_update from peer.
wait_for(lambda: [a['active'] for a in l2.rpc.listchannels()['channels']] == [True, True])
# Make invoice and pay it
inv = l2.rpc.invoice(msatoshi=123456, label="inv1", description="?")
# Check routeboost.
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
# Pay it (and make sure it's fully resolved before we take l1 offline!)
l1.rpc.pay(inv['bolt11'])
wait_channel_quiescent(l1, l2)
# Due to reserve, l1 doesn't have capacity to pay this.
inv = l2.rpc.invoice(msatoshi=10**7 - 123456, label="inv2", description="?")
# Check warning
assert 'warning_capacity' in inv
assert 'warning_offline' not in inv
l1.stop()
wait_for(lambda: not only_one(l2.rpc.listpeers(l1.info['id'])['peers'])['connected'])
inv = l2.rpc.invoice(123456, label="inv3", description="?")
# Check warning.
assert 'warning_capacity' not in inv
assert 'warning_offline' in inv
def test_invoice_expiry(node_factory, executor):
l1, l2 = node_factory.line_graph(2, fundchannel=True)