diff --git a/tests/test_pay.py b/tests/test_pay.py index 7ad1792d4..af2e68b53 100644 --- a/tests/test_pay.py +++ b/tests/test_pay.py @@ -2218,7 +2218,7 @@ def test_channel_spendable(node_factory, bitcoind): payment_hash = l1.rpc.invoice('any', 'inv', 'for testing')['payment_hash'] amount = l2.rpc.listpeers()['peers'][0]['channels'][0]['spendable_msat'] - # Turns out we this won't route, as it's over max - reserve: + # Turns out we won't route this, as it's over max - reserve: route = l2.rpc.getroute(l1.info['id'], amount + 1, riskfactor=1, fuzzpercent=0)['route'] l2.rpc.sendpay(route, payment_hash) @@ -2237,6 +2237,59 @@ def test_channel_spendable(node_factory, bitcoind): l2.rpc.waitsendpay(payment_hash, TIMEOUT) +@unittest.skipIf(not DEVELOPER, "gossip without DEVELOPER=1 is slow") +def test_channel_receivable(node_factory, bitcoind): + """Test that receivable_msat is accurate""" + sats = 10**6 + l1, l2 = node_factory.line_graph(2, fundamount=sats, wait_for_announce=True, + opts={'plugin': os.path.join(os.getcwd(), 'tests/plugins/hold_invoice.py'), 'holdtime': str(TIMEOUT / 2)}) + + payment_hash = l2.rpc.invoice('any', 'inv', 'for testing')['payment_hash'] + + # We should be able to receive this much, and not one msat more! + amount = l2.rpc.listpeers()['peers'][0]['channels'][0]['receivable_msat'] + route = l1.rpc.getroute(l2.info['id'], amount + 1, riskfactor=1, fuzzpercent=0)['route'] + l1.rpc.sendpay(route, payment_hash) + + # This should fail locally with "capacity exceeded" + with pytest.raises(RpcError, match=r"Capacity exceeded.*'erring_index': 0"): + l1.rpc.waitsendpay(payment_hash, TIMEOUT) + + # Exact amount should succeed. + route = l1.rpc.getroute(l2.info['id'], amount, riskfactor=1, fuzzpercent=0)['route'] + l1.rpc.sendpay(route, payment_hash) + + # Amount should drop to 0 once HTLC is sent; we have time, thanks to + # hold_invoice.py plugin. + wait_for(lambda: len(l2.rpc.listpeers()['peers'][0]['channels'][0]['htlcs']) == 1) + assert l2.rpc.listpeers()['peers'][0]['channels'][0]['receivable_msat'] == Millisatoshi(0) + l1.rpc.waitsendpay(payment_hash, TIMEOUT) + + # Make sure l2 thinks it's all over. + wait_for(lambda: len(l2.rpc.listpeers()['peers'][0]['channels'][0]['htlcs']) == 0) + # Now, reverse should work similarly. + payment_hash = l1.rpc.invoice('any', 'inv', 'for testing')['payment_hash'] + amount = l1.rpc.listpeers()['peers'][0]['channels'][0]['receivable_msat'] + + # Turns out we won't route this, as it's over max - reserve: + route = l2.rpc.getroute(l1.info['id'], amount + 1, riskfactor=1, fuzzpercent=0)['route'] + l2.rpc.sendpay(route, payment_hash) + + # This should fail locally with "capacity exceeded" + with pytest.raises(RpcError, match=r"Capacity exceeded.*'erring_index': 0"): + l2.rpc.waitsendpay(payment_hash, TIMEOUT) + + # Exact amount should succeed. + route = l2.rpc.getroute(l1.info['id'], amount, riskfactor=1, fuzzpercent=0)['route'] + l2.rpc.sendpay(route, payment_hash) + + # Amount should drop to 0 once HTLC is sent; we have time, thanks to + # hold_invoice.py plugin. + wait_for(lambda: len(l1.rpc.listpeers()['peers'][0]['channels'][0]['htlcs']) == 1) + assert l1.rpc.listpeers()['peers'][0]['channels'][0]['receivable_msat'] == Millisatoshi(0) + l2.rpc.waitsendpay(payment_hash, TIMEOUT) + + @unittest.skipIf(not DEVELOPER, "gossip without DEVELOPER=1 is slow") def test_channel_spendable_large(node_factory, bitcoind): """Test that spendable_msat is accurate for large channels""" @@ -2248,26 +2301,30 @@ def test_channel_spendable_large(node_factory, bitcoind): payment_hash = l2.rpc.invoice('any', 'inv', 'for testing')['payment_hash'] # We should be able to spend this much, and not one msat more! - amount = l1.rpc.listpeers()['peers'][0]['channels'][0]['spendable_msat'] + spendable = l1.rpc.listpeers()['peers'][0]['channels'][0]['spendable_msat'] + + # receivable from the other side should calculate to the exact same amount + receivable = l2.rpc.listpeers()['peers'][0]['channels'][0]['receivable_msat'] + assert spendable == receivable # route or waitsendpay fill fail. with pytest.raises(RpcError): - route = l1.rpc.getroute(l2.info['id'], amount + 1, riskfactor=1, fuzzpercent=0)['route'] + route = l1.rpc.getroute(l2.info['id'], spendable + 1, riskfactor=1, fuzzpercent=0)['route'] l1.rpc.sendpay(route, payment_hash) l1.rpc.waitsendpay(payment_hash, TIMEOUT) - print(l2.rpc.listchannels()) # Exact amount should succeed. - route = l1.rpc.getroute(l2.info['id'], amount, riskfactor=1, fuzzpercent=0)['route'] + route = l1.rpc.getroute(l2.info['id'], spendable, riskfactor=1, fuzzpercent=0)['route'] l1.rpc.sendpay(route, payment_hash) l1.rpc.waitsendpay(payment_hash, TIMEOUT) -def test_channel_spendable_capped(node_factory, bitcoind): - """Test that spendable_msat is capped at 2^32-1""" +def test_channel_spendable_receivable_capped(node_factory, bitcoind): + """Test that spendable_msat and receivable_msat is capped at 2^32-1""" sats = 16777215 l1, l2 = node_factory.line_graph(2, fundamount=sats, wait_for_announce=False) assert l1.rpc.listpeers()['peers'][0]['channels'][0]['spendable_msat'] == Millisatoshi(0xFFFFFFFF) + assert l2.rpc.listpeers()['peers'][0]['channels'][0]['receivable_msat'] == Millisatoshi(0xFFFFFFFF) def test_lockup_drain(node_factory, bitcoind):