mirror of
https://github.com/aljazceru/lightning.git
synced 2026-02-23 15:04:19 +01:00
lightningd: add listhtlcs to list all the HTLCs we know about.
Using `listfowards` for this wrong; expose this directly if people care (and unlike listforwards, which could be deleted, we have to remember these while the channel is still open!). Signed-off-by: Rusty Russell <rusty@rustcorp.com.au> Changelog-Added: JSON-RPC: `listhtlcs` new command to list all known HTLCS.
This commit is contained in:
committed by
Christian Decker
parent
311807ff1f
commit
7420a7021f
@@ -3992,12 +3992,13 @@ def test_multichan(node_factory, executor, bitcoind):
|
||||
# Now fund *second* channel l2->l3 (slightly larger)
|
||||
bitcoind.rpc.sendtoaddress(l2.rpc.newaddr()['bech32'], 0.1)
|
||||
bitcoind.generate_block(1)
|
||||
sync_blockheight(bitcoind, [l2])
|
||||
sync_blockheight(bitcoind, [l1, l2, l3])
|
||||
l2.rpc.fundchannel(l3.info['id'], '0.01001btc')
|
||||
assert(len(only_one(l2.rpc.listpeers(l3.info['id'])['peers'])['channels']) == 2)
|
||||
assert(len(only_one(l3.rpc.listpeers(l2.info['id'])['peers'])['channels']) == 2)
|
||||
|
||||
bitcoind.generate_block(1, wait_for_mempool=1)
|
||||
sync_blockheight(bitcoind, [l1, l2, l3])
|
||||
# Make sure new channel is also CHANNELD_NORMAL
|
||||
wait_for(lambda: [c['state'] for c in only_one(l2.rpc.listpeers(l3.info['id'])['peers'])['channels']] == ["CHANNELD_NORMAL", "CHANNELD_NORMAL"])
|
||||
|
||||
@@ -4023,9 +4024,9 @@ def test_multichan(node_factory, executor, bitcoind):
|
||||
'delay': 5,
|
||||
'channel': scid23a}]
|
||||
before = only_one(l2.rpc.listpeers(l3.info['id'])['peers'])['channels']
|
||||
inv = l3.rpc.invoice(100000000, "invoice", "invoice")
|
||||
l1.rpc.sendpay(route, inv['payment_hash'], payment_secret=inv['payment_secret'])
|
||||
l1.rpc.waitsendpay(inv['payment_hash'])
|
||||
inv1 = l3.rpc.invoice(100000000, "invoice", "invoice")
|
||||
l1.rpc.sendpay(route, inv1['payment_hash'], payment_secret=inv1['payment_secret'])
|
||||
l1.rpc.waitsendpay(inv1['payment_hash'])
|
||||
# Wait until HTLCs fully settled
|
||||
wait_for(lambda: [c['htlcs'] for c in only_one(l2.rpc.listpeers(l3.info['id'])['peers'])['channels']] == [[], []])
|
||||
after = only_one(l2.rpc.listpeers(l3.info['id'])['peers'])['channels']
|
||||
@@ -4049,9 +4050,9 @@ def test_multichan(node_factory, executor, bitcoind):
|
||||
|
||||
before = only_one(l2.rpc.listpeers(l3.info['id'])['peers'])['channels']
|
||||
route[1]['channel'] = scid23b
|
||||
inv = l3.rpc.invoice(100000000, "invoice2", "invoice2")
|
||||
l1.rpc.sendpay(route, inv['payment_hash'], payment_secret=inv['payment_secret'])
|
||||
l1.rpc.waitsendpay(inv['payment_hash'])
|
||||
inv2 = l3.rpc.invoice(100000000, "invoice2", "invoice2")
|
||||
l1.rpc.sendpay(route, inv2['payment_hash'], payment_secret=inv2['payment_secret'])
|
||||
l1.rpc.waitsendpay(inv2['payment_hash'])
|
||||
# Wait until HTLCs fully settled
|
||||
wait_for(lambda: [c['htlcs'] for c in only_one(l2.rpc.listpeers(l3.info['id'])['peers'])['channels']] == [[], []])
|
||||
after = only_one(l2.rpc.listpeers(l3.info['id'])['peers'])['channels']
|
||||
@@ -4062,6 +4063,7 @@ def test_multichan(node_factory, executor, bitcoind):
|
||||
|
||||
# Make sure gossip works.
|
||||
bitcoind.generate_block(5)
|
||||
sync_blockheight(bitcoind, [l1, l2, l3])
|
||||
|
||||
wait_for(lambda: len(l1.rpc.listchannels(source=l3.info['id'])['channels']) == 2)
|
||||
|
||||
@@ -4084,6 +4086,7 @@ def test_multichan(node_factory, executor, bitcoind):
|
||||
|
||||
l2.rpc.close(scid23b)
|
||||
bitcoind.generate_block(1, wait_for_mempool=1)
|
||||
sync_blockheight(bitcoind, [l1, l2, l3])
|
||||
|
||||
# Gossip works as expected.
|
||||
wait_for(lambda: len(l1.rpc.listchannels(source=l3.info['id'])['channels']) == 1)
|
||||
@@ -4091,9 +4094,9 @@ def test_multichan(node_factory, executor, bitcoind):
|
||||
|
||||
# We can actually pay by *closed* scid (at least until it's completely forgotten)
|
||||
route[1]['channel'] = scid23a
|
||||
inv = l3.rpc.invoice(100000000, "invoice3", "invoice3")
|
||||
l1.rpc.sendpay(route, inv['payment_hash'], payment_secret=inv['payment_secret'])
|
||||
l1.rpc.waitsendpay(inv['payment_hash'])
|
||||
inv3 = l3.rpc.invoice(100000000, "invoice3", "invoice3")
|
||||
l1.rpc.sendpay(route, inv3['payment_hash'], payment_secret=inv3['payment_secret'])
|
||||
l1.rpc.waitsendpay(inv3['payment_hash'])
|
||||
|
||||
# Restart with multiple channels works.
|
||||
l3.restart()
|
||||
@@ -4103,8 +4106,48 @@ def test_multichan(node_factory, executor, bitcoind):
|
||||
except RpcError:
|
||||
wait_for(lambda: only_one(l3.rpc.listpeers(l2.info['id'])['peers'])['connected'])
|
||||
|
||||
inv = l3.rpc.invoice(100000000, "invoice4", "invoice4")
|
||||
l1.rpc.pay(inv['bolt11'])
|
||||
inv4 = l3.rpc.invoice(100000000, "invoice4", "invoice4")
|
||||
l1.rpc.pay(inv4['bolt11'])
|
||||
|
||||
# A good place to test listhtlcs!
|
||||
wait_for(lambda: all([h['state'] == 'RCVD_REMOVE_ACK_REVOCATION' for h in l1.rpc.listhtlcs()['htlcs']]))
|
||||
|
||||
l1htlcs = l1.rpc.listhtlcs()['htlcs']
|
||||
assert l1htlcs == l1.rpc.listhtlcs(scid12)['htlcs']
|
||||
assert l1htlcs == [{"short_channel_id": scid12,
|
||||
"id": 0,
|
||||
"expiry": 117,
|
||||
"direction": "out",
|
||||
"amount_msat": Millisatoshi(100001001),
|
||||
"payment_hash": inv1['payment_hash'],
|
||||
"state": "RCVD_REMOVE_ACK_REVOCATION"},
|
||||
{"short_channel_id": scid12,
|
||||
"id": 1,
|
||||
"expiry": 117,
|
||||
"direction": "out",
|
||||
"amount_msat": Millisatoshi(100001001),
|
||||
"payment_hash": inv2['payment_hash'],
|
||||
"state": "RCVD_REMOVE_ACK_REVOCATION"},
|
||||
{"short_channel_id": scid12,
|
||||
"id": 2,
|
||||
"expiry": 123,
|
||||
"direction": "out",
|
||||
"amount_msat": Millisatoshi(100001001),
|
||||
"payment_hash": inv3['payment_hash'],
|
||||
"state": "RCVD_REMOVE_ACK_REVOCATION"},
|
||||
{"short_channel_id": scid12,
|
||||
"id": 3,
|
||||
"expiry": 123,
|
||||
"direction": "out",
|
||||
"amount_msat": Millisatoshi(100001001),
|
||||
"payment_hash": inv4['payment_hash'],
|
||||
"state": "RCVD_REMOVE_ACK_REVOCATION"}]
|
||||
|
||||
# Reverse direction, should match l2's view of channel.
|
||||
for h in l1htlcs:
|
||||
h['direction'] = 'in'
|
||||
h['state'] = 'SENT_REMOVE_ACK_REVOCATION'
|
||||
assert l2.rpc.listhtlcs(scid12)['htlcs'] == l1htlcs
|
||||
|
||||
|
||||
@pytest.mark.developer("dev-no-reconnect required")
|
||||
|
||||
@@ -2,7 +2,7 @@ from bitcoin.rpc import RawProxy
|
||||
from decimal import Decimal
|
||||
from fixtures import * # noqa: F401,F403
|
||||
from fixtures import LightningNode, TEST_NETWORK
|
||||
from pyln.client import RpcError
|
||||
from pyln.client import RpcError, Millisatoshi
|
||||
from threading import Event
|
||||
from pyln.testing.utils import (
|
||||
DEVELOPER, TIMEOUT, VALGRIND, DEPRECATED_APIS, sync_blockheight, only_one,
|
||||
@@ -2379,15 +2379,15 @@ def test_listfunds(node_factory):
|
||||
assert open_txid in txids
|
||||
|
||||
|
||||
def test_listforwards(node_factory, bitcoind):
|
||||
"""Test listfunds command."""
|
||||
def test_listforwards_and_listhtlcs(node_factory, bitcoind):
|
||||
"""Test listforwards and listhtlcs commands."""
|
||||
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)
|
||||
c12, c12res = l1.fundchannel(l2, 10**5)
|
||||
c23, _ = l2.fundchannel(l3, 10**5)
|
||||
c24, _ = l2.fundchannel(l4, 10**5)
|
||||
|
||||
@@ -2406,7 +2406,7 @@ def test_listforwards(node_factory, bitcoind):
|
||||
failed_inv = l3.rpc.invoice(4000, 'failed', 'desc')
|
||||
failed_route = l1.rpc.getroute(l3.info['id'], 4000, 1)['route']
|
||||
|
||||
l2.rpc.close(c23, 1)
|
||||
l2.rpc.close(c23)
|
||||
|
||||
with pytest.raises(RpcError):
|
||||
l1.rpc.sendpay(failed_route, failed_inv['payment_hash'], payment_secret=failed_inv['payment_secret'])
|
||||
@@ -2447,6 +2447,52 @@ def test_listforwards(node_factory, bitcoind):
|
||||
c24_forwards = l2.rpc.listforwards(out_channel=c24)['forwards']
|
||||
assert len(c24_forwards) == 1
|
||||
|
||||
# listhtlcs on l1 is the same with or without id specifiers
|
||||
c1htlcs = l1.rpc.listhtlcs()['htlcs']
|
||||
assert l1.rpc.listhtlcs(c12)['htlcs'] == c1htlcs
|
||||
assert l1.rpc.listhtlcs(c12res['channel_id'])['htlcs'] == c1htlcs
|
||||
c1htlcs.sort(key=lambda h: h['id'])
|
||||
assert [h['id'] for h in c1htlcs] == [0, 1, 2]
|
||||
assert [h['short_channel_id'] for h in c1htlcs] == [c12] * 3
|
||||
assert [h['amount_msat'] for h in c1htlcs] == [Millisatoshi(1001),
|
||||
Millisatoshi(2001),
|
||||
Millisatoshi(4001)]
|
||||
assert [h['direction'] for h in c1htlcs] == ['out'] * 3
|
||||
assert [h['state'] for h in c1htlcs] == ['RCVD_REMOVE_ACK_REVOCATION'] * 3
|
||||
|
||||
# These should be a mirror!
|
||||
c2c1htlcs = l2.rpc.listhtlcs(c12)['htlcs']
|
||||
for h in c2c1htlcs:
|
||||
assert h['state'] == 'SENT_REMOVE_ACK_REVOCATION'
|
||||
assert h['direction'] == 'in'
|
||||
h['state'] = 'RCVD_REMOVE_ACK_REVOCATION'
|
||||
h['direction'] = 'out'
|
||||
assert c2c1htlcs == c1htlcs
|
||||
|
||||
# One channel at a time should result in all htlcs.
|
||||
allhtlcs = l2.rpc.listhtlcs()['htlcs']
|
||||
parthtlcs = (l2.rpc.listhtlcs(c12)['htlcs']
|
||||
+ l2.rpc.listhtlcs(c23)['htlcs']
|
||||
+ l2.rpc.listhtlcs(c24)['htlcs'])
|
||||
assert len(allhtlcs) == len(parthtlcs)
|
||||
for h in allhtlcs:
|
||||
assert h in parthtlcs
|
||||
|
||||
# Now, close and forget.
|
||||
l2.rpc.close(c24)
|
||||
l2.rpc.close(c12)
|
||||
|
||||
bitcoind.generate_block(100, wait_for_mempool=3)
|
||||
|
||||
# Once channels are gone, htlcs are gone.
|
||||
for n in (l1, l2, l3, l4):
|
||||
# They might reconnect, but still will have no channels
|
||||
wait_for(lambda: all(p['channels'] == [] for p in n.rpc.listpeers()['peers']))
|
||||
assert n.rpc.listhtlcs() == {'htlcs': []}
|
||||
|
||||
# But forwards are not forgotten!
|
||||
assert l2.rpc.listforwards()['forwards'] == all_forwards
|
||||
|
||||
|
||||
@pytest.mark.openchannel('v1')
|
||||
def test_version_reexec(node_factory, bitcoind):
|
||||
|
||||
Reference in New Issue
Block a user