Test bwatch handles a 1-block reorg correctly
(node_factory, bitcoind)
| 5328 | |
| 5329 | |
| 5330 | def test_bwatch_reorg_1_block(node_factory, bitcoind): |
| 5331 | """Test bwatch handles a 1-block reorg correctly""" |
| 5332 | l1 = node_factory.get_node(options=BWATCH_OPTS) |
| 5333 | |
| 5334 | # Wait for bwatch to initialize and sync to initial tip |
| 5335 | wait_bwatch_caught_up(l1) |
| 5336 | |
| 5337 | # Mine a few blocks to establish history |
| 5338 | bitcoind.generate_block(5) |
| 5339 | # Wait for bwatch to fully catch up (important: bwatch must have the block |
| 5340 | # that will be reorged, otherwise it can't detect the reorg) |
| 5341 | l1.daemon.wait_for_log(r'No block change') |
| 5342 | |
| 5343 | # Get the actual number of blocks bwatch has stored before reorg |
| 5344 | ds_before = l1.rpc.listdatastore(['bwatch', 'block_history']) |
| 5345 | blocks_before = len(ds_before['datastore']) |
| 5346 | |
| 5347 | # Get the hash of the last block (the one we'll reorg out) |
| 5348 | height = bitcoind.rpc.getblockcount() |
| 5349 | old_block_hash = bitcoind.rpc.getblockhash(height) |
| 5350 | common_ancestor_hash = bitcoind.rpc.getblockhash(height - 1) |
| 5351 | |
| 5352 | # Invalidate the last block (1-block reorg) |
| 5353 | bitcoind.rpc.invalidateblock(old_block_hash) |
| 5354 | |
| 5355 | # Mine 2 new blocks on the new chain |
| 5356 | bitcoind.generate_block(2) |
| 5357 | |
| 5358 | # bwatch should detect and handle the reorg |
| 5359 | l1.daemon.wait_for_log(r'Reorg detected', timeout=60) |
| 5360 | # Persisting rolled-forward blocks is async; don't assert immediately after |
| 5361 | # "Reorg detected" (see test_bwatch_reorg_2_blocks). |
| 5362 | l1.daemon.wait_for_log(r'No block change', timeout=60) |
| 5363 | |
| 5364 | # Verify bwatch's block history matches bitcoind's new chain |
| 5365 | new_height = bitcoind.rpc.getblockcount() |
| 5366 | # After reorg: removed 1 old block, added 2 new blocks, so net +1 |
| 5367 | wait_for(lambda: len(l1.rpc.listdatastore(['bwatch', 'block_history'])['datastore']) |
| 5368 | == blocks_before + 1, |
| 5369 | timeout=60) |
| 5370 | |
| 5371 | ds = l1.rpc.listdatastore(['bwatch', 'block_history']) |
| 5372 | |
| 5373 | # Verify the common ancestor is still present with correct hash |
| 5374 | ancestor_entry = next((e for e in ds['datastore'] |
| 5375 | if e['key'][-1] == f"{height - 1:010d}"), None) |
| 5376 | assert ancestor_entry is not None |
| 5377 | # Decode the block record and verify hash matches |
| 5378 | assert reverse_bitcoin_hash(common_ancestor_hash) in str(ancestor_entry['hex']) |
| 5379 | |
| 5380 | # Verify the old block hash is not in datastore |
| 5381 | for entry in ds['datastore']: |
| 5382 | assert reverse_bitcoin_hash(old_block_hash) not in str(entry['hex']) |
| 5383 | |
| 5384 | # Verify the new tip matches bitcoind |
| 5385 | new_tip_hash = bitcoind.rpc.getblockhash(new_height) |
| 5386 | tip_entry = next((e for e in ds['datastore'] |
| 5387 | if e['key'][-1] == f"{new_height:010d}"), None) |
nothing calls this directly
no test coverage detected