mirror of
https://github.com/aljazceru/lightning.git
synced 2025-12-20 15:44:21 +01:00
pytest: Add a test for the commitment_revocation hook
This commit is contained in:
committed by
Rusty Russell
parent
d1f8509060
commit
8f2ce1e638
14
tests/plugins/watchtower.py
Executable file
14
tests/plugins/watchtower.py
Executable file
@@ -0,0 +1,14 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
from pyln.client import Plugin
|
||||||
|
|
||||||
|
plugin = Plugin()
|
||||||
|
|
||||||
|
|
||||||
|
@plugin.hook('commitment_revocation')
|
||||||
|
def on_commitment_revocation(commitment_txid, penalty_tx, plugin, **kwargs):
|
||||||
|
with open('watchtower.csv', 'a') as f:
|
||||||
|
f.write("{}, {}\n".format(commitment_txid, penalty_tx))
|
||||||
|
|
||||||
|
|
||||||
|
plugin.run()
|
||||||
@@ -1270,6 +1270,78 @@ def test_replacement_payload(node_factory):
|
|||||||
assert l2.daemon.wait_for_log("Attept to pay.*with wrong secret")
|
assert l2.daemon.wait_for_log("Attept to pay.*with wrong secret")
|
||||||
|
|
||||||
|
|
||||||
|
@unittest.skipIf(not DEVELOPER, "Requires dev_sign_last_tx")
|
||||||
|
def test_watchtower(node_factory, bitcoind, directory, chainparams):
|
||||||
|
"""Test watchtower hook.
|
||||||
|
|
||||||
|
l1 and l2 open a channel, make a couple of updates and then l1 cheats on
|
||||||
|
l2 while that one is offline. The watchtower plugin meanwhile stashes all
|
||||||
|
the penalty transactions and we release the one matching the offending
|
||||||
|
commitment transaction.
|
||||||
|
|
||||||
|
"""
|
||||||
|
p = os.path.join(os.path.dirname(__file__), "plugins/watchtower.py")
|
||||||
|
l1, l2 = node_factory.line_graph(
|
||||||
|
2,
|
||||||
|
opts=[{'may_fail': True, 'allow_broken_log': True}, {'plugin': p}]
|
||||||
|
)
|
||||||
|
|
||||||
|
# Force a new commitment
|
||||||
|
l1.rpc.pay(l2.rpc.invoice(25000000, 'lbl1', 'desc1')['bolt11'])
|
||||||
|
|
||||||
|
tx = l1.rpc.dev_sign_last_tx(l2.info['id'])['tx']
|
||||||
|
|
||||||
|
# Now make sure it is out of date
|
||||||
|
l1.rpc.pay(l2.rpc.invoice(25000000, 'lbl2', 'desc2')['bolt11'])
|
||||||
|
|
||||||
|
# l2 stops watching the chain, allowing the watchtower to react
|
||||||
|
l2.stop()
|
||||||
|
|
||||||
|
# Now l1 cheats
|
||||||
|
bitcoind.rpc.sendrawtransaction(tx)
|
||||||
|
time.sleep(1)
|
||||||
|
bitcoind.generate_block(1)
|
||||||
|
|
||||||
|
wt_file = os.path.join(
|
||||||
|
l2.daemon.lightning_dir,
|
||||||
|
chainparams['name'],
|
||||||
|
'watchtower.csv'
|
||||||
|
)
|
||||||
|
|
||||||
|
cheat_tx = bitcoind.rpc.decoderawtransaction(tx)
|
||||||
|
for l in open(wt_file, 'r'):
|
||||||
|
txid, penalty = l.strip().split(', ')
|
||||||
|
if txid == cheat_tx['txid']:
|
||||||
|
# This one should succeed, since it is a response to the cheat_tx
|
||||||
|
bitcoind.rpc.sendrawtransaction(penalty)
|
||||||
|
break
|
||||||
|
|
||||||
|
# Need this to check that l2 gets the funds
|
||||||
|
penalty_meta = bitcoind.rpc.decoderawtransaction(penalty)
|
||||||
|
|
||||||
|
time.sleep(1)
|
||||||
|
bitcoind.generate_block(1)
|
||||||
|
|
||||||
|
# Make sure l2's normal penalty_tx doesn't reach the network
|
||||||
|
def mock_sendrawtransaction(tx):
|
||||||
|
print("NOT broadcasting", tx)
|
||||||
|
|
||||||
|
l2.daemon.rpcproxy.mock_rpc('sendrawtransaction', mock_sendrawtransaction)
|
||||||
|
|
||||||
|
# Restart l2, and it should continue where the watchtower left off:
|
||||||
|
l2.start()
|
||||||
|
|
||||||
|
# l2 will still try to broadcast its latest commitment tx, but it'll fail
|
||||||
|
# since l1 has cheated. All commitments share the same prefix, so look for
|
||||||
|
# that.
|
||||||
|
penalty_prefix = tx[:(4 + 1 + 36) * 2] # version, txin_count, first txin in hex
|
||||||
|
l2.daemon.wait_for_log(r'Expected error broadcasting tx {}'.format(penalty_prefix))
|
||||||
|
|
||||||
|
# Now make sure the penalty output ends up in our wallet
|
||||||
|
fund_txids = [o['txid'] for o in l2.rpc.listfunds()['outputs']]
|
||||||
|
assert(penalty_meta['txid'] in fund_txids)
|
||||||
|
|
||||||
|
|
||||||
def test_plugin_fail(node_factory):
|
def test_plugin_fail(node_factory):
|
||||||
"""Test that a plugin which fails (not during a command)"""
|
"""Test that a plugin which fails (not during a command)"""
|
||||||
plugin = os.path.join(os.path.dirname(__file__), 'plugins/fail_by_itself.py')
|
plugin = os.path.join(os.path.dirname(__file__), 'plugins/fail_by_itself.py')
|
||||||
|
|||||||
Reference in New Issue
Block a user