#!/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)."""

from test_framework.test_framework import BitcoinTestFramework
from test_framework.test_node import (
    ErrorMatch,
    NULL_BLK_XOR_KEY,
)
from test_framework.util import (
    assert_equal,
    assert_greater_than,
    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 = node.read_xor_key()
        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")
        node.blocks_key_path.unlink()
        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)
        self.log.info("Check that blocks XOR key is recreated")
        assert_equal(node.read_xor_key(), NULL_BLK_XOR_KEY)


if __name__ == '__main__':
    BlocksXORTest(__file__).main()
