|
4 | 4 | from fixtures import TEST_NETWORK |
5 | 5 | from pyln.client import RpcError, Millisatoshi |
6 | 6 | from utils import ( |
7 | | - only_one, wait_for, sync_blockheight, |
| 7 | + only_one, wait_for, sync_blockheight, mine_funding_to_announce, |
8 | 8 | VALGRIND, check_coin_moves, TailableProc, scriptpubkey_addr, |
9 | | - check_utxos_channel, check_feerate, did_short_sig |
| 9 | + check_utxos_channel, check_feerate, did_short_sig, first_scid, |
10 | 10 | ) |
11 | 11 |
|
12 | 12 | import os |
@@ -2536,3 +2536,48 @@ def test_hsm_wrong_passphrase_crash(node_factory): |
2536 | 2536 |
|
2537 | 2537 | os.close(master_fd2) |
2538 | 2538 | os.close(slave_fd2) |
| 2539 | + |
| 2540 | + |
| 2541 | +@pytest.mark.xfail(strict=True) |
| 2542 | +def test_unspend_during_reorg(node_factory, bitcoind): |
| 2543 | + l1, l2 = node_factory.line_graph(2) |
| 2544 | + scid = first_scid(l1, l2) |
| 2545 | + blockheight, txindex, _ = scid.split('x') |
| 2546 | + |
| 2547 | + # Use mainnet settings for rescan. |
| 2548 | + l3 = node_factory.get_node(options={'rescan': 15}) |
| 2549 | + l3.connect(l2) |
| 2550 | + |
| 2551 | + mine_funding_to_announce(bitcoind, [l1, l2, l3]) |
| 2552 | + bitcoind.generate_block(20) |
| 2553 | + sync_blockheight(bitcoind, [l3]) |
| 2554 | + wait_for(lambda: len(l3.rpc.listchannels()['channels']) == 2) |
| 2555 | + |
| 2556 | + # db shows it unspent. |
| 2557 | + assert only_one(l1.db_query(f"SELECT spendheight as spendheight FROM utxoset WHERE blockheight={blockheight} AND txindex={txindex}"))['spendheight'] is None |
| 2558 | + |
| 2559 | + # Now, l3 sees the close, marks channel dying. |
| 2560 | + l1.rpc.close(l2.info['id']) |
| 2561 | + spentheight = bitcoind.rpc.getblockcount() + 1 |
| 2562 | + bitcoind.generate_block(14, wait_for_mempool=1) |
| 2563 | + wait_for(lambda: len(l3.rpc.listchannels()['channels']) == 2) |
| 2564 | + |
| 2565 | + # In one fell swoop it goes through dying, to dead (12 blocks) |
| 2566 | + l3.daemon.wait_for_log(f"Adding block {spentheight}") |
| 2567 | + l3.daemon.wait_for_log(f"gossipd: channel {scid} closing soon due to the funding outpoint being spent") |
| 2568 | + l3.daemon.wait_for_log(f"gossipd: Deleting channel {scid} due to the funding outpoint being spent") |
| 2569 | + |
| 2570 | + # db shows it spent |
| 2571 | + assert only_one(l3.db_query(f"SELECT spendheight as spendheight FROM utxoset WHERE blockheight={blockheight} AND txindex={txindex}"))['spendheight'] == spentheight |
| 2572 | + |
| 2573 | + # Restart, see replay. |
| 2574 | + l3.stop() |
| 2575 | + # This is enough to take channel from dying to dead. |
| 2576 | + bitcoind.generate_block(10) |
| 2577 | + |
| 2578 | + l3.start() |
| 2579 | + # Channel should still be dead. |
| 2580 | + l3.daemon.wait_for_log(f"Adding block {spentheight}") |
| 2581 | + |
| 2582 | + sync_blockheight(bitcoind, [l3]) |
| 2583 | + assert only_one(l3.db_query(f"SELECT spendheight as spendheight FROM utxoset WHERE blockheight={blockheight} AND txindex={txindex}"))['spendheight'] == spentheight |
0 commit comments