diff --git a/test/functional/feature_blocksxor.py b/test/functional/feature_blocksxor.py new file mode 100755 index 0000000000000..88e0244cd420f --- /dev/null +++ b/test/functional/feature_blocksxor.py @@ -0,0 +1,65 @@ +#!/usr/bin/env python3 +# Copyright (c) 2024 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 support for XORed block data and undo files (`-blocksxor` option).""" +import os + +from test_framework.test_framework import BitcoinTestFramework +from test_framework.test_node import ErrorMatch +from test_framework.util import ( + assert_equal, + assert_greater_than, + read_xor_key, + util_xor, +) +from test_framework.wallet import MiniWallet + + +class BlocksXORTest(BitcoinTestFramework): + def set_test_params(self): + self.num_nodes = 1 + self.extra_args = [[ + '-blocksxor=1', + '-fastprune=1', # use smaller block files + '-datacarriersize=100000', # needed to pad transaction with MiniWallet + ]] + + def run_test(self): + self.log.info("Mine some blocks, to create multiple blk*.dat/rev*.dat files") + node = self.nodes[0] + wallet = MiniWallet(node) + for _ in range(5): + wallet.send_self_transfer(from_node=node, target_weight=80000) + self.generate(wallet, 1) + + block_files = list(node.blocks_path.glob('blk[0-9][0-9][0-9][0-9][0-9].dat')) + undo_files = list(node.blocks_path.glob('rev[0-9][0-9][0-9][0-9][0-9].dat')) + assert_equal(len(block_files), len(undo_files)) + assert_greater_than(len(block_files), 1) # we want at least one full block file + + self.log.info("Shut down node and un-XOR block/undo files manually") + self.stop_node(0) + xor_key = read_xor_key(node=node) + for data_file in sorted(block_files + undo_files): + self.log.debug(f"Rewriting file {data_file}...") + with open(data_file, 'rb+') as f: + xored_data = f.read() + f.seek(0) + f.write(util_xor(xored_data, xor_key, offset=0)) + + self.log.info("Check that restarting with 'blocksxor=0' fails if XOR key is present") + node.assert_start_raises_init_error(['-blocksxor=0'], + 'The blocksdir XOR-key can not be disabled when a random key was already stored!', + match=ErrorMatch.PARTIAL_REGEX) + + self.log.info("Delete XOR key, restart node with '-blocksxor=0', check blk*.dat/rev*.dat file integrity") + os.remove(node.blocks_path / 'xor.dat') + self.start_node(0, extra_args=['-blocksxor=0']) + # checklevel=2 -> verify block validity + undo data + # nblocks=0 -> verify all blocks + node.verifychain(checklevel=2, nblocks=0) + + +if __name__ == '__main__': + BlocksXORTest(__file__).main() diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py index 67693259d3c35..b85bf1c668f8e 100755 --- a/test/functional/test_runner.py +++ b/test/functional/test_runner.py @@ -284,6 +284,7 @@ 'mempool_package_limits.py', 'mempool_package_rbf.py', 'feature_versionbits_warning.py', + 'feature_blocksxor.py', 'rpc_preciousblock.py', 'wallet_importprunedfunds.py --legacy-wallet', 'wallet_importprunedfunds.py --descriptors',