Merge bitcoin/bitcoin#31468: test: Avoid intermittent error in assert_equal(pruneheight_new, 248)

fa0998f0a0 test: Avoid intermittent error in assert_equal(pruneheight_new, 248) (MarcoFalke)

Pull request description:

  Fixes https://github.com/bitcoin/bitcoin/issues/31446

  The test uses the P2P network to sync blocks, which has no inherent guarantee that the blocks are sent and received in the right order, assuming the headers are received first.

  This can mean that the first block file is flushed with block at height 249 and block at height 248 is added to the second file. In the log it looks like: `Leaving block file 0: CBlockFileInfo(blocks=249, size=65319, heights=0...249, time=2011-02-02...2024-12-03) (onto 1) (height 248)`. The test assumes that the height of the last pruned block in the first file is 248, expecting it to look like: `Leaving block file 0: CBlockFileInfo(blocks=249, size=65319, heights=0...248, time=2011-02-02...2024-12-09) (onto 1) (height 249) `.

  Fix the issue by using a linear dumb sync.

ACKs for top commit:
  achow101:
    ACK fa0998f0a0
  mzumsande:
    Code Review ACK fa0998f0a0
  i-am-yuvi:
    Code Review ACK fa0998f0a0
  fjahr:
    Code review ACK fa0998f0a0

Tree-SHA512: 59cb4317be6cf9012c9bf7a3e9f5ba96b8b114b30bd2ac42af4fe742cd26a634d685b075f04a84bd782b2a43a342d75bb20a042bd82ad2831dbf844d39517ca2
This commit is contained in:
Ava Chow 2024-12-30 14:28:01 -05:00
commit ba0cb7d5a5
No known key found for this signature in database
GPG key ID: 17565732E08E5E41

View file

@ -1,8 +1,9 @@
#!/usr/bin/env python3
# Copyright (c) 2020-2022 The Bitcoin Core developers
# Copyright (c) 2020-present The Bitcoin Core developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test indices in conjunction with prune."""
import concurrent.futures
import os
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
@ -19,9 +20,25 @@ class FeatureIndexPruneTest(BitcoinTestFramework):
["-fastprune", "-prune=1", "-blockfilterindex=1"],
["-fastprune", "-prune=1", "-coinstatsindex=1"],
["-fastprune", "-prune=1", "-blockfilterindex=1", "-coinstatsindex=1"],
[]
[],
]
def setup_network(self):
self.setup_nodes() # No P2P connection, so that linear_sync works
def linear_sync(self, node_from, *, height_from=None):
# Linear sync over RPC, because P2P sync may not be linear
to_height = node_from.getblockcount()
if height_from is None:
height_from = min([n.getblockcount() for n in self.nodes]) + 1
with concurrent.futures.ThreadPoolExecutor(max_workers=self.num_nodes) as rpc_threads:
for i in range(height_from, to_height + 1):
b = node_from.getblock(blockhash=node_from.getblockhash(i), verbosity=0)
list(rpc_threads.map(lambda n: n.submitblock(b), self.nodes))
def generate(self, node, num_blocks, sync_fun=None):
return super().generate(node, num_blocks, sync_fun=sync_fun or (lambda: self.linear_sync(node)))
def sync_index(self, height):
expected_filter = {
'basic block filter index': {'synced': True, 'best_block_height': height},
@ -36,22 +53,9 @@ class FeatureIndexPruneTest(BitcoinTestFramework):
expected = {**expected_filter, **expected_stats}
self.wait_until(lambda: self.nodes[2].getindexinfo() == expected)
def reconnect_nodes(self):
self.connect_nodes(0,1)
self.connect_nodes(0,2)
self.connect_nodes(0,3)
def mine_batches(self, blocks):
n = blocks // 250
for _ in range(n):
self.generate(self.nodes[0], 250)
self.generate(self.nodes[0], blocks % 250)
self.sync_blocks()
def restart_without_indices(self):
for i in range(3):
self.restart_node(i, extra_args=["-fastprune", "-prune=1"])
self.reconnect_nodes()
def run_test(self):
filter_nodes = [self.nodes[0], self.nodes[2]]
@ -65,7 +69,7 @@ class FeatureIndexPruneTest(BitcoinTestFramework):
for node in stats_nodes:
assert node.gettxoutsetinfo(hash_type="muhash", hash_or_height=tip)['muhash']
self.mine_batches(500)
self.generate(self.nodes[0], 500)
self.sync_index(height=700)
self.log.info("prune some blocks")
@ -104,7 +108,7 @@ class FeatureIndexPruneTest(BitcoinTestFramework):
msg = "Querying specific block heights requires coinstatsindex"
assert_raises_rpc_error(-8, msg, node.gettxoutsetinfo, "muhash", height_hash)
self.mine_batches(749)
self.generate(self.nodes[0], 749)
self.log.info("prune exactly up to the indices best blocks while the indices are disabled")
for i in range(3):
@ -118,7 +122,7 @@ class FeatureIndexPruneTest(BitcoinTestFramework):
self.log.info("prune further than the indices best blocks while the indices are disabled")
self.restart_without_indices()
self.mine_batches(1000)
self.generate(self.nodes[0], 1000)
for i in range(3):
pruneheight_3 = self.nodes[i].pruneblockchain(2000)
@ -134,12 +138,10 @@ class FeatureIndexPruneTest(BitcoinTestFramework):
self.log.info("make sure the nodes start again with the indices and an additional -reindex arg")
for i in range(3):
restart_args = self.extra_args[i]+["-reindex"]
restart_args = self.extra_args[i] + ["-reindex"]
self.restart_node(i, extra_args=restart_args)
# The nodes need to be reconnected to the non-pruning node upon restart, otherwise they will be stuck
self.connect_nodes(i, 3)
self.sync_blocks(timeout=300)
self.linear_sync(self.nodes[3])
self.sync_index(height=2500)
for node in self.nodes[:2]:
@ -150,8 +152,7 @@ class FeatureIndexPruneTest(BitcoinTestFramework):
self.log.info("ensure that prune locks don't prevent indices from failing in a reorg scenario")
with self.nodes[0].assert_debug_log(['basic block filter index prune lock moved back to 2480']):
self.nodes[3].invalidateblock(self.nodes[0].getblockhash(2480))
self.generate(self.nodes[3], 30)
self.sync_blocks()
self.generate(self.nodes[3], 30, sync_fun=lambda: self.linear_sync(self.nodes[3], height_from=2480))
if __name__ == '__main__':