diff options
-rw-r--r-- | depends/packages/libevent.mk | 12 | ||||
-rw-r--r-- | depends/patches/libevent/libevent-2-fixes.patch | 18 | ||||
-rw-r--r-- | depends/patches/libevent/reuseaddr.patch | 21 | ||||
-rw-r--r-- | doc/developer-notes.md | 18 | ||||
-rw-r--r-- | doc/man/bitcoin-qt.1 | 12 | ||||
-rw-r--r-- | doc/man/bitcoind.1 | 12 | ||||
-rwxr-xr-x | qa/rpc-tests/pruning.py | 128 | ||||
-rw-r--r-- | src/Makefile.qt.include | 2 | ||||
-rw-r--r-- | src/init.cpp | 16 | ||||
-rw-r--r-- | src/rpc/blockchain.cpp | 41 | ||||
-rw-r--r-- | src/rpc/client.cpp | 1 | ||||
-rw-r--r-- | src/txmempool.cpp | 2 | ||||
-rw-r--r-- | src/validation.cpp | 44 | ||||
-rw-r--r-- | src/validation.h | 2 |
14 files changed, 252 insertions, 77 deletions
diff --git a/depends/packages/libevent.mk b/depends/packages/libevent.mk index 4b02b2eff9..70f345e71d 100644 --- a/depends/packages/libevent.mk +++ b/depends/packages/libevent.mk @@ -1,13 +1,11 @@ package=libevent -$(package)_version=2.0.22 -$(package)_download_path=https://github.com/libevent/libevent/releases/download/release-2.0.22-stable -$(package)_file_name=$(package)-$($(package)_version)-stable.tar.gz -$(package)_sha256_hash=71c2c49f0adadacfdbe6332a372c38cf9c8b7895bb73dabeaa53cdcc1d4e1fa3 -$(package)_patches=reuseaddr.patch libevent-2-fixes.patch +$(package)_version=2.1.7 +$(package)_download_path=https://github.com/libevent/libevent/archive/ +$(package)_file_name=release-$($(package)_version)-rc.tar.gz +$(package)_sha256_hash=548362d202e22fe24d4c3fad38287b4f6d683e6c21503341373b89785fa6f991 define $(package)_preprocess_cmds - patch -p1 < $($(package)_patch_dir)/reuseaddr.patch && \ - patch -p1 < $($(package)_patch_dir)/libevent-2-fixes.patch + ./autogen.sh endef define $(package)_set_vars diff --git a/depends/patches/libevent/libevent-2-fixes.patch b/depends/patches/libevent/libevent-2-fixes.patch deleted file mode 100644 index 79fec8a488..0000000000 --- a/depends/patches/libevent/libevent-2-fixes.patch +++ /dev/null @@ -1,18 +0,0 @@ ---- a/util-internal.h 2013-11-01 12:18:57.000000000 -0600 -+++ b/util-internal.h 2015-07-20 20:19:43.199560900 -0500 -@@ -299,8 +299,13 @@ HANDLE evutil_load_windows_system_librar - - #if defined(__STDC__) && defined(__STDC_VERSION__) - #if (__STDC_VERSION__ >= 199901L) --#define EV_SIZE_FMT "%zu" --#define EV_SSIZE_FMT "%zd" -+ #if defined(_MSC_VER) || defined(__MINGW32__) || defined(__MINGW64__) -+ #define EV_SIZE_FMT "%Iu" -+ #define EV_SSIZE_FMT "%Id" -+ #else -+ #define EV_SIZE_FMT "%zu" -+ #define EV_SSIZE_FMT "%zd" -+ #endif - #define EV_SIZE_ARG(x) (x) - #define EV_SSIZE_ARG(x) (x) - #endif diff --git a/depends/patches/libevent/reuseaddr.patch b/depends/patches/libevent/reuseaddr.patch deleted file mode 100644 index 58695c11f5..0000000000 --- a/depends/patches/libevent/reuseaddr.patch +++ /dev/null @@ -1,21 +0,0 @@ ---- old/evutil.c 2015-08-28 19:26:23.488765923 -0400 -+++ new/evutil.c 2015-08-28 19:27:41.392767019 -0400 -@@ -321,15 +321,16 @@ - int - evutil_make_listen_socket_reuseable(evutil_socket_t sock) - { --#ifndef WIN32 - int one = 1; -+#ifndef WIN32 - /* REUSEADDR on Unix means, "don't hang on to this address after the - * listener is closed." On Windows, though, it means "don't keep other - * processes from binding to this address while we're using it. */ - return setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void*) &one, - (ev_socklen_t)sizeof(one)); - #else -- return 0; -+ return setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char*) &one, -+ (ev_socklen_t)sizeof(one)); - #endif - } - diff --git a/doc/developer-notes.md b/doc/developer-notes.md index ba03579e86..f8c34e060f 100644 --- a/doc/developer-notes.md +++ b/doc/developer-notes.md @@ -11,9 +11,13 @@ gradually. - Braces on new lines for namespaces, classes, functions, methods. - Braces on the same line for everything else. - 4 space indentation (no tabs) for every block except namespaces. - - No indentation for public/protected/private or for namespaces. + - No indentation for `public`/`protected`/`private` or for `namespace`. - No extra spaces inside parenthesis; don't do ( this ) - - No space after function names; one space after if, for and while. + - No space after function names; one space after `if`, `for` and `while`. + - If an `if` only has a single-statement then-clause, it can appear + on the same line as the if, without braces. In every other case, + braces are required, and the then and else clauses must appear + correctly indented on a new line. - `++i` is preferred over `i++`. Block style example: @@ -22,14 +26,18 @@ namespace foo { class Class { - bool Function(char* psz, int n) + bool Function(const std::string& s, int n) { // Comment summarising what this section of code does for (int i = 0; i < n; ++i) { // When something fails, return early - if (!Something()) - return false; + if (!Something()) return false; ... + if (SomethingElse()) { + DoMore(); + } else { + DoLess(); + } } // Success return is usually at the end diff --git a/doc/man/bitcoin-qt.1 b/doc/man/bitcoin-qt.1 index 24b529dac1..2129a151e2 100644 --- a/doc/man/bitcoin-qt.1 +++ b/doc/man/bitcoin-qt.1 @@ -75,11 +75,13 @@ Specify pid file (default: bitcoind.pid) .HP \fB\-prune=\fR<n> .IP -Reduce storage requirements by pruning (deleting) old blocks. This mode -is incompatible with \fB\-txindex\fR and \fB\-rescan\fR. Warning: Reverting -this setting requires re\-downloading the entire blockchain. -(default: 0 = disable pruning blocks, >550 = target size in MiB -to use for block files) +Reduce storage requirements by enabling pruning (deleting) of old blocks. +This allows the pruneblockchain RPC to be called to delete specific blocks, +and enables automatic pruning of old blocks if a target size in MiB is +provided. This mode is incompatible with \fB\-txindex\fR and \fB\-rescan\fR. +Warning: Reverting this setting requires re\-downloading the entire blockchain. +(default: 0 = disable pruning blocks, 1 = allow manual pruning via RPC, >550 = +automatically prune block files to stay under the specified target size in MiB) .HP \fB\-reindex\-chainstate\fR .IP diff --git a/doc/man/bitcoind.1 b/doc/man/bitcoind.1 index b99657a5fa..47539d8131 100644 --- a/doc/man/bitcoind.1 +++ b/doc/man/bitcoind.1 @@ -80,11 +80,13 @@ Specify pid file (default: bitcoind.pid) .HP \fB\-prune=\fR<n> .IP -Reduce storage requirements by pruning (deleting) old blocks. This mode -is incompatible with \fB\-txindex\fR and \fB\-rescan\fR. Warning: Reverting -this setting requires re\-downloading the entire blockchain. -(default: 0 = disable pruning blocks, >550 = target size in MiB -to use for block files) +Reduce storage requirements by enabling pruning (deleting) of old blocks. +This allows the pruneblockchain RPC to be called to delete specific blocks, +and enables automatic pruning of old blocks if a target size in MiB is +provided. This mode is incompatible with \fB\-txindex\fR and \fB\-rescan\fR. +Warning: Reverting this setting requires re\-downloading the entire blockchain. +(default: 0 = disable pruning blocks, 1 = allow manual pruning via RPC, >550 = +automatically prune block files to stay under the specified target size in MiB) .HP \fB\-reindex\-chainstate\fR .IP diff --git a/qa/rpc-tests/pruning.py b/qa/rpc-tests/pruning.py index 78b8938e4a..25f0779003 100755 --- a/qa/rpc-tests/pruning.py +++ b/qa/rpc-tests/pruning.py @@ -25,7 +25,7 @@ class PruneTest(BitcoinTestFramework): def __init__(self): super().__init__() self.setup_clean_chain = True - self.num_nodes = 3 + self.num_nodes = 6 # Cache for utxos, as the listunspent may take a long time later in the test self.utxo_cache_0 = [] @@ -43,10 +43,22 @@ class PruneTest(BitcoinTestFramework): self.nodes.append(start_node(2, self.options.tmpdir, ["-debug","-maxreceivebuffer=20000","-prune=550"], timewait=900)) self.prunedir = self.options.tmpdir+"/node2/regtest/blocks/" + # Create nodes 3 and 4 to test manual pruning (they will be re-started with manual pruning later) + self.nodes.append(start_node(3, self.options.tmpdir, ["-debug=0","-maxreceivebuffer=20000","-blockmaxsize=999000"], timewait=900)) + self.nodes.append(start_node(4, self.options.tmpdir, ["-debug=0","-maxreceivebuffer=20000","-blockmaxsize=999000"], timewait=900)) + + # Create nodes 5 to test wallet in prune mode, but do not connect + self.nodes.append(start_node(5, self.options.tmpdir, ["-debug=0", "-prune=550"])) + + # Determine default relay fee + self.relayfee = self.nodes[0].getnetworkinfo()["relayfee"] + connect_nodes(self.nodes[0], 1) connect_nodes(self.nodes[1], 2) connect_nodes(self.nodes[2], 0) - sync_blocks(self.nodes[0:3]) + connect_nodes(self.nodes[0], 3) + connect_nodes(self.nodes[0], 4) + sync_blocks(self.nodes[0:5]) def create_big_chain(self): # Start by creating some coinbases we can spend later @@ -57,7 +69,7 @@ class PruneTest(BitcoinTestFramework): for i in range(645): mine_large_block(self.nodes[0], self.utxo_cache_0) - sync_blocks(self.nodes[0:3]) + sync_blocks(self.nodes[0:5]) def test_height_min(self): if not os.path.isfile(self.prunedir+"blk00000.dat"): @@ -212,6 +224,103 @@ class PruneTest(BitcoinTestFramework): # Verify we can now have the data for a block previously pruned assert(self.nodes[2].getblock(self.forkhash)["height"] == self.forkheight) + def manual_test(self, node_number, use_timestamp): + # at this point, node has 995 blocks and has not yet run in prune mode + node = self.nodes[node_number] = start_node(node_number, self.options.tmpdir, ["-debug=0"], timewait=900) + assert_equal(node.getblockcount(), 995) + assert_raises_message(JSONRPCException, "not in prune mode", node.pruneblockchain, 500) + stop_node(node, node_number) + + # now re-start in manual pruning mode + node = self.nodes[node_number] = start_node(node_number, self.options.tmpdir, ["-debug=0","-prune=1"], timewait=900) + assert_equal(node.getblockcount(), 995) + + def height(index): + if use_timestamp: + return node.getblockheader(node.getblockhash(index))["time"] + else: + return index + + def has_block(index): + return os.path.isfile(self.options.tmpdir + "/node{}/regtest/blocks/blk{:05}.dat".format(node_number, index)) + + # should not prune because chain tip of node 3 (995) < PruneAfterHeight (1000) + assert_raises_message(JSONRPCException, "Blockchain is too short for pruning", node.pruneblockchain, height(500)) + + # mine 6 blocks so we are at height 1001 (i.e., above PruneAfterHeight) + node.generate(6) + + # negative and zero inputs should raise an exception + try: + node.pruneblockchain(-10) + raise AssertionError("pruneblockchain(-10) should have failed.") + except: + pass + + try: + node.pruneblockchain(0) + raise AssertionError("pruneblockchain(0) should have failed.") + except: + pass + + # height=100 too low to prune first block file so this is a no-op + node.pruneblockchain(height(100)) + if not has_block(0): + raise AssertionError("blk00000.dat is missing when should still be there") + + # height=500 should prune first file + node.pruneblockchain(height(500)) + if has_block(0): + raise AssertionError("blk00000.dat is still there, should be pruned by now") + if not has_block(1): + raise AssertionError("blk00001.dat is missing when should still be there") + + # height=650 should prune second file + node.pruneblockchain(height(650)) + if has_block(1): + raise AssertionError("blk00001.dat is still there, should be pruned by now") + + # height=1000 should not prune anything more, because tip-288 is in blk00002.dat. + node.pruneblockchain(height(1000)) + if not has_block(2): + raise AssertionError("blk00002.dat is still there, should be pruned by now") + + # advance the tip so blk00002.dat and blk00003.dat can be pruned (the last 288 blocks should now be in blk00004.dat) + node.generate(288) + node.pruneblockchain(height(1000)) + if has_block(2): + raise AssertionError("blk00002.dat is still there, should be pruned by now") + if has_block(3): + raise AssertionError("blk00003.dat is still there, should be pruned by now") + + # stop node, start back up with auto-prune at 550MB, make sure still runs + stop_node(node, node_number) + self.nodes[node_number] = start_node(node_number, self.options.tmpdir, ["-debug=0","-prune=550"], timewait=900) + + print("Success") + + def wallet_test(self): + # check that the pruning node's wallet is still in good shape + print("Stop and start pruning node to trigger wallet rescan") + try: + stop_node(self.nodes[2], 2) + start_node(2, self.options.tmpdir, ["-debug=1","-prune=550"]) + print("Success") + except Exception as detail: + raise AssertionError("Wallet test: unable to re-start the pruning node") + + # check that wallet loads loads successfully when restarting a pruned node after IBD. + # this was reported to fail in #7494. + print ("Syncing node 5 to test wallet") + connect_nodes(self.nodes[0], 5) + nds = [self.nodes[0], self.nodes[5]] + sync_blocks(nds) + try: + stop_node(self.nodes[5],5) #stop and start to trigger rescan + start_node(5, self.options.tmpdir, ["-debug=1","-prune=550"]) + print ("Success") + except Exception as detail: + raise AssertionError("Wallet test: unable to re-start node5") def run_test(self): print("Warning! This test requires 4GB of disk space and takes over 30 mins (up to 2 hours)") @@ -226,6 +335,10 @@ class PruneTest(BitcoinTestFramework): # Start by mining a simple chain that all nodes have # N0=N1=N2 **...*(995) + # stop manual-pruning node with 995 blocks + stop_node(self.nodes[3],3) + stop_node(self.nodes[4],4) + print("Check that we haven't started pruning yet because we're below PruneAfterHeight") self.test_height_min() # Extend this chain past the PruneAfterHeight @@ -308,6 +421,15 @@ class PruneTest(BitcoinTestFramework): # # N1 doesn't change because 1033 on main chain (*) is invalid + print("Test manual pruning with block indices") + self.manual_test(3, use_timestamp=False) + + print("Test manual pruning with timestamps") + self.manual_test(4, use_timestamp=True) + + print("Test wallet re-scan") + self.wallet_test() + print("Done") if __name__ == '__main__': diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include index 7ae42e83b1..edc3c4b292 100644 --- a/src/Makefile.qt.include +++ b/src/Makefile.qt.include @@ -355,7 +355,7 @@ endif RES_IMAGES = -RES_MOVIES = $(wildcard qt/res/movies/spinner-*.png) +RES_MOVIES = $(wildcard $(srcdir)/qt/res/movies/spinner-*.png) BITCOIN_RC = qt/res/bitcoin-qt-res.rc diff --git a/src/init.cpp b/src/init.cpp index 992ce8ebdc..9ac69b7d39 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -351,9 +351,9 @@ std::string HelpMessage(HelpMessageMode mode) #ifndef WIN32 strUsage += HelpMessageOpt("-pid=<file>", strprintf(_("Specify pid file (default: %s)"), BITCOIN_PID_FILENAME)); #endif - strUsage += HelpMessageOpt("-prune=<n>", strprintf(_("Reduce storage requirements by pruning (deleting) old blocks. This mode is incompatible with -txindex and -rescan. " + strUsage += HelpMessageOpt("-prune=<n>", strprintf(_("Reduce storage requirements by enabling pruning (deleting) of old blocks. This allows the pruneblockchain RPC to be called to delete specific blocks, and enables automatic pruning of old blocks if a target size in MiB is provided. This mode is incompatible with -txindex and -rescan. " "Warning: Reverting this setting requires re-downloading the entire blockchain. " - "(default: 0 = disable pruning blocks, >%u = target size in MiB to use for block files)"), MIN_DISK_SPACE_FOR_BLOCK_FILES / 1024 / 1024)); + "(default: 0 = disable pruning blocks, 1 = allow manual pruning via RPC, >%u = automatically prune block files to stay under the specified target size in MiB)"), MIN_DISK_SPACE_FOR_BLOCK_FILES / 1024 / 1024)); strUsage += HelpMessageOpt("-reindex-chainstate", _("Rebuild chain state from the currently indexed blocks")); strUsage += HelpMessageOpt("-reindex", _("Rebuild chain state and block index from the blk*.dat files on disk")); #ifndef WIN32 @@ -936,12 +936,16 @@ bool AppInitParameterInteraction() nScriptCheckThreads = MAX_SCRIPTCHECK_THREADS; // block pruning; get the amount of disk space (in MiB) to allot for block & undo files - int64_t nSignedPruneTarget = GetArg("-prune", 0) * 1024 * 1024; - if (nSignedPruneTarget < 0) { + int64_t nPruneArg = GetArg("-prune", 0); + if (nPruneArg < 0) { return InitError(_("Prune cannot be configured with a negative value.")); } - nPruneTarget = (uint64_t) nSignedPruneTarget; - if (nPruneTarget) { + nPruneTarget = (uint64_t) nPruneArg * 1024 * 1024; + if (nPruneArg == 1) { // manual pruning: -prune=1 + LogPrintf("Block pruning enabled. Use RPC call pruneblockchain(height) to manually prune block and undo files.\n"); + nPruneTarget = std::numeric_limits<uint64_t>::max(); + fPruneMode = true; + } else if (nPruneTarget) { if (nPruneTarget < MIN_DISK_SPACE_FOR_BLOCK_FILES) { return InitError(strprintf(_("Prune configured below the minimum of %d MiB. Please use a higher number."), MIN_DISK_SPACE_FOR_BLOCK_FILES / 1024 / 1024)); } diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 59748dd200..caa92bef9d 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -814,6 +814,46 @@ static bool GetUTXOStats(CCoinsView *view, CCoinsStats &stats) return true; } +UniValue pruneblockchain(const JSONRPCRequest& request) +{ + if (request.fHelp || request.params.size() != 1) + throw runtime_error( + "pruneblockchain\n" + "\nArguments:\n" + "1. \"height\" (numeric, required) The block height to prune up to. May be set to a discrete height, or to a unix timestamp to prune based on block time.\n"); + + if (!fPruneMode) + throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Cannot prune blocks because node is not in prune mode."); + + LOCK(cs_main); + + int heightParam = request.params[0].get_int(); + if (heightParam < 0) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative block height."); + + // Height value more than a billion is too high to be a block height, and + // too low to be a block time (corresponds to timestamp from Sep 2001). + if (heightParam > 1000000000) { + CBlockIndex* pindex = chainActive.FindLatestBefore(heightParam); + if (!pindex) { + throw JSONRPCError(RPC_INTERNAL_ERROR, "Could not find block before specified timestamp."); + } + heightParam = pindex->nHeight; + } + + unsigned int height = (unsigned int) heightParam; + unsigned int chainHeight = (unsigned int) chainActive.Height(); + if (chainHeight < Params().PruneAfterHeight()) + throw JSONRPCError(RPC_INTERNAL_ERROR, "Blockchain is too short for pruning."); + else if (height > chainHeight) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Blockchain is shorter than the attempted prune height."); + else if (height > chainHeight - MIN_BLOCKS_TO_KEEP) + LogPrint("rpc", "Attempt to prune blocks close to the tip. Retaining the minimum number of blocks."); + + PruneBlockFilesManual(height); + return NullUniValue; +} + UniValue gettxoutsetinfo(const JSONRPCRequest& request) { if (request.fHelp || request.params.size() != 0) @@ -1384,6 +1424,7 @@ static const CRPCCommand commands[] = { "blockchain", "getrawmempool", &getrawmempool, true, {"verbose"} }, { "blockchain", "gettxout", &gettxout, true, {"txid","n","include_mempool"} }, { "blockchain", "gettxoutsetinfo", &gettxoutsetinfo, true, {} }, + { "blockchain", "pruneblockchain", &pruneblockchain, true, {"height"} }, { "blockchain", "verifychain", &verifychain, true, {"checklevel","nblocks"} }, { "blockchain", "preciousblock", &preciousblock, true, {"blockhash"} }, diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp index 422d005f0c..5d3c458455 100644 --- a/src/rpc/client.cpp +++ b/src/rpc/client.cpp @@ -103,6 +103,7 @@ static const CRPCConvertParam vRPCConvertParams[] = { "importmulti", 1, "options" }, { "verifychain", 0, "checklevel" }, { "verifychain", 1, "nblocks" }, + { "pruneblockchain", 0, "height" }, { "keypoolrefill", 0, "newsize" }, { "getrawmempool", 0, "verbose" }, { "estimatefee", 0, "nblocks" }, diff --git a/src/txmempool.cpp b/src/txmempool.cpp index 4f4540a1fc..373687430b 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -581,8 +581,8 @@ void CTxMemPool::removeConflicts(const CTransaction &tx) const CTransaction &txConflict = *it->second; if (txConflict != tx) { - removeRecursive(txConflict); ClearPrioritisation(txConflict.GetHash()); + removeRecursive(txConflict); } } } diff --git a/src/validation.cpp b/src/validation.cpp index 37a4186e0a..109a71fe74 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -185,7 +185,8 @@ enum FlushStateMode { }; // See definition for documentation -bool static FlushStateToDisk(CValidationState &state, FlushStateMode mode); +bool static FlushStateToDisk(CValidationState &state, FlushStateMode mode, int nManualPruneHeight=0); +void FindFilesToPruneManual(std::set<int>& setFilesToPrune, int nManualPruneHeight); bool IsFinalTx(const CTransaction &tx, int nBlockHeight, int64_t nBlockTime) { @@ -1934,7 +1935,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin * if they're too large, if it's been a while since the last write, * or always and in all cases if we're in prune mode and are deleting files. */ -bool static FlushStateToDisk(CValidationState &state, FlushStateMode mode) { +bool static FlushStateToDisk(CValidationState &state, FlushStateMode mode, int nManualPruneHeight) { int64_t nMempoolUsage = mempool.DynamicMemoryUsage(); const CChainParams& chainparams = Params(); LOCK2(cs_main, cs_LastBlockFile); @@ -1944,9 +1945,13 @@ bool static FlushStateToDisk(CValidationState &state, FlushStateMode mode) { std::set<int> setFilesToPrune; bool fFlushForPrune = false; try { - if (fPruneMode && fCheckForPruning && !fReindex) { - FindFilesToPrune(setFilesToPrune, chainparams.PruneAfterHeight()); - fCheckForPruning = false; + if (fPruneMode && (fCheckForPruning || nManualPruneHeight > 0) && !fReindex) { + if (nManualPruneHeight > 0) { + FindFilesToPruneManual(setFilesToPrune, nManualPruneHeight); + } else { + FindFilesToPrune(setFilesToPrune, chainparams.PruneAfterHeight()); + fCheckForPruning = false; + } if (!setFilesToPrune.empty()) { fFlushForPrune = true; if (!fHavePruned) { @@ -3247,6 +3252,35 @@ void UnlinkPrunedFiles(std::set<int>& setFilesToPrune) } } +/* Calculate the block/rev files to delete based on height specified by user with RPC command pruneblockchain */ +void FindFilesToPruneManual(std::set<int>& setFilesToPrune, int nManualPruneHeight) +{ + assert(fPruneMode && nManualPruneHeight > 0); + + LOCK2(cs_main, cs_LastBlockFile); + if (chainActive.Tip() == NULL) + return; + + // last block to prune is the lesser of (user-specified height, MIN_BLOCKS_TO_KEEP from the tip) + unsigned int nLastBlockWeCanPrune = min((unsigned)nManualPruneHeight, chainActive.Tip()->nHeight - MIN_BLOCKS_TO_KEEP); + int count=0; + for (int fileNumber = 0; fileNumber < nLastBlockFile; fileNumber++) { + if (vinfoBlockFile[fileNumber].nSize == 0 || vinfoBlockFile[fileNumber].nHeightLast > nLastBlockWeCanPrune) + continue; + PruneOneBlockFile(fileNumber); + setFilesToPrune.insert(fileNumber); + count++; + } + LogPrintf("Prune (Manual): prune_height=%d removed %d blk/rev pairs\n", nLastBlockWeCanPrune, count); +} + +/* This function is called from the RPC code for pruneblockchain */ +void PruneBlockFilesManual(int nManualPruneHeight) +{ + CValidationState state; + FlushStateToDisk(state, FLUSH_STATE_NONE, nManualPruneHeight); +} + /* Calculate the block/rev files that should be deleted to remain under target*/ void FindFilesToPrune(std::set<int>& setFilesToPrune, uint64_t nPruneAfterHeight) { diff --git a/src/validation.h b/src/validation.h index 631602a701..f5e76c7d31 100644 --- a/src/validation.h +++ b/src/validation.h @@ -309,6 +309,8 @@ CBlockIndex * InsertBlockIndex(uint256 hash); void FlushStateToDisk(); /** Prune block files and flush state to disk. */ void PruneAndFlush(); +/** Prune block files up to a given height */ +void PruneBlockFilesManual(int nPruneUpToHeight); /** (try to) add transaction to memory pool **/ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransactionRef &tx, bool fLimitFree, |