From 271c23e87f61276d7acab74e115b25a35144c8b4 Mon Sep 17 00:00:00 2001 From: Martin Zumsande Date: Tue, 28 Feb 2023 18:26:23 -0500 Subject: [PATCH 1/2] blockstorage: Adjust fastprune limit if block exceeds blockfile size If the added block exceeds the blockfile size in test-only -fastprune mode, the node would get stuck in an infinite loop and run out of memory. Avoid this by raising the blockfile size to the size of the added block in this situation. Co-authored-by: TheCharlatan --- src/node/blockstorage.cpp | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/node/blockstorage.cpp b/src/node/blockstorage.cpp index af84e6d7e7f..27b11af8866 100644 --- a/src/node/blockstorage.cpp +++ b/src/node/blockstorage.cpp @@ -619,7 +619,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 From 8f14fc86225d8fe77353f61ebd6b0bdda8d13aa9 Mon Sep 17 00:00:00 2001 From: Matthew Zipkin Date: Wed, 22 Mar 2023 13:42:46 -0400 Subject: [PATCH 2/2] test: cover fastprune with excessive block size --- test/functional/feature_fastprune.py | 42 ++++++++++++++++++++++++++++ test/functional/test_runner.py | 1 + 2 files changed, 43 insertions(+) create mode 100755 test/functional/feature_fastprune.py diff --git a/test/functional/feature_fastprune.py b/test/functional/feature_fastprune.py new file mode 100755 index 00000000000..825de63e3d2 --- /dev/null +++ b/test/functional/feature_fastprune.py @@ -0,0 +1,42 @@ +#!/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, + add_witness_commitment +) +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]) + tx = wallet.create_self_transfer()['tx'] + annex = [0x50] + for _ in range(0x10000): + annex.append(0xff) + tx.wit.vtxinwit[0].scriptWitness.stack.append(bytes(annex)) + tip = int(self.nodes[0].getbestblockhash(), 16) + time = self.nodes[0].getblock(self.nodes[0].getbestblockhash())['time'] + 1 + height = self.nodes[0].getblockcount() + 1 + block = create_block(hashprev=tip, ntime=time, txlist=[tx], coinbase=create_coinbase(height=height)) + add_witness_commitment(block) + block.solve() + self.nodes[0].submitblock(block.serialize().hex()) + 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 bcedc0c9af8..7bae36ab2c0 100755 --- a/test/functional/test_runner.py +++ b/test/functional/test_runner.py @@ -320,6 +320,7 @@ BASE_SCRIPTS = [ 'feature_includeconf.py', 'feature_addrman.py', 'feature_asmap.py', + 'feature_fastprune.py', 'mempool_unbroadcast.py', 'mempool_compatibility.py', 'mempool_accept_wtxid.py',