diff --git a/contrib/pyln-client/pyln/client/lightning.py b/contrib/pyln-client/pyln/client/lightning.py index 394a27052..cbbc8a858 100644 --- a/contrib/pyln-client/pyln/client/lightning.py +++ b/contrib/pyln-client/pyln/client/lightning.py @@ -916,10 +916,16 @@ class LightningRpc(UnixDomainSocketRpc): } return self.call("listconfigs", payload) - def listforwards(self): - """List all forwarded payments and their information. + def listforwards(self, status=None, in_channel=None, out_channel=None): + """List all forwarded payments and their information matching + forward {status}, {in_channel} and {out_channel}. """ - return self.call("listforwards") + payload = { + "status": status, + "in_channel": in_channel, + "out_channel": out_channel, + } + return self.call("listforwards", payload) def listfunds(self, spent=False): """ diff --git a/tests/test_misc.py b/tests/test_misc.py index 9a2f3ed04..d6a3e02ea 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -2448,3 +2448,62 @@ def test_listfunds(node_factory): # 1 spent output (channel opening) and 1 unspent output assert len(all_outputs) == 2 assert open_txid in txids + + +def test_listforwards(node_factory, bitcoind): + """Test listfunds command.""" + l1, l2, l3, l4 = node_factory.get_nodes(4, opts=[{}, {}, {}, {}]) + + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) + l2.rpc.connect(l3.info['id'], 'localhost', l3.port) + l2.rpc.connect(l4.info['id'], 'localhost', l4.port) + + c12, _ = l1.fundchannel(l2, 10**5) + c23, _ = l2.fundchannel(l3, 10**5) + c24, _ = l2.fundchannel(l4, 10**5) + + # Wait until channels are active + bitcoind.generate_block(5) + l1.wait_channel_active(c23) + + # successful payments + i31 = l3.rpc.invoice(1000, 'i31', 'desc') + l1.rpc.pay(i31['bolt11']) + + i41 = l4.rpc.invoice(2000, 'i41', 'desc') + l1.rpc.pay(i41['bolt11']) + + # failed payment + failed_payment_hash = l3.rpc.invoice(4000, 'failed', 'desc')['payment_hash'] + failed_route = l1.rpc.getroute(l3.info['id'], 4000, 1)['route'] + + l2.rpc.close(c23, 1) + + with pytest.raises(RpcError): + l1.rpc.sendpay(failed_route, failed_payment_hash) + l1.rpc.waitsendpay(failed_payment_hash) + + all_forwards = l2.rpc.listforwards()['forwards'] + print(json.dumps(all_forwards, indent=True)) + + assert len(all_forwards) == 3 + assert i31['payment_hash'] in map(lambda x: x['payment_hash'], all_forwards) + assert i41['payment_hash'] in map(lambda x: x['payment_hash'], all_forwards) + assert failed_payment_hash in map(lambda x: x['payment_hash'], all_forwards) + + # status=settled + settled_forwards = l2.rpc.listforwards(status='settled')['forwards'] + assert len(settled_forwards) == 2 + assert sum(x['out_msatoshi'] for x in settled_forwards) == 3000 + + # status=local_failed + failed_forwards = l2.rpc.listforwards(status='local_failed')['forwards'] + assert len(failed_forwards) == 1 + + # in_channel=c23 + c23_forwards = l2.rpc.listforwards(in_channel=c23, status='settled')['forwards'] + assert len(c23_forwards) == 0 + + # out_channel=c24 + c24_forwards = l2.rpc.listforwards(out_channel=c24)['forwards'] + assert len(c24_forwards) == 1