mirror of
https://github.com/ElementsProject/lightning.git
synced 2025-01-18 05:12:45 +01:00
pytest: Add a test for the commitment_revocation hook
This commit is contained in:
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")
|
||||
|
||||
|
||||
@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):
|
||||
"""Test that a plugin which fails (not during a command)"""
|
||||
plugin = os.path.join(os.path.dirname(__file__), 'plugins/fail_by_itself.py')
|
||||
|
Loading…
Reference in New Issue
Block a user