diff --git a/src/blockfilter.h b/src/blockfilter.h --- a/src/blockfilter.h +++ b/src/blockfilter.h @@ -97,6 +97,12 @@ bool BlockFilterTypeByName(const std::string &name, BlockFilterType &filter_type); +/** Get a list of known filter types. */ +const std::vector &AllBlockFilterTypes(); + +/** Get a comma-separated list of known filter type names. */ +const std::string &ListBlockFilterTypes(); + /** * Complete block filter struct as defined in BIP 157. Serialization matches * payload of "cfilter" messages. diff --git a/src/blockfilter.cpp b/src/blockfilter.cpp --- a/src/blockfilter.cpp +++ b/src/blockfilter.cpp @@ -2,6 +2,9 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#include +#include + #include #include #include @@ -215,6 +218,38 @@ return false; } +const std::vector &AllBlockFilterTypes() { + static std::vector types; + + static std::once_flag flag; + std::call_once(flag, []() { + types.reserve(g_filter_types.size()); + for (auto entry : g_filter_types) { + types.push_back(entry.first); + } + }); + + return types; +} + +const std::string &ListBlockFilterTypes() { + static std::string type_list; + + static std::once_flag flag; + std::call_once(flag, []() { + std::stringstream ret; + bool first = true; + for (auto entry : g_filter_types) { + if (!first) ret << ", "; + ret << entry.second; + first = false; + } + type_list = ret.str(); + }); + + return type_list; +} + static GCSFilter::ElementSet BasicFilterElements(const CBlock &block, const CBlockUndo &block_undo) { GCSFilter::ElementSet elements; diff --git a/src/init.cpp b/src/init.cpp --- a/src/init.cpp +++ b/src/init.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -23,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -197,6 +199,7 @@ if (g_txindex) { g_txindex->Interrupt(); } + ForEachBlockFilterIndex([](BlockFilterIndex &index) { index.Interrupt(); }); } void Shutdown(NodeContext &node) { @@ -244,6 +247,7 @@ if (g_txindex) { g_txindex->Stop(); } + ForEachBlockFilterIndex([](BlockFilterIndex &index) { index.Stop(); }); StopTorControl(); @@ -261,6 +265,7 @@ g_connman.reset(); g_banman.reset(); g_txindex.reset(); + DestroyAllBlockFilterIndexes(); if (::g_mempool.IsLoaded() && gArgs.GetArg("-persistmempool", DEFAULT_PERSIST_MEMPOOL)) { @@ -558,6 +563,13 @@ "getrawtransaction rpc call (default: %d)", DEFAULT_TXINDEX), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); + gArgs.AddArg("-blockfilterindex=", + strprintf("Maintain an index of compact filters by block " + "(default: %s, values: %s).", + DEFAULT_BLOCKFILTERINDEX, ListBlockFilterTypes()) + + " If is not supplied or if = 1, indexes for " + "all known types are enabled.", + ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); gArgs.AddArg("-usecashaddr", "Use Cash Address for destination encoding instead of base58 " "(activate by default on Jan, 14)", @@ -1548,6 +1560,7 @@ int nFD; ServiceFlags nLocalServices = ServiceFlags(NODE_NETWORK | NODE_NETWORK_LIMITED); int64_t peer_connect_timeout; +std::vector g_enabled_filter_types; } // namespace @@ -1648,12 +1661,36 @@ gArgs.GetArg("-blocksdir", ""))); } + // parse and validate enabled filter types + std::string blockfilterindex_value = + gArgs.GetArg("-blockfilterindex", DEFAULT_BLOCKFILTERINDEX); + if (blockfilterindex_value == "" || blockfilterindex_value == "1") { + g_enabled_filter_types = AllBlockFilterTypes(); + } else if (blockfilterindex_value != "0") { + const std::vector names = + gArgs.GetArgs("-blockfilterindex"); + g_enabled_filter_types.reserve(names.size()); + for (const auto &name : names) { + BlockFilterType filter_type; + if (!BlockFilterTypeByName(name, filter_type)) { + return InitError(strprintf( + _("Unknown -blockfilterindex value %s.").translated, name)); + } + g_enabled_filter_types.push_back(filter_type); + } + } + // if using block pruning, then disallow txindex if (gArgs.GetArg("-prune", 0)) { if (gArgs.GetBoolArg("-txindex", DEFAULT_TXINDEX)) { return InitError( _("Prune mode is incompatible with -txindex.").translated); } + if (!g_enabled_filter_types.empty()) { + return InitError( + _("Prune mode is incompatible with -blockfilterindex.") + .translated); + } } // -bind and -whitebind can't be set when not listening @@ -2304,6 +2341,14 @@ ? nMaxTxIndexCache << 20 : 0); nTotalCache -= nTxIndexCache; + int64_t filter_index_cache = 0; + if (!g_enabled_filter_types.empty()) { + size_t n_indexes = g_enabled_filter_types.size(); + int64_t max_cache = + std::min(nTotalCache / 8, max_filter_index_cache << 20); + filter_index_cache = max_cache / n_indexes; + nTotalCache -= filter_index_cache * n_indexes; + } // use 25%-50% of the remainder for disk cache int64_t nCoinDBCache = std::min(nTotalCache / 2, (nTotalCache / 4) + (1 << 23)); @@ -2321,6 +2366,11 @@ LogPrintf("* Using %.1fMiB for transaction index database\n", nTxIndexCache * (1.0 / 1024 / 1024)); } + for (BlockFilterType filter_type : g_enabled_filter_types) { + LogPrintf("* Using %.1f MiB for %s block filter index database\n", + filter_index_cache * (1.0 / 1024 / 1024), + BlockFilterTypeName(filter_type)); + } LogPrintf("* Using %.1fMiB for chain state database\n", nCoinDBCache * (1.0 / 1024 / 1024)); LogPrintf("* Using %.1fMiB for in-memory UTXO set (plus up to %.1fMiB of " @@ -2539,6 +2589,11 @@ g_txindex->Start(); } + for (const auto &filter_type : g_enabled_filter_types) { + InitBlockFilterIndex(filter_type, filter_index_cache, false, fReindex); + GetBlockFilterIndex(filter_type)->Start(); + } + // Step 9: load wallet for (const auto &client : node.chain_clients) { if (!client->load(chainparams)) { diff --git a/src/txdb.h b/src/txdb.h --- a/src/txdb.h +++ b/src/txdb.h @@ -43,6 +43,8 @@ // a meaningful difference: // https://github.com/bitcoin/bitcoin/pull/8273#issuecomment-229601991 static const int64_t nMaxTxIndexCache = 1024; +//! Max memory allocated to all block filter index caches combined in MiB. +static const int64_t max_filter_index_cache = 1024; //! Max memory allocated to coin DB specific cache (MiB) static const int64_t nMaxCoinsDBCache = 8; diff --git a/src/validation.h b/src/validation.h --- a/src/validation.h +++ b/src/validation.h @@ -138,6 +138,7 @@ static const bool DEFAULT_CHECKPOINTS_ENABLED = true; static const bool DEFAULT_TXINDEX = false; +static const char *const DEFAULT_BLOCKFILTERINDEX = "0"; static const unsigned int DEFAULT_BANSCORE_THRESHOLD = 100; /** Default for -persistmempool */