diff --git a/tests/test_db.py b/tests/test_db.py index 692581a73..e0b66df89 100644 --- a/tests/test_db.py +++ b/tests/test_db.py @@ -1,4 +1,8 @@ from fixtures import * # noqa: F401,F403 +from utils import wait_for + + +import pytest def test_db_dangling_peer_fix(node_factory): @@ -15,3 +19,57 @@ def test_db_dangling_peer_fix(node_factory): # Make sure l2 has register connection l2.daemon.wait_for_log('Handed peer, entering loop') l2.fund_channel(l1, 200000, wait_for_active=True) + + +@pytest.mark.xfail(strict=True) +def test_block_backfill(node_factory, bitcoind): + """Test whether we backfill data from the blockchain correctly. + + For normal operation we will process any block after the initial start + height, or rescan height, but for gossip we actually also need to backfill + the blocks we skipped initially. We do so on-demand, whenever we see a + channel_announcement referencing a blockheight we haven't processed yet, + we fetch the entire block, extract P2WSH outputs and ask `bitcoin + gettxout` for each of them. We then store the block header in the `blocks` + table and the unspent outputs in the `utxoset` table. + + The test consist of two nodes opening a channel at height X, and an + unrelated P2WSH transaction being sent at the same height (will be used to + check for completeness of the backfill). Then a second node starts at + height X+100 and connect to one of the nodes. It should not have the block + in its DB before connecting. After connecting it should sync the gossip, + triggering a backfill of block X, and all associated P2WSH outputs. + + """ + # Need to manually open the channels later since otherwise we can't have a + # tx in the same block (`line_graph` with `fundchannel=True` generates + # blocks). + l1, l2 = node_factory.line_graph(2, fundchannel=False) + + # Get some funds to l1 + addr = l1.rpc.newaddr()['bech32'] + bitcoind.rpc.sendtoaddress(addr, 1) + wait_for(lambda: len(bitcoind.rpc.getrawmempool()) == 1) + bitcoind.generate_block(1) + l1.daemon.wait_for_log(r'Owning') + + # Now send the needle we will go looking for later: + bitcoind.rpc.sendtoaddress('bcrt1qtwxd8wg5eanumk86vfeujvp48hfkgannf77evggzct048wggsrxsum2pmm', 1) + l1.rpc.fundchannel(l2.info['id'], 10**6, announce=True) + wait_for(lambda: len(bitcoind.rpc.getrawmempool()) == 2) + + # Confirm and get some distance between the funding and the l3 wallet birth date + bitcoind.generate_block(100) + wait_for(lambda: len(l1.rpc.listnodes()['nodes']) == 2) + + # Start the tester node, and connect it to l1. l0 should sync the gossip + # and call out to `bitcoind` to backfill the block. + l3 = node_factory.get_node() + heights = [r['height'] for r in l3.db_query("SELECT height FROM blocks")] + assert(103 not in heights) + + l3.rpc.connect(l1.info['id'], 'localhost', l1.port) + + wait_for(lambda: len(l3.rpc.listnodes()['nodes']) == 2) + heights = [r['height'] for r in l3.db_query("SELECT height FROM blocks")] + assert(103 in heights)