diff --git a/src/blockdb.cpp b/src/blockdb.cpp --- a/src/blockdb.cpp +++ b/src/blockdb.cpp @@ -10,7 +10,10 @@ extern RecursiveMutex cs_main; FlatFileSeq BlockFileSeq() { - return FlatFileSeq(gArgs.GetBlocksDirPath(), "blk", BLOCKFILE_CHUNK_SIZE); + return FlatFileSeq(gArgs.GetBlocksDirPath(), "blk", + gArgs.GetBoolArg("-fastprune", false) + ? 0x4000 /* 16kb */ + : BLOCKFILE_CHUNK_SIZE); } FlatFileSeq UndoFileSeq() { diff --git a/src/chainparams.cpp b/src/chainparams.cpp --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -457,7 +457,7 @@ netMagic[2] = 0xbf; netMagic[3] = 0xfa; nDefaultPort = 18444; - nPruneAfterHeight = 1000; + nPruneAfterHeight = gArgs.GetBoolArg("-fastprune", false) ? 100 : 1000; m_assumed_blockchain_size = 0; m_assumed_chain_state_size = 0; diff --git a/src/init.cpp b/src/init.cpp --- a/src/init.cpp +++ b/src/init.cpp @@ -456,6 +456,11 @@ "Specify directory to hold blocks subdirectory for *.dat " "files (default: )", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); + argsman.AddArg("-fastprune", + "Use smaller block files and lower minimum prune height for " + "testing purposes", + ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, + OptionsCategory::DEBUG_TEST); #if defined(HAVE_SYSTEM) argsman.AddArg("-blocknotify=", "Execute command when the best block changes (%s in cmd is " diff --git a/src/validation.cpp b/src/validation.cpp --- a/src/validation.cpp +++ b/src/validation.cpp @@ -3670,7 +3670,9 @@ bool finalize_undo = false; if (!fKnown) { - while (vinfoBlockFile[nFile].nSize + nAddSize >= MAX_BLOCKFILE_SIZE) { + while (vinfoBlockFile[nFile].nSize + nAddSize >= + (gArgs.GetBoolArg("-fastprune", false) ? 0x10000 /* 64kb */ + : 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 diff --git a/test/functional/feature_blockfilterindex_prune.py b/test/functional/feature_blockfilterindex_prune.py new file mode 100755 --- /dev/null +++ b/test/functional/feature_blockfilterindex_prune.py @@ -0,0 +1,100 @@ +#!/usr/bin/env python3 +# Copyright (c) 2020 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 blockfilterindex in conjunction with prune.""" +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import ( + assert_equal, + assert_greater_than, + assert_raises_rpc_error, +) + + +class FeatureBlockfilterindexPruneTest(BitcoinTestFramework): + def set_test_params(self): + self.num_nodes = 1 + self.extra_args = [["-fastprune", "-prune=1", "-blockfilterindex=1"]] + + def sync_index(self, height): + expected = { + 'basic block filter index': { + 'synced': True, + 'best_block_height': height + } + } + self.wait_until(lambda: self.nodes[0].getindexinfo() == expected) + + def run_test(self): + node = self.nodes[0] + + self.log.info("check if we can access a blockfilter when pruning is " + "enabled but no blocks are actually pruned") + self.sync_index(200) + assert_greater_than( + len(node.getblockfilter(node.getbestblockhash())['filter']), + 0) + # Mine two batches of blocks to avoid hitting + # NODE_NETWORK_LIMITED_MIN_BLOCKS disconnection + node.generate(250) + self.sync_all() + node.generate(250) + self.sync_all() + self.sync_index(height=700) + + self.log.info("prune some blocks") + pruneheight = node.pruneblockchain(400) + # Backport note: + # Bitcoin ABC fits 346 tx per blk?????.dat file, while Bitcoin Core + # fits only 250 tx per file. This is caused by the additional witness + # data in the Core transaction format. + # Pruning works by removing complete blk?????.dat files. + assert_equal(pruneheight, 346) + + self.log.info("check if we can access the tips blockfilter when we have" + " pruned some blocks") + assert_greater_than( + len(node.getblockfilter(node.getbestblockhash())['filter']), + 0) + + self.log.info( + "check if we can access the blockfilter of a pruned block") + assert_greater_than( + len(node.getblockfilter(node.getblockhash(2))['filter']), + 0) + + self.log.info("start node without blockfilterindex") + self.restart_node(0, extra_args=["-fastprune", "-prune=1"]) + + self.log.info("make sure accessing the blockfilters throws an error") + assert_raises_rpc_error( + -1, "Index is not enabled for filtertype basic", + node.getblockfilter, node.getblockhash(2)) + node.generate(1000) + + self.log.info("prune below the blockfilterindexes best block while " + "blockfilters are disabled") + # Backport note: we need to prune 1038 blocks to effectively prune the + # blockfilterindexes best block (height 700), because the pruning step + # is 346 blocks per blk?????.dat file. + pruneheight_new = node.pruneblockchain(1038) + assert_greater_than(pruneheight_new, pruneheight) + self.stop_node(0) + + self.log.info("make sure we get an init error when starting the node " + "again with block filters") + with node.assert_debug_log( + ["basic block filter index best block of the index goes beyond " + "pruned data. Please disable the index or reindex (which will " + "download the whole blockchain again)"]): + node.assert_start_raises_init_error( + extra_args=["-fastprune", "-prune=1", "-blockfilterindex=1"]) + self.log.info("make sure the node starts again with the -reindex arg") + self.start_node( + 0, + extra_args=["-fastprune", "-prune=1", "-blockfilterindex", + "-reindex"]) + + +if __name__ == '__main__': + FeatureBlockfilterindexPruneTest().main()