From de8f9123afbecc3b4f59fa80af8148bc865d0588 Mon Sep 17 00:00:00 2001 From: Matthew Zipkin Date: Wed, 21 Jun 2023 10:41:09 -0400 Subject: test: cover read-only blockstore Co-authored-by: Andrew Chow --- test/functional/feature_reindex_readonly.py | 64 +++++++++++++++++++++++++++++ test/functional/test_runner.py | 1 + 2 files changed, 65 insertions(+) create mode 100755 test/functional/feature_reindex_readonly.py diff --git a/test/functional/feature_reindex_readonly.py b/test/functional/feature_reindex_readonly.py new file mode 100755 index 0000000000..9f1bb30023 --- /dev/null +++ b/test/functional/feature_reindex_readonly.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python3 +# Copyright (c) 2023-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 running bitcoind with -reindex from a read-only blockstore +- Start a node, generate blocks, then restart with -reindex after setting blk files to read-only +""" + +import platform +import stat +import subprocess +from test_framework.test_framework import BitcoinTestFramework + + +class BlockstoreReindexTest(BitcoinTestFramework): + def set_test_params(self): + self.setup_clean_chain = True + self.num_nodes = 1 + self.extra_args = [["-fastprune"]] + + def reindex_readonly(self): + self.log.debug("Generate block big enough to start second block file") + fastprune_blockfile_size = 0x10000 + opreturn = "6a" + nulldata = fastprune_blockfile_size * "ff" + self.generateblock(self.nodes[0], output=f"raw({opreturn}{nulldata})", transactions=[]) + self.stop_node(0) + + assert (self.nodes[0].chain_path / "blocks" / "blk00000.dat").exists() + assert (self.nodes[0].chain_path / "blocks" / "blk00001.dat").exists() + + self.log.debug("Make the first block file read-only") + filename = self.nodes[0].chain_path / "blocks" / "blk00000.dat" + filename.chmod(stat.S_IREAD) + + used_chattr = False + if platform.system() == "Linux": + try: + subprocess.run(['chattr', '+i', filename], capture_output=True, check=True) + used_chattr = True + self.log.info("Made file immutable with chattr") + except subprocess.CalledProcessError as e: + self.log.warning(str(e)) + if e.stdout: + self.log.warning(f"stdout: {e.stdout}") + if e.stderr: + self.log.warning(f"stderr: {e.stderr}") + + self.log.debug("Attempt to restart and reindex the node with the unwritable block file") + with self.nodes[0].assert_debug_log(expected_msgs=['FlushStateToDisk', 'failed to open file'], unexpected_msgs=[]): + self.nodes[0].assert_start_raises_init_error(extra_args=['-reindex', '-fastprune'], + expected_msg="Error: A fatal internal error occurred, see debug.log for details") + + if used_chattr: + subprocess.check_call(['chattr', '-i', filename]) + + filename.chmod(0o777) + + def run_test(self): + self.reindex_readonly() + + +if __name__ == '__main__': + BlockstoreReindexTest().main() diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py index db04bb8bdb..f44a5be19f 100755 --- a/test/functional/test_runner.py +++ b/test/functional/test_runner.py @@ -162,6 +162,7 @@ BASE_SCRIPTS = [ 'wallet_abandonconflict.py --legacy-wallet', 'wallet_abandonconflict.py --descriptors', 'feature_reindex.py', + 'feature_reindex_readonly.py', 'wallet_labels.py --legacy-wallet', 'wallet_labels.py --descriptors', 'p2p_compactblocks.py', -- cgit v1.2.3