Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F13711329
D11599.id34003.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
11 KB
Subscribers
None
D11599.id34003.diff
View Options
diff --git a/contrib/debian/examples/bitcoin.conf b/contrib/debian/examples/bitcoin.conf
--- a/contrib/debian/examples/bitcoin.conf
+++ b/contrib/debian/examples/bitcoin.conf
@@ -127,8 +127,11 @@
# be validated sooner.
#paytxfee=0.00
+# Maintain coinstats index used by the gettxoutsetinfo RPC (default: 0).
+#coinstatsindex=1
+
# Enable pruning to reduce storage requirements by deleting old blocks.
-# This mode is incompatible with -txindex and -rescan.
+# This mode is incompatible with -txindex, -coinstatsindex and -rescan.
# 0 = default (no pruning).
# 1 = allows manual pruning via RPC.
# >=550 = target to stay under in MiB.
diff --git a/doc/release-notes.md b/doc/release-notes.md
--- a/doc/release-notes.md
+++ b/doc/release-notes.md
@@ -5,3 +5,5 @@
<https://download.bitcoinabc.org/0.25.8/>
This release includes the following features and fixes:
+- Users can start their node with the option `-coinstatsindex` which syncs an
+ index of coin statistics in the background.
diff --git a/src/init.cpp b/src/init.cpp
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -29,6 +29,7 @@
#include <httprpc.h>
#include <httpserver.h>
#include <index/blockfilterindex.h>
+#include <index/coinstatsindex.h>
#include <index/txindex.h>
#include <interfaces/chain.h>
#include <interfaces/node.h>
@@ -175,6 +176,9 @@
g_txindex->Interrupt();
}
ForEachBlockFilterIndex([](BlockFilterIndex &index) { index.Interrupt(); });
+ if (g_coin_stats_index) {
+ g_coin_stats_index->Interrupt();
+ }
}
void Shutdown(NodeContext &node) {
@@ -269,6 +273,10 @@
g_txindex->Stop();
g_txindex.reset();
}
+ if (g_coin_stats_index) {
+ g_coin_stats_index->Stop();
+ g_coin_stats_index.reset();
+ }
ForEachBlockFilterIndex([](BlockFilterIndex &index) { index.Stop(); });
DestroyAllBlockFilterIndexes();
@@ -467,6 +475,11 @@
" not affected. (default: %u)",
DEFAULT_BLOCKSONLY),
ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ argsman.AddArg("-coinstatsindex",
+ strprintf("Maintain coinstats index used by the "
+ "gettxoutsetinfo RPC (default: %u)",
+ DEFAULT_COINSTATSINDEX),
+ ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg(
"-conf=<file>",
strprintf("Specify path to read-only configuration file. Relative "
@@ -567,11 +580,12 @@
"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, 1 "
- "= allow manual pruning via RPC, >=%u = automatically prune "
- "block files to stay under the specified target size in MiB)",
+ "This mode is incompatible with -txindex, -coinstatsindex "
+ "and -rescan. Warning: Reverting this setting requires "
+ "re-downloading the entire blockchain. (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),
ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg(
@@ -1846,11 +1860,15 @@
nLocalServices = ServiceFlags(nLocalServices | NODE_COMPACT_FILTERS);
}
- // if using block pruning, then disallow txindex
+ // if using block pruning, then disallow txindex and coinstatsindex
if (args.GetArg("-prune", 0)) {
if (args.GetBoolArg("-txindex", DEFAULT_TXINDEX)) {
return InitError(_("Prune mode is incompatible with -txindex."));
}
+ if (args.GetBoolArg("-coinstatsindex", DEFAULT_COINSTATSINDEX)) {
+ return InitError(
+ _("Prune mode is incompatible with -coinstatsindex."));
+ }
}
// -bind and -whitebind can't be set when not listening
@@ -2855,6 +2873,11 @@
GetBlockFilterIndex(filter_type)->Start();
}
+ if (args.GetBoolArg("-coinstatsindex", DEFAULT_COINSTATSINDEX)) {
+ g_coin_stats_index = std::make_unique<CoinStatsIndex>(
+ /* cache size */ 0, false, fReindex);
+ g_coin_stats_index->Start();
+ }
// Step 9: load wallet
for (const auto &client : node.chain_clients) {
if (!client->load()) {
diff --git a/src/node/coinstats.cpp b/src/node/coinstats.cpp
--- a/src/node/coinstats.cpp
+++ b/src/node/coinstats.cpp
@@ -8,6 +8,7 @@
#include <coins.h>
#include <crypto/muhash.h>
#include <hash.h>
+#include <index/coinstatsindex.h>
#include <primitives/txid.h>
#include <serialize.h>
#include <util/check.h>
@@ -91,15 +92,24 @@
const std::function<void()> &interruption_point) {
std::unique_ptr<CCoinsViewCursor> pcursor(view->Cursor());
assert(pcursor);
-
stats.hashBlock = pcursor->GetBestBlock();
+
+ const CBlockIndex *pindex;
{
LOCK(cs_main);
assert(std::addressof(g_chainman.m_blockman) ==
std::addressof(blockman));
- const CBlockIndex *block = blockman.LookupBlockIndex(stats.hashBlock);
- stats.nHeight = Assert(block)->nHeight;
+ pindex = blockman.LookupBlockIndex(stats.hashBlock);
+ stats.nHeight = Assert(pindex)->nHeight;
+ }
+
+ // Use CoinStatsIndex if it is available and a hash_type of Muhash or None
+ // was requested
+ if ((stats.m_hash_type == CoinStatsHashType::MUHASH ||
+ stats.m_hash_type == CoinStatsHashType::NONE) &&
+ g_coin_stats_index) {
+ return g_coin_stats_index->LookUpStats(pindex, stats);
}
PrepareHash(hash_obj, stats);
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -16,6 +16,7 @@
#include <core_io.h>
#include <hash.h>
#include <index/blockfilterindex.h>
+#include <index/coinstatsindex.h>
#include <node/blockstorage.h>
#include <node/coinstats.h>
#include <node/context.h>
@@ -1321,6 +1322,19 @@
ret.pushKV("disk_size", stats.nDiskSize);
ret.pushKV("total_amount", stats.nTotalAmount);
} else {
+ if (g_coin_stats_index) {
+ const IndexSummary summary{
+ g_coin_stats_index->GetSummary()};
+
+ if (!summary.synced) {
+ throw JSONRPCError(
+ RPC_INTERNAL_ERROR,
+ strprintf("Unable to read UTXO set because "
+ "coinstatsindex is still syncing. "
+ "Current height: %d",
+ summary.best_block_height));
+ }
+ }
throw JSONRPCError(RPC_INTERNAL_ERROR,
"Unable to read UTXO set");
}
diff --git a/src/validation.h b/src/validation.h
--- a/src/validation.h
+++ b/src/validation.h
@@ -89,6 +89,7 @@
static const int64_t DEFAULT_MAX_TIP_AGE = 24 * 60 * 60;
static const bool DEFAULT_CHECKPOINTS_ENABLED = true;
static const bool DEFAULT_TXINDEX = false;
+static constexpr bool DEFAULT_COINSTATSINDEX{false};
static const char *const DEFAULT_BLOCKFILTERINDEX = "0";
/** Default for -persistmempool */
diff --git a/test/functional/feature_coinstatsindex.py b/test/functional/feature_coinstatsindex.py
new file mode 100755
--- /dev/null
+++ b/test/functional/feature_coinstatsindex.py
@@ -0,0 +1,77 @@
+#!/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 coinstatsindex across nodes.
+
+Test that the values returned by gettxoutsetinfo are consistent
+between a node running the coinstatsindex and a node without
+the index.
+"""
+
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import assert_equal, try_rpc
+
+
+class CoinStatsIndexTest(BitcoinTestFramework):
+ def set_test_params(self):
+ self.setup_clean_chain = True
+ self.num_nodes = 2
+ self.supports_cli = False
+ self.extra_args = [
+ [],
+ ["-coinstatsindex"]
+ ]
+
+ def skip_test_if_missing_module(self):
+ self.skip_if_no_wallet()
+
+ def run_test(self):
+ self._test_coin_stats_index()
+
+ def _test_coin_stats_index(self):
+ node = self.nodes[0]
+ index_node = self.nodes[1]
+ # Both none and muhash options allow the usage of the index
+ index_hash_options = ['none', 'muhash']
+
+ # Generate a normal transaction and mine it
+ node.generate(101)
+ address = self.nodes[0].get_deterministic_priv_key().address
+ node.sendtoaddress(
+ address=address,
+ amount=10_000_000,
+ subtractfeefromamount=True)
+ node.generate(1)
+
+ self.sync_blocks(timeout=120)
+
+ self.log.info(
+ "Test that gettxoutsetinfo() output is consistent with or without coinstatsindex option")
+ self.wait_until(lambda: not try_rpc(-32603,
+ "Unable to read UTXO set", node.gettxoutsetinfo))
+ res0 = node.gettxoutsetinfo('none')
+
+ # The fields 'disk_size' and 'transactions' do not work on the index, so
+ # don't check them.
+ del res0['disk_size'], res0['transactions']
+
+ self.wait_until(lambda: not try_rpc(-32603,
+ "Unable to read UTXO set",
+ index_node.gettxoutsetinfo,
+ 'muhash'))
+ for hash_option in index_hash_options:
+ res1 = index_node.gettxoutsetinfo(hash_option)
+ res1.pop('muhash', None)
+
+ # The fields 'disk_size' and 'transactions' do not work on the index
+ # so don't check them (they will be removed from the index in the
+ # next commit).
+ del res1['disk_size'], res1['transactions']
+
+ # Everything left should be the same
+ assert_equal(res1, res0)
+
+
+if __name__ == '__main__':
+ CoinStatsIndexTest().main()
diff --git a/test/functional/test_framework/test_framework.py b/test/functional/test_framework/test_framework.py
--- a/test/functional/test_framework/test_framework.py
+++ b/test/functional/test_framework/test_framework.py
@@ -794,8 +794,8 @@
# Remove empty wallets dir
os.rmdir(cache_path('wallets'))
for entry in os.listdir(cache_path()):
- # Only keep chainstate and blocks folder
- if entry not in ['chainstate', 'blocks']:
+ # Only keep indexes, chainstate and blocks folders
+ if entry not in ['chainstate', 'blocks', 'indexes']:
os.remove(cache_path(entry))
for i in range(self.num_nodes):
diff --git a/test/lint/lint-circular-dependencies.sh b/test/lint/lint-circular-dependencies.sh
--- a/test/lint/lint-circular-dependencies.sh
+++ b/test/lint/lint-circular-dependencies.sh
@@ -17,6 +17,7 @@
"node/blockstorage -> validation -> node/blockstorage"
"index/blockfilterindex -> node/blockstorage -> validation -> index/blockfilterindex"
"index/base -> validation -> index/blockfilterindex -> index/base"
+ "index/coinstatsindex -> node/coinstats -> index/coinstatsindex"
"qt/addresstablemodel -> qt/walletmodel -> qt/addresstablemodel"
"qt/bitcoingui -> qt/walletframe -> qt/bitcoingui"
"qt/recentrequeststablemodel -> qt/walletmodel -> qt/recentrequeststablemodel"
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sat, Apr 26, 11:38 (6 h, 18 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
5570824
Default Alt Text
D11599.id34003.diff (11 KB)
Attached To
D11599: index: Coinstats index can be activated with command line flag
Event Timeline
Log In to Comment