diff --git a/test/functional/feature_init.py b/test/functional/feature_init.py index 15b3e8595fc..a7b7e0c6760 100755 --- a/test/functional/feature_init.py +++ b/test/functional/feature_init.py @@ -47,18 +47,20 @@ class InitTest(BitcoinTestFramework): node.process.terminate() node.process.wait() - def start_expecting_error(err_fragment): + def start_expecting_error(err_fragment, args): node.assert_start_raises_init_error( - extra_args=['-txindex=1', '-blockfilterindex=1', '-coinstatsindex=1', '-checkblocks=200', '-checklevel=4'], + extra_args=args, expected_msg=err_fragment, match=ErrorMatch.PARTIAL_REGEX, ) - def check_clean_start(): + def check_clean_start(extra_args): """Ensure that node restarts successfully after various interrupts.""" - node.start() + node.start(extra_args) node.wait_for_rpc_connection() - assert_equal(200, node.getblockcount()) + height = node.getblockcount() + assert_equal(200, height) + self.wait_until(lambda: all(i["synced"] and i["best_block_height"] == height for i in node.getindexinfo().values())) lines_to_terminate_after = [ b'Validating signatures for all blocks', @@ -97,34 +99,83 @@ class InitTest(BitcoinTestFramework): self.log.debug("Terminating node after terminate line was found") sigterm_node() - check_clean_start() + # Prior to deleting/perturbing index files, start node with all indexes enabled. + # 'check_clean_start' will ensure indexes are synchronized (i.e., data exists to modify) + check_clean_start(args) self.stop_node(0) self.log.info("Test startup errors after removing certain essential files") - files_to_delete = { - 'blocks/index/*.ldb': 'Error opening block database.', - 'chainstate/*.ldb': 'Error opening coins database.', - 'blocks/blk*.dat': 'Error loading block database.', - 'indexes/txindex/MANIFEST*': 'LevelDB error: Corruption: CURRENT points to a non-existent file', + deletion_rounds = [ + { + 'filepath_glob': 'blocks/index/*.ldb', + 'error_message': 'Error opening block database.', + 'startup_args': [], + }, + { + 'filepath_glob': 'chainstate/*.ldb', + 'error_message': 'Error opening coins database.', + 'startup_args': ['-checklevel=4'], + }, + { + 'filepath_glob': 'blocks/blk*.dat', + 'error_message': 'Error loading block database.', + 'startup_args': ['-checkblocks=200', '-checklevel=4'], + }, + { + 'filepath_glob': 'indexes/txindex/MANIFEST*', + 'error_message': 'LevelDB error: Corruption: CURRENT points to a non-existent file', + 'startup_args': ['-txindex=1'], + }, # Removing these files does not result in a startup error: # 'indexes/blockfilter/basic/*.dat', 'indexes/blockfilter/basic/db/*.*', 'indexes/coinstats/db/*.*', # 'indexes/txindex/*.log', 'indexes/txindex/CURRENT', 'indexes/txindex/LOCK' - } + ] - files_to_perturb = { - 'blocks/index/*.ldb': 'Error loading block database.', - 'chainstate/*.ldb': 'Error opening coins database.', - 'blocks/blk*.dat': 'Corrupted block database detected.', - 'indexes/blockfilter/basic/db/*.*': 'LevelDB error: Corruption', - 'indexes/coinstats/db/*.*': 'LevelDB error: Corruption', - 'indexes/txindex/*.log': 'LevelDB error: Corruption', - 'indexes/txindex/CURRENT': 'LevelDB error: Corruption', + perturbation_rounds = [ + { + 'filepath_glob': 'blocks/index/*.ldb', + 'error_message': 'Error loading block database.', + 'startup_args': [], + }, + { + 'filepath_glob': 'chainstate/*.ldb', + 'error_message': 'Error opening coins database.', + 'startup_args': [], + }, + { + 'filepath_glob': 'blocks/blk*.dat', + 'error_message': 'Corrupted block database detected.', + 'startup_args': ['-checkblocks=200', '-checklevel=4'], + }, + { + 'filepath_glob': 'indexes/blockfilter/basic/db/*.*', + 'error_message': 'LevelDB error: Corruption', + 'startup_args': ['-blockfilterindex=1'], + }, + { + 'filepath_glob': 'indexes/coinstats/db/*.*', + 'error_message': 'LevelDB error: Corruption', + 'startup_args': ['-coinstatsindex=1'], + }, + { + 'filepath_glob': 'indexes/txindex/*.log', + 'error_message': 'LevelDB error: Corruption', + 'startup_args': ['-txindex=1'], + }, + { + 'filepath_glob': 'indexes/txindex/CURRENT', + 'error_message': 'LevelDB error: Corruption', + 'startup_args': ['-txindex=1'], + }, # Perturbing these files does not result in a startup error: # 'indexes/blockfilter/basic/*.dat', 'indexes/txindex/MANIFEST*', 'indexes/txindex/LOCK' - } + ] - for file_patt, err_fragment in files_to_delete.items(): + for round_info in deletion_rounds: + file_patt = round_info['filepath_glob'] + err_fragment = round_info['error_message'] + startup_args = round_info['startup_args'] target_files = list(node.chain_path.glob(file_patt)) for target_file in target_files: @@ -132,19 +183,23 @@ class InitTest(BitcoinTestFramework): bak_path = str(target_file) + ".bak" target_file.rename(bak_path) - start_expecting_error(err_fragment) + start_expecting_error(err_fragment, startup_args) for target_file in target_files: bak_path = str(target_file) + ".bak" self.log.debug(f"Restoring file from {bak_path} and restarting") Path(bak_path).rename(target_file) - check_clean_start() + check_clean_start(args) self.stop_node(0) self.log.info("Test startup errors after perturbing certain essential files") dirs = ["blocks", "chainstate", "indexes"] - for file_patt, err_fragment in files_to_perturb.items(): + for round_info in perturbation_rounds: + file_patt = round_info['filepath_glob'] + err_fragment = round_info['error_message'] + startup_args = round_info['startup_args'] + for dir in dirs: shutil.copytree(node.chain_path / dir, node.chain_path / f"{dir}_bak") target_files = list(node.chain_path.glob(file_patt)) @@ -158,7 +213,7 @@ class InitTest(BitcoinTestFramework): tf.seek(150) tf.write(b"1" * 200) - start_expecting_error(err_fragment) + start_expecting_error(err_fragment, startup_args) for dir in dirs: shutil.rmtree(node.chain_path / dir)