diff --git a/src/node/blockstorage.cpp b/src/node/blockstorage.cpp index 0a5ca022fa73..1589d1afbb2b 100644 --- a/src/node/blockstorage.cpp +++ b/src/node/blockstorage.cpp @@ -624,7 +624,18 @@ bool BlockManager::FindBlockPos(FlatFilePos& pos, unsigned int nAddSize, unsigne bool finalize_undo = false; if (!fKnown) { - while (m_blockfile_info[nFile].nSize + nAddSize >= (gArgs.GetBoolArg("-fastprune", false) ? 0x10000 /* 64kb */ : MAX_BLOCKFILE_SIZE)) { + unsigned int max_blockfile_size{MAX_BLOCKFILE_SIZE}; + // Use smaller blockfiles in test-only -fastprune mode - but avoid + // the possibility of having a block not fit into the block file. + if (gArgs.GetBoolArg("-fastprune", false)) { + max_blockfile_size = 0x10000; // 64kiB + if (nAddSize >= max_blockfile_size) { + // dynamically adjust the blockfile size to be larger than the added size + max_blockfile_size = nAddSize + 1; + } + } + assert(nAddSize < max_blockfile_size); + while (m_blockfile_info[nFile].nSize + nAddSize >= max_blockfile_size) { // when the undo file is keeping up with the block file, we want to flush it explicitly // when it is lagging behind (more blocks arrive than are being connected), we let the // undo block write case handle it diff --git a/test/functional/feature_fastprune.py b/test/functional/feature_fastprune.py new file mode 100755 index 000000000000..977bbb0f08be --- /dev/null +++ b/test/functional/feature_fastprune.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python3 +# Copyright (c) 2023 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 fastprune mode.""" +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import ( + assert_equal +) +from test_framework.blocktools import ( + create_block, + create_coinbase, +) +from test_framework.messages import CTxOut, tx_from_hex +from test_framework.script import CScript, OP_RETURN +from test_framework.wallet import MiniWallet + + +class FeatureFastpruneTest(BitcoinTestFramework): + def set_test_params(self): + self.num_nodes = 1 + self.extra_args = [["-fastprune"]] + + def run_test(self): + self.log.info("ensure that large blocks don't crash or freeze in -fastprune") + wallet = MiniWallet(self.nodes[0]) + # Generate a block to fund the wallet with a UTXO + self.generate(wallet, 1) + + # Create a single transaction with large OP_RETURN to make block >64kb + # We need to create a transaction that's large enough to exceed the fastprune limit + tx = wallet.create_self_transfer()['tx'] + # Add a large OP_RETURN output (65kb of data to exceed 64kb fastprune limit) + large_data = b'\x00' * 65536 + tx.vout.append(CTxOut(0, CScript([OP_RETURN, large_data]))) + tx.rehash() + + tip = int(self.nodes[0].getbestblockhash(), 16) + time = self.nodes[0].getblock(self.nodes[0].getbestblockhash())['time'] + 1 + height = self.nodes[0].getblockcount() + 1 + + # Create proper CbTx for Dash (DIP4/v20 activated) + cbb = create_coinbase(height, dip4_activated=True, v20_activated=True) + gbt = self.nodes[0].getblocktemplate() + cbb.vExtraPayload = bytes.fromhex(gbt["coinbase_payload"]) + cbb.rehash() + + block = create_block(hashprev=tip, ntime=time, txlist=[tx], coinbase=cbb, version=4) + + # Add quorum commitments from block template + for tx_obj in gbt["transactions"]: + tx = tx_from_hex(tx_obj["data"]) + if tx.nType == 6: # TRANSACTION_QUORUM_COMMITMENT + block.vtx.append(tx) + + block.hashMerkleRoot = block.calc_merkle_root() + block.solve() + result = self.nodes[0].submitblock(block.serialize().hex()) + # submitblock returns None on success, error string on failure + if result is not None: + raise AssertionError(f"submitblock failed: {result}") + assert_equal(int(self.nodes[0].getbestblockhash(), 16), block.sha256) + + +if __name__ == '__main__': + FeatureFastpruneTest().main() diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py index 2c1a8597f535..1c6ce86a9afe 100755 --- a/test/functional/test_runner.py +++ b/test/functional/test_runner.py @@ -365,6 +365,7 @@ 'p2p_message_capture.py', 'feature_addrman.py', 'feature_asmap.py', + 'feature_fastprune.py', 'feature_includeconf.py', 'mempool_unbroadcast.py', 'mempool_compatibility.py',