Skip to content

Commit

Permalink
test_lightningd.py: test case where onchaind restarts with different …
Browse files Browse the repository at this point in the history
…fees.

This was revealed in ElementsProject#1114; onchaind isn't actually completely idempotent
due to fee changes (and the now-fixed change in keys used).

This triggers the bug by restarting with different fees, resulting in
onchaind not recognizing its own proposal:

	2018-03-05T09:38:15.550Z lightningd(23076): lightning_onchaind-022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59 chan #1: STATUS_FAIL_INTERNAL_ERROR: THEIR_UNILATERAL/OUR_HTLC spent with weird witness 3

Signed-off-by: Rusty Russell <[email protected]>
  • Loading branch information
rustyrussell authored and cdecker committed Mar 7, 2018
1 parent 6e4eb65 commit 16ed504
Showing 1 changed file with 74 additions and 0 deletions.
74 changes: 74 additions & 0 deletions tests/test_lightningd.py
Original file line number Diff line number Diff line change
Expand Up @@ -1663,6 +1663,80 @@ def test_penalty_outhtlc(self):
# FIXME: Test wallet balance...
wait_forget_channels(l2)

@unittest.skipIf(not DEVELOPER, "needs DEVELOPER=1")
def test_onchain_feechange(self):
"""Onchain handling when we restart with different fees"""
# HTLC 1->2, 2 fails just after they're both irrevocably committed
# We need 2 to drop to chain, because then 1's HTLC timeout tx
# is generated on-the-fly, and is thus feerate sensitive.
disconnects = ['-WIRE_UPDATE_FAIL_HTLC', 'permfail']
l1 = self.node_factory.get_node()
l2 = self.node_factory.get_node(disconnect=disconnects)

l1.rpc.connect(l2.info['id'], 'localhost', l2.info['port'])
self.fund_channel(l1, l2, 10**6)

rhash = l2.rpc.invoice(10**8, 'onchain_timeout', 'desc')['payment_hash']
# We underpay, so it fails.
routestep = {
'msatoshi': 10**8 - 1,
'id': l2.info['id'],
'delay': 5,
'channel': '1:1:1'
}

self.executor.submit(l1.rpc.sendpay, to_json([routestep]), rhash)

# l2 will drop to chain.
l2.daemon.wait_for_log('permfail')
l2.daemon.wait_for_log('sendrawtx exit 0')
bitcoind.generate_block(1)
l1.daemon.wait_for_log(' to ONCHAIN')
l2.daemon.wait_for_log(' to ONCHAIN')

# Wait for timeout.
l1.daemon.wait_for_log('Propose handling THEIR_UNILATERAL/OUR_HTLC by OUR_HTLC_TIMEOUT_TO_US .* in 5 blocks')
bitcoind.generate_block(5)

l1.daemon.wait_for_log('sendrawtx exit 0')

# Make sure that gets included.

bitcoind.generate_block(1)
# Now we restart with different feerates.
l1.stop()

l1.daemon.cmd_line.append('--override-fee-rates=20000/9000/2000')
l1.daemon.start()

# We recognize different proposal as ours.
l1.daemon.wait_for_log('Resolved THEIR_UNILATERAL/OUR_HTLC by our proposal OUR_HTLC_TIMEOUT_TO_US')

# We use 3 blocks for "reasonable depth", so add two more
bitcoind.generate_block(2)

# Note that the very similar test_onchain_timeout looks for a
# different string: that's because it sees the JSONRPC response,
# and due to the l1 restart, there is none here.
l1.daemon.wait_for_log('WIRE_PERMANENT_CHANNEL_FAILURE')

# 91 later, l2 is done
bitcoind.generate_block(90)
sync_blockheight([l2])
assert not l2.daemon.is_in_log('onchaind complete, forgetting peer')
bitcoind.generate_block(1)
l2.daemon.wait_for_log('onchaind complete, forgetting peer')

# Now, 6 blocks and l1 should be done.
bitcoind.generate_block(5)
sync_blockheight([l1])
assert not l1.daemon.is_in_log('onchaind complete, forgetting peer')
bitcoind.generate_block(1)
l1.daemon.wait_for_log('onchaind complete, forgetting peer')

# Payment failed, BTW
assert l2.rpc.listinvoices('onchain_timeout')['invoices'][0]['status'] == 'unpaid'

@unittest.skipIf(not DEVELOPER, "needs DEVELOPER=1")
def test_permfail_new_commit(self):
# Test case where we have two possible commits: it will use new one.
Expand Down

0 comments on commit 16ed504

Please sign in to comment.