Merge bitcoin/bitcoin#24410: [kernel 2a/n] Split hashing/index GetUTXOStats codepaths, decouple from coinstatsindex

664a14ba7c coinstats: Move GetUTXOStats to rpc/blockchain (Carl Dong)
f100687566 kernel: Use ComputeUTXOStats in validation (Carl Dong)
faa52387e8 style-only: Rearrange using decls after scripted-diff (Carl Dong)
f329a9298c scripted-diff: Move src/kernel/coinstats to kernel:: (Carl Dong)
0e54456f04 Use only kernel/coinstats.h in index/coinstatsindex.h (Carl Dong)
80970985c9 coinstats: Split node/coinstats.h to kernel/coinstats.h (Carl Dong)
35f73ce4b2 coinstats: Move hasher codepath to kernel/coinstats (Carl Dong)
b7634fe02b Move logic from LookupUTXOStatsWithIndex to CoinStatsIndex::LookUpStats (Carl Dong)
1352e410a5 coinstats: Separate hasher/index lookup codepaths (Carl Dong)
524463daf6 coinstats: Return purely out-param CCoinsStats (Carl Dong)
46eb9fc56a coinstats: Extract index_requested in-member to in-param (Carl Dong)
a789f3f2b8 coinstats: Extract hash_type in-member to in-param (Carl Dong)
102294898d includes: Remove rpc/util.h -> node/coinstats.h (Carl Dong)
0848db9c35 fuzz: Remove useless GetUTXOStats fuzz case (Carl Dong)
52b1939993 kernel: Remove unnecessary blockfilter{index,}.cpp (Carl Dong)

Pull request description:

  Part of: #24303
  Depends on: #24322

  The `GetUTXOStats` function has 2 codepaths:
    - One which queries the `CoinStatsIndex` for the UTXO hash
    - One which actually performs the hashing

  For `libbitcoinkernel`, the only place where we call `GetUTXOStats` is in `PopulateAndValidateSnapshots`, which uses the `SHA256D` hash, and is therefore unable to use the `CoinStatsIndex` since that only provides `MuHash` hashes. Not that I think indices necessarily belong in `libbitcoinkernel` anyway.

  This PR separates these 2 aforementioned codepaths of `GetUTXOStats`, uses the hashing codepath in `PopulateAndValidateSnapshots`, and removes the need to link in `index/coinstatsindex.cpp` and `node/coinstats.cpp`.

  -----

  Logistically, this PR:
  - Extracts out the `index_requested` and `hash_type` members of `CoinStats`, which served as "in-params" to `GetUTXOStats` embedded within the `CoinStats` struct. This allows `CoinStats` to only consist of "out-param" members, and be returned by `GetUTXOStats` without needing to be an "in-out" param
  - Introduce the purely virtual `UTXOHashers` class, with 3 implementations: `SHA256DHasher`, `MuHashHasher`, and `NullHasher`. These replace the existing template-based polymorphism.
  - Split `GetUTXOStats` into:
      - `CalculateUTXOStatsWithHasher(UTXOHasher&, ...)`, and
      - `LookupUTXOStatsWithIndex(CoinStatsIndex&, ...)`
  - Use `CalculateUTXOStatsWithHasher` directly where appropriate (`src/validation.cpp` and `src/fuzz`)
  - Move `GetUTXOStats` to `rpc/blockchain`, which is the only place that depends on `GetUTXOStats`'s weird fallback behaviour
  - Move `LookupUTXOStatsWithIndex` to `index/coinstatsindex`

  Code organization:
  - `src/`
    - `kernel/` → only contains the hashing codepath
      - `coinstats.cpp` → hashing codepath implementations
      - `coinstats.h` → header for `kernel/coinstats.cpp`
    - `index/` → only contains the index codepath
      - `coinstatsindex.cpp` → index codepath implementations
      - `coinstatsindex.h`
    - `validation.cpp` → only uses the hashing codepath
    - `rpc/blockchain.cpp` → uses both the hashing and index codepath, old `GetUTXOStats` fallback logic moved here as static
    - `test/fuzz/coins_view.cpp` → only uses the hashing codepath

  TODOs:
  - [x] Commit messages could be fleshed out more

  Would love any feedback!

ACKs for top commit:
  laanwj:
    Code review ACK 664a14ba7c

Tree-SHA512: 18722c7bd279174d2d1881fec33ea04a9b261aae1c12e998cf434ef297d8ded47de69c526c8033a2ba7abc93ba3d2ff5faf4ce05e8888c725c31cf885ce3ef73
This commit is contained in:
laanwj committed 2022-05-24 14:43:00 +02:00
commit 7008087548
11 files changed
+145 -131

No files matched your search

+3 -7
View File
@@ -172,6 +172,7 @@ BITCOIN_CORE_H = \
interfaces/node.h \ interfaces/node.h \
interfaces/wallet.h \ interfaces/wallet.h \
kernel/chainstatemanager_opts.h \ kernel/chainstatemanager_opts.h \
kernel/coinstats.h \
key.h \ key.h \
key_io.h \ key_io.h \
logging.h \ logging.h \
@@ -191,7 +192,6 @@ BITCOIN_CORE_H = \
node/caches.h \ node/caches.h \
node/chainstate.h \ node/chainstate.h \
node/coin.h \ node/coin.h \
node/coinstats.h \
node/context.h \ node/context.h \
node/miner.h \ node/miner.h \
node/minisketchwrapper.h \ node/minisketchwrapper.h \
@@ -357,6 +357,7 @@ libbitcoin_node_a_SOURCES = \
index/coinstatsindex.cpp \ index/coinstatsindex.cpp \
index/txindex.cpp \ index/txindex.cpp \
init.cpp \ init.cpp \
kernel/coinstats.cpp \
mapport.cpp \ mapport.cpp \
net.cpp \ net.cpp \
netgroup.cpp \ netgroup.cpp \
@@ -365,7 +366,6 @@ libbitcoin_node_a_SOURCES = \
node/caches.cpp \ node/caches.cpp \
node/chainstate.cpp \ node/chainstate.cpp \
node/coin.cpp \ node/coin.cpp \
node/coinstats.cpp \
node/context.cpp \ node/context.cpp \
node/interfaces.cpp \ node/interfaces.cpp \
node/miner.cpp \ node/miner.cpp \
@@ -853,7 +853,6 @@ endif
libbitcoinkernel_la_SOURCES = \ libbitcoinkernel_la_SOURCES = \
kernel/bitcoinkernel.cpp \ kernel/bitcoinkernel.cpp \
arith_uint256.cpp \ arith_uint256.cpp \
blockfilter.cpp \
chain.cpp \ chain.cpp \
chainparamsbase.cpp \ chainparamsbase.cpp \
chainparams.cpp \ chainparams.cpp \
@@ -871,15 +870,12 @@ libbitcoinkernel_la_SOURCES = \
flatfile.cpp \ flatfile.cpp \
fs.cpp \ fs.cpp \
hash.cpp \ hash.cpp \
index/base.cpp \
index/blockfilterindex.cpp \
index/coinstatsindex.cpp \
init/common.cpp \ init/common.cpp \
kernel/coinstats.cpp \
key.cpp \ key.cpp \
logging.cpp \ logging.cpp \
node/blockstorage.cpp \ node/blockstorage.cpp \
node/chainstate.cpp \ node/chainstate.cpp \
node/coinstats.cpp \
node/ui_interface.cpp \ node/ui_interface.cpp \
policy/feerate.cpp \ policy/feerate.cpp \
policy/fees.cpp \ policy/fees.cpp \
+23 -19
View File
@@ -12,10 +12,11 @@
#include <undo.h> #include <undo.h>
#include <validation.h> #include <validation.h>
using node::CCoinsStats; using kernel::CCoinsStats;
using node::GetBogoSize; using kernel::GetBogoSize;
using kernel::TxOutSer;
using node::ReadBlockFromDisk; using node::ReadBlockFromDisk;
using node::TxOutSer;
using node::UndoReadFromDisk; using node::UndoReadFromDisk;
static constexpr uint8_t DB_BLOCK_HASH{'s'}; static constexpr uint8_t DB_BLOCK_HASH{'s'};
@@ -316,28 +317,31 @@ static bool LookUpOne(const CDBWrapper& db, const CBlockIndex* block_index, DBVa
return db.Read(DBHashKey(block_index->GetBlockHash()), result); return db.Read(DBHashKey(block_index->GetBlockHash()), result);
} }
bool CoinStatsIndex::LookUpStats(const CBlockIndex* block_index, CCoinsStats& coins_stats) const std::optional<CCoinsStats> CoinStatsIndex::LookUpStats(const CBlockIndex* block_index) const
{ {
CCoinsStats stats{Assert(block_index)->nHeight, block_index->GetBlockHash()};
stats.index_used = true;
DBVal entry; DBVal entry;
if (!LookUpOne(*m_db, block_index, entry)) { if (!LookUpOne(*m_db, block_index, entry)) {
return false; return std::nullopt;
} }
coins_stats.hashSerialized = entry.muhash; stats.hashSerialized = entry.muhash;
coins_stats.nTransactionOutputs = entry.transaction_output_count; stats.nTransactionOutputs = entry.transaction_output_count;
coins_stats.nBogoSize = entry.bogo_size; stats.nBogoSize = entry.bogo_size;
coins_stats.total_amount = entry.total_amount; stats.total_amount = entry.total_amount;
coins_stats.total_subsidy = entry.total_subsidy; stats.total_subsidy = entry.total_subsidy;
coins_stats.total_unspendable_amount = entry.total_unspendable_amount; stats.total_unspendable_amount = entry.total_unspendable_amount;
coins_stats.total_prevout_spent_amount = entry.total_prevout_spent_amount; stats.total_prevout_spent_amount = entry.total_prevout_spent_amount;
coins_stats.total_new_outputs_ex_coinbase_amount = entry.total_new_outputs_ex_coinbase_amount; stats.total_new_outputs_ex_coinbase_amount = entry.total_new_outputs_ex_coinbase_amount;
coins_stats.total_coinbase_amount = entry.total_coinbase_amount; stats.total_coinbase_amount = entry.total_coinbase_amount;
coins_stats.total_unspendables_genesis_block = entry.total_unspendables_genesis_block; stats.total_unspendables_genesis_block = entry.total_unspendables_genesis_block;
coins_stats.total_unspendables_bip30 = entry.total_unspendables_bip30; stats.total_unspendables_bip30 = entry.total_unspendables_bip30;
coins_stats.total_unspendables_scripts = entry.total_unspendables_scripts; stats.total_unspendables_scripts = entry.total_unspendables_scripts;
coins_stats.total_unspendables_unclaimed_rewards = entry.total_unspendables_unclaimed_rewards; stats.total_unspendables_unclaimed_rewards = entry.total_unspendables_unclaimed_rewards;
return true; return stats;
} }
bool CoinStatsIndex::Init() bool CoinStatsIndex::Init()
+2 -2
View File
@@ -9,7 +9,7 @@
#include <crypto/muhash.h> #include <crypto/muhash.h>
#include <flatfile.h> #include <flatfile.h>
#include <index/base.h> #include <index/base.h>
#include <node/coinstats.h> #include <kernel/coinstats.h>
/** /**
* CoinStatsIndex maintains statistics on the UTXO set. * CoinStatsIndex maintains statistics on the UTXO set.
@@ -56,7 +56,7 @@ public:
explicit CoinStatsIndex(size_t n_cache_size, bool f_memory = false, bool f_wipe = false); explicit CoinStatsIndex(size_t n_cache_size, bool f_memory = false, bool f_wipe = false);
// Look up stats for a specific block using CBlockIndex // Look up stats for a specific block using CBlockIndex
bool LookUpStats(const CBlockIndex* block_index, node::CCoinsStats& coins_stats) const; std::optional<kernel::CCoinsStats> LookUpStats(const CBlockIndex* block_index) const;
}; };
/// The global UTXO set hash object. /// The global UTXO set hash object.
@@ -1,14 +1,12 @@
// Copyright (c) 2010 Satoshi Nakamoto // Copyright (c) 2022 The Bitcoin Core developers
// Copyright (c) 2009-2021 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying // Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php. // file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <node/coinstats.h> #include <kernel/coinstats.h>
#include <coins.h> #include <coins.h>
#include <crypto/muhash.h> #include <crypto/muhash.h>
#include <hash.h> #include <hash.h>
#include <index/coinstatsindex.h>
#include <serialize.h> #include <serialize.h>
#include <uint256.h> #include <uint256.h>
#include <util/overflow.h> #include <util/overflow.h>
@@ -17,7 +15,12 @@
#include <map> #include <map>
namespace node { namespace kernel {
CCoinsStats::CCoinsStats(int block_height, const uint256& block_hash)
: nHeight(block_height),
hashBlock(block_hash) {}
// Database-independent metric indicating the UTXO set size // Database-independent metric indicating the UTXO set size
uint64_t GetBogoSize(const CScript& script_pub_key) uint64_t GetBogoSize(const CScript& script_pub_key)
{ {
@@ -93,24 +96,11 @@ static void ApplyStats(CCoinsStats& stats, const uint256& hash, const std::map<u
//! Calculate statistics about the unspent transaction output set //! Calculate statistics about the unspent transaction output set
template <typename T> template <typename T>
static bool GetUTXOStats(CCoinsView* view, BlockManager& blockman, CCoinsStats& stats, T hash_obj, const std::function<void()>& interruption_point, const CBlockIndex* pindex) static bool ComputeUTXOStats(CCoinsView* view, CCoinsStats& stats, T hash_obj, const std::function<void()>& interruption_point)
{ {
std::unique_ptr<CCoinsViewCursor> pcursor(view->Cursor()); std::unique_ptr<CCoinsViewCursor> pcursor(view->Cursor());
assert(pcursor); assert(pcursor);
if (!pindex) {
LOCK(cs_main);
pindex = blockman.LookupBlockIndex(view->GetBestBlock());
}
stats.nHeight = Assert(pindex)->nHeight;
stats.hashBlock = pindex->GetBlockHash();
// Use CoinStatsIndex if it is requested and 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 && stats.index_requested) {
stats.index_used = true;
return g_coin_stats_index->LookUpStats(pindex, stats);
}
PrepareHash(hash_obj, stats); PrepareHash(hash_obj, stats);
uint256 prevkey; uint256 prevkey;
@@ -141,25 +131,36 @@ static bool GetUTXOStats(CCoinsView* view, BlockManager& blockman, CCoinsStats&
FinalizeHash(hash_obj, stats); FinalizeHash(hash_obj, stats);
stats.nDiskSize = view->EstimateSize(); stats.nDiskSize = view->EstimateSize();
return true; return true;
} }
bool GetUTXOStats(CCoinsView* view, BlockManager& blockman, CCoinsStats& stats, const std::function<void()>& interruption_point, const CBlockIndex* pindex) std::optional<CCoinsStats> ComputeUTXOStats(CoinStatsHashType hash_type, CCoinsView* view, node::BlockManager& blockman, const std::function<void()>& interruption_point)
{ {
switch (stats.m_hash_type) { CBlockIndex* pindex = WITH_LOCK(::cs_main, return blockman.LookupBlockIndex(view->GetBestBlock()));
case(CoinStatsHashType::HASH_SERIALIZED): { CCoinsStats stats{Assert(pindex)->nHeight, pindex->GetBlockHash()};
CHashWriter ss(SER_GETHASH, PROTOCOL_VERSION);
return GetUTXOStats(view, blockman, stats, ss, interruption_point, pindex); bool success = [&]() -> bool {
switch (hash_type) {
case(CoinStatsHashType::HASH_SERIALIZED): {
CHashWriter ss(SER_GETHASH, PROTOCOL_VERSION);
return ComputeUTXOStats(view, stats, ss, interruption_point);
}
case(CoinStatsHashType::MUHASH): {
MuHash3072 muhash;
return ComputeUTXOStats(view, stats, muhash, interruption_point);
}
case(CoinStatsHashType::NONE): {
return ComputeUTXOStats(view, stats, nullptr, interruption_point);
}
} // no default case, so the compiler can warn about missing cases
assert(false);
}();
if (!success) {
return std::nullopt;
} }
case(CoinStatsHashType::MUHASH): { return stats;
MuHash3072 muhash;
return GetUTXOStats(view, blockman, stats, muhash, interruption_point, pindex);
}
case(CoinStatsHashType::NONE): {
return GetUTXOStats(view, blockman, stats, nullptr, interruption_point, pindex);
}
} // no default case, so the compiler can warn about missing cases
assert(false);
} }
// The legacy hash serializes the hashBlock // The legacy hash serializes the hashBlock
@@ -182,4 +183,5 @@ static void FinalizeHash(MuHash3072& muhash, CCoinsStats& stats)
stats.hashSerialized = out; stats.hashSerialized = out;
} }
static void FinalizeHash(std::nullptr_t, CCoinsStats& stats) {} static void FinalizeHash(std::nullptr_t, CCoinsStats& stats) {}
} // namespace node
} // namespace kernel
+10 -16
View File
@@ -1,10 +1,9 @@
// Copyright (c) 2010 Satoshi Nakamoto // Copyright (c) 2022 The Bitcoin Core developers
// Copyright (c) 2009-2021 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying // Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php. // file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_NODE_COINSTATS_H #ifndef BITCOIN_KERNEL_COINSTATS_H
#define BITCOIN_NODE_COINSTATS_H #define BITCOIN_KERNEL_COINSTATS_H
#include <chain.h> #include <chain.h>
#include <coins.h> #include <coins.h>
@@ -20,7 +19,7 @@ namespace node {
class BlockManager; class BlockManager;
} // namespace node } // namespace node
namespace node { namespace kernel {
enum class CoinStatsHashType { enum class CoinStatsHashType {
HASH_SERIALIZED, HASH_SERIALIZED,
MUHASH, MUHASH,
@@ -28,9 +27,6 @@ enum class CoinStatsHashType {
}; };
struct CCoinsStats { struct CCoinsStats {
//! Which hash type to use
const CoinStatsHashType m_hash_type;
int nHeight{0}; int nHeight{0};
uint256 hashBlock{}; uint256 hashBlock{};
uint64_t nTransactions{0}; uint64_t nTransactions{0};
@@ -44,8 +40,6 @@ struct CCoinsStats {
//! The number of coins contained. //! The number of coins contained.
uint64_t coins_count{0}; uint64_t coins_count{0};
//! Signals if the coinstatsindex should be used (when available).
bool index_requested{true};
//! Signals if the coinstatsindex was used to retrieve the statistics. //! Signals if the coinstatsindex was used to retrieve the statistics.
bool index_used{false}; bool index_used{false};
@@ -70,15 +64,15 @@ struct CCoinsStats {
//! Total cumulative amount of coins lost due to unclaimed miner rewards up to and including this block //! Total cumulative amount of coins lost due to unclaimed miner rewards up to and including this block
CAmount total_unspendables_unclaimed_rewards{0}; CAmount total_unspendables_unclaimed_rewards{0};
CCoinsStats(CoinStatsHashType hash_type) : m_hash_type(hash_type) {} CCoinsStats() = default;
CCoinsStats(int block_height, const uint256& block_hash);
}; };
//! Calculate statistics about the unspent transaction output set
bool GetUTXOStats(CCoinsView* view, node::BlockManager& blockman, CCoinsStats& stats, const std::function<void()>& interruption_point = {}, const CBlockIndex* pindex = nullptr);
uint64_t GetBogoSize(const CScript& script_pub_key); uint64_t GetBogoSize(const CScript& script_pub_key);
CDataStream TxOutSer(const COutPoint& outpoint, const Coin& coin); CDataStream TxOutSer(const COutPoint& outpoint, const Coin& coin);
} // namespace node
#endif // BITCOIN_NODE_COINSTATS_H std::optional<CCoinsStats> ComputeUTXOStats(CoinStatsHashType hash_type, CCoinsView* view, node::BlockManager& blockman, const std::function<void()>& interruption_point = {});
} // namespace kernel
#endif // BITCOIN_KERNEL_COINSTATS_H
+53 -18
View File
@@ -19,11 +19,11 @@
#include <hash.h> #include <hash.h>
#include <index/blockfilterindex.h> #include <index/blockfilterindex.h>
#include <index/coinstatsindex.h> #include <index/coinstatsindex.h>
#include <kernel/coinstats.h>
#include <logging/timer.h> #include <logging/timer.h>
#include <net.h> #include <net.h>
#include <net_processing.h> #include <net_processing.h>
#include <node/blockstorage.h> #include <node/blockstorage.h>
#include <node/coinstats.h>
#include <node/context.h> #include <node/context.h>
#include <node/utxo_snapshot.h> #include <node/utxo_snapshot.h>
#include <primitives/transaction.h> #include <primitives/transaction.h>
@@ -51,10 +51,10 @@
#include <memory> #include <memory>
#include <mutex> #include <mutex>
using kernel::CCoinsStats;
using kernel::CoinStatsHashType;
using node::BlockManager; using node::BlockManager;
using node::CCoinsStats;
using node::CoinStatsHashType;
using node::GetUTXOStats;
using node::NodeContext; using node::NodeContext;
using node::ReadBlockFromDisk; using node::ReadBlockFromDisk;
using node::SnapshotMetadata; using node::SnapshotMetadata;
@@ -808,6 +808,36 @@ CoinStatsHashType ParseHashType(const std::string& hash_type_input)
} }
} }
/**
* Calculate statistics about the unspent transaction output set
*
* @param[in] index_requested Signals if the coinstatsindex should be used (when available).
*/
static std::optional<kernel::CCoinsStats> GetUTXOStats(CCoinsView* view, node::BlockManager& blockman,
kernel::CoinStatsHashType hash_type,
const std::function<void()>& interruption_point = {},
const CBlockIndex* pindex = nullptr,
bool index_requested = true)
{
// Use CoinStatsIndex if it is requested and available and a hash_type of Muhash or None was requested
if ((hash_type == kernel::CoinStatsHashType::MUHASH || hash_type == kernel::CoinStatsHashType::NONE) && g_coin_stats_index && index_requested) {
if (pindex) {
return g_coin_stats_index->LookUpStats(pindex);
} else {
CBlockIndex* block_index = WITH_LOCK(::cs_main, return blockman.LookupBlockIndex(view->GetBestBlock()));
return g_coin_stats_index->LookUpStats(block_index);
}
}
// If the coinstats index isn't requested or is otherwise not usable, the
// pindex should either be null or equal to the view's best block. This is
// because without the coinstats index we can only get coinstats about the
// best block.
CHECK_NONFATAL(!pindex || pindex->GetBlockHash() == view->GetBestBlock());
return kernel::ComputeUTXOStats(hash_type, view, blockman, interruption_point);
}
static RPCHelpMan gettxoutsetinfo() static RPCHelpMan gettxoutsetinfo()
{ {
return RPCHelpMan{"gettxoutsetinfo", return RPCHelpMan{"gettxoutsetinfo",
@@ -862,8 +892,7 @@ static RPCHelpMan gettxoutsetinfo()
const CBlockIndex* pindex{nullptr}; const CBlockIndex* pindex{nullptr};
const CoinStatsHashType hash_type{request.params[0].isNull() ? CoinStatsHashType::HASH_SERIALIZED : ParseHashType(request.params[0].get_str())}; const CoinStatsHashType hash_type{request.params[0].isNull() ? CoinStatsHashType::HASH_SERIALIZED : ParseHashType(request.params[0].get_str())};
CCoinsStats stats{hash_type}; bool index_requested = request.params[2].isNull() || request.params[2].get_bool();
stats.index_requested = request.params[2].isNull() || request.params[2].get_bool();
NodeContext& node = EnsureAnyNodeContext(request.context); NodeContext& node = EnsureAnyNodeContext(request.context);
ChainstateManager& chainman = EnsureChainman(node); ChainstateManager& chainman = EnsureChainman(node);
@@ -884,14 +913,14 @@ static RPCHelpMan gettxoutsetinfo()
throw JSONRPCError(RPC_INVALID_PARAMETER, "Querying specific block heights requires coinstatsindex"); throw JSONRPCError(RPC_INVALID_PARAMETER, "Querying specific block heights requires coinstatsindex");
} }
if (stats.m_hash_type == CoinStatsHashType::HASH_SERIALIZED) { if (hash_type == CoinStatsHashType::HASH_SERIALIZED) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "hash_serialized_2 hash type cannot be queried for a specific block"); throw JSONRPCError(RPC_INVALID_PARAMETER, "hash_serialized_2 hash type cannot be queried for a specific block");
} }
pindex = ParseHashOrHeight(request.params[1], chainman); pindex = ParseHashOrHeight(request.params[1], chainman);
} }
if (stats.index_requested && g_coin_stats_index) { if (index_requested && g_coin_stats_index) {
if (!g_coin_stats_index->BlockUntilSyncedToCurrentChain()) { if (!g_coin_stats_index->BlockUntilSyncedToCurrentChain()) {
const IndexSummary summary{g_coin_stats_index->GetSummary()}; const IndexSummary summary{g_coin_stats_index->GetSummary()};
@@ -903,7 +932,9 @@ static RPCHelpMan gettxoutsetinfo()
} }
} }
if (GetUTXOStats(coins_view, *blockman, stats, node.rpc_interruption_point, pindex)) { const std::optional<CCoinsStats> maybe_stats = GetUTXOStats(coins_view, *blockman, hash_type, node.rpc_interruption_point, pindex, index_requested);
if (maybe_stats.has_value()) {
const CCoinsStats& stats = maybe_stats.value();
ret.pushKV("height", (int64_t)stats.nHeight); ret.pushKV("height", (int64_t)stats.nHeight);
ret.pushKV("bestblock", stats.hashBlock.GetHex()); ret.pushKV("bestblock", stats.hashBlock.GetHex());
ret.pushKV("txouts", (int64_t)stats.nTransactionOutputs); ret.pushKV("txouts", (int64_t)stats.nTransactionOutputs);
@@ -922,10 +953,13 @@ static RPCHelpMan gettxoutsetinfo()
} else { } else {
ret.pushKV("total_unspendable_amount", ValueFromAmount(stats.total_unspendable_amount)); ret.pushKV("total_unspendable_amount", ValueFromAmount(stats.total_unspendable_amount));
CCoinsStats prev_stats{hash_type}; CCoinsStats prev_stats{};
if (pindex->nHeight > 0) { if (pindex->nHeight > 0) {
GetUTXOStats(coins_view, *blockman, prev_stats, node.rpc_interruption_point, pindex->pprev); const std::optional<CCoinsStats> maybe_prev_stats = GetUTXOStats(coins_view, *blockman, hash_type, node.rpc_interruption_point, pindex->pprev, index_requested);
if (!maybe_prev_stats) {
throw JSONRPCError(RPC_INTERNAL_ERROR, "Unable to read UTXO set");
}
prev_stats = maybe_prev_stats.value();
} }
UniValue block_info(UniValue::VOBJ); UniValue block_info(UniValue::VOBJ);
@@ -2285,7 +2319,7 @@ UniValue CreateUTXOSnapshot(
const fs::path& temppath) const fs::path& temppath)
{ {
std::unique_ptr<CCoinsViewCursor> pcursor; std::unique_ptr<CCoinsViewCursor> pcursor;
CCoinsStats stats{CoinStatsHashType::HASH_SERIALIZED}; std::optional<CCoinsStats> maybe_stats;
const CBlockIndex* tip; const CBlockIndex* tip;
{ {
@@ -2305,19 +2339,20 @@ UniValue CreateUTXOSnapshot(
chainstate.ForceFlushStateToDisk(); chainstate.ForceFlushStateToDisk();
if (!GetUTXOStats(&chainstate.CoinsDB(), chainstate.m_blockman, stats, node.rpc_interruption_point)) { maybe_stats = GetUTXOStats(&chainstate.CoinsDB(), chainstate.m_blockman, CoinStatsHashType::HASH_SERIALIZED, node.rpc_interruption_point);
if (!maybe_stats) {
throw JSONRPCError(RPC_INTERNAL_ERROR, "Unable to read UTXO set"); throw JSONRPCError(RPC_INTERNAL_ERROR, "Unable to read UTXO set");
} }
pcursor = chainstate.CoinsDB().Cursor(); pcursor = chainstate.CoinsDB().Cursor();
tip = CHECK_NONFATAL(chainstate.m_blockman.LookupBlockIndex(stats.hashBlock)); tip = CHECK_NONFATAL(chainstate.m_blockman.LookupBlockIndex(maybe_stats->hashBlock));
} }
LOG_TIME_SECONDS(strprintf("writing UTXO snapshot at height %s (%s) to file %s (via %s)", LOG_TIME_SECONDS(strprintf("writing UTXO snapshot at height %s (%s) to file %s (via %s)",
tip->nHeight, tip->GetBlockHash().ToString(), tip->nHeight, tip->GetBlockHash().ToString(),
fs::PathToString(path), fs::PathToString(temppath))); fs::PathToString(path), fs::PathToString(temppath)));
SnapshotMetadata metadata{tip->GetBlockHash(), stats.coins_count, tip->nChainTx}; SnapshotMetadata metadata{tip->GetBlockHash(), maybe_stats->coins_count, tip->nChainTx};
afile << metadata; afile << metadata;
@@ -2339,11 +2374,11 @@ UniValue CreateUTXOSnapshot(
afile.fclose(); afile.fclose();
UniValue result(UniValue::VOBJ); UniValue result(UniValue::VOBJ);
result.pushKV("coins_written", stats.coins_count); result.pushKV("coins_written", maybe_stats->coins_count);
result.pushKV("base_hash", tip->GetBlockHash().ToString()); result.pushKV("base_hash", tip->GetBlockHash().ToString());
result.pushKV("base_height", tip->nHeight); result.pushKV("base_height", tip->nHeight);
result.pushKV("path", path.u8string()); result.pushKV("path", path.u8string());
result.pushKV("txoutset_hash", stats.hashSerialized.ToString()); result.pushKV("txoutset_hash", maybe_stats->hashSerialized.ToString());
// Cast required because univalue doesn't have serialization specified for // Cast required because univalue doesn't have serialization specified for
// `unsigned int`, nChainTx's type. // `unsigned int`, nChainTx's type.
result.pushKV("nchaintx", uint64_t{tip->nChainTx}); result.pushKV("nchaintx", uint64_t{tip->nChainTx});
-1
View File
@@ -5,7 +5,6 @@
#ifndef BITCOIN_RPC_UTIL_H #ifndef BITCOIN_RPC_UTIL_H
#define BITCOIN_RPC_UTIL_H #define BITCOIN_RPC_UTIL_H
#include <node/coinstats.h>
#include <node/transaction.h> #include <node/transaction.h>
#include <outputtype.h> #include <outputtype.h>
#include <protocol.h> #include <protocol.h>
+6 -8
View File
@@ -13,8 +13,8 @@
#include <chrono> #include <chrono>
using node::CCoinsStats; using kernel::CCoinsStats;
using node::CoinStatsHashType; using kernel::CoinStatsHashType;
BOOST_AUTO_TEST_SUITE(coinstatsindex_tests) BOOST_AUTO_TEST_SUITE(coinstatsindex_tests)
@@ -33,7 +33,6 @@ BOOST_FIXTURE_TEST_CASE(coinstatsindex_initial_sync, TestChain100Setup)
{ {
CoinStatsIndex coin_stats_index{1 << 20, true}; CoinStatsIndex coin_stats_index{1 << 20, true};
CCoinsStats coin_stats{CoinStatsHashType::MUHASH};
const CBlockIndex* block_index; const CBlockIndex* block_index;
{ {
LOCK(cs_main); LOCK(cs_main);
@@ -41,7 +40,7 @@ BOOST_FIXTURE_TEST_CASE(coinstatsindex_initial_sync, TestChain100Setup)
} }
// CoinStatsIndex should not be found before it is started. // CoinStatsIndex should not be found before it is started.
BOOST_CHECK(!coin_stats_index.LookUpStats(block_index, coin_stats)); BOOST_CHECK(!coin_stats_index.LookUpStats(block_index));
// BlockUntilSyncedToCurrentChain should return false before CoinStatsIndex // BlockUntilSyncedToCurrentChain should return false before CoinStatsIndex
// is started. // is started.
@@ -57,10 +56,10 @@ BOOST_FIXTURE_TEST_CASE(coinstatsindex_initial_sync, TestChain100Setup)
LOCK(cs_main); LOCK(cs_main);
genesis_block_index = m_node.chainman->ActiveChain().Genesis(); genesis_block_index = m_node.chainman->ActiveChain().Genesis();
} }
BOOST_CHECK(coin_stats_index.LookUpStats(genesis_block_index, coin_stats)); BOOST_CHECK(coin_stats_index.LookUpStats(genesis_block_index));
// Check that CoinStatsIndex updates with new blocks. // Check that CoinStatsIndex updates with new blocks.
coin_stats_index.LookUpStats(block_index, coin_stats); BOOST_CHECK(coin_stats_index.LookUpStats(block_index));
const CScript script_pub_key{CScript() << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG}; const CScript script_pub_key{CScript() << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG};
std::vector<CMutableTransaction> noTxns; std::vector<CMutableTransaction> noTxns;
@@ -69,13 +68,12 @@ BOOST_FIXTURE_TEST_CASE(coinstatsindex_initial_sync, TestChain100Setup)
// Let the CoinStatsIndex to catch up again. // Let the CoinStatsIndex to catch up again.
BOOST_CHECK(coin_stats_index.BlockUntilSyncedToCurrentChain()); BOOST_CHECK(coin_stats_index.BlockUntilSyncedToCurrentChain());
CCoinsStats new_coin_stats{CoinStatsHashType::MUHASH};
const CBlockIndex* new_block_index; const CBlockIndex* new_block_index;
{ {
LOCK(cs_main); LOCK(cs_main);
new_block_index = m_node.chainman->ActiveChain().Tip(); new_block_index = m_node.chainman->ActiveChain().Tip();
} }
coin_stats_index.LookUpStats(new_block_index, new_coin_stats); BOOST_CHECK(coin_stats_index.LookUpStats(new_block_index));
BOOST_CHECK(block_index != new_block_index); BOOST_CHECK(block_index != new_block_index);
-15
View File
@@ -10,7 +10,6 @@
#include <consensus/tx_verify.h> #include <consensus/tx_verify.h>
#include <consensus/validation.h> #include <consensus/validation.h>
#include <key.h> #include <key.h>
#include <node/coinstats.h>
#include <policy/policy.h> #include <policy/policy.h>
#include <primitives/transaction.h> #include <primitives/transaction.h>
#include <pubkey.h> #include <pubkey.h>
@@ -26,10 +25,6 @@
#include <string> #include <string>
#include <vector> #include <vector>
using node::CCoinsStats;
using node::CoinStatsHashType;
using node::GetUTXOStats;
namespace { namespace {
const TestingSetup* g_setup; const TestingSetup* g_setup;
const Coin EMPTY_COIN{}; const Coin EMPTY_COIN{};
@@ -269,16 +264,6 @@ FUZZ_TARGET_INIT(coins_view, initialize_coins_view)
} }
(void)GetTransactionSigOpCost(transaction, coins_view_cache, flags); (void)GetTransactionSigOpCost(transaction, coins_view_cache, flags);
}, },
[&] {
CCoinsStats stats{CoinStatsHashType::HASH_SERIALIZED};
bool expected_code_path = false;
try {
(void)GetUTXOStats(&coins_view_cache, g_setup->m_node.chainman->m_blockman, stats);
} catch (const std::logic_error&) {
expected_code_path = true;
}
assert(expected_code_path);
},
[&] { [&] {
(void)IsWitnessStandard(CTransaction{random_mutable_transaction}, coins_view_cache); (void)IsWitnessStandard(CTransaction{random_mutable_transaction}, coins_view_cache);
}); });
+11 -9
View File
@@ -18,10 +18,10 @@
#include <cuckoocache.h> #include <cuckoocache.h>
#include <flatfile.h> #include <flatfile.h>
#include <hash.h> #include <hash.h>
#include <kernel/coinstats.h>
#include <logging.h> #include <logging.h>
#include <logging/timer.h> #include <logging/timer.h>
#include <node/blockstorage.h> #include <node/blockstorage.h>
#include <node/coinstats.h>
#include <node/ui_interface.h> #include <node/ui_interface.h>
#include <node/utxo_snapshot.h> #include <node/utxo_snapshot.h>
#include <policy/policy.h> #include <policy/policy.h>
@@ -58,17 +58,18 @@
#include <optional> #include <optional>
#include <string> #include <string>
using kernel::CCoinsStats;
using kernel::CoinStatsHashType;
using kernel::ComputeUTXOStats;
using node::BLOCKFILE_CHUNK_SIZE; using node::BLOCKFILE_CHUNK_SIZE;
using node::BlockManager; using node::BlockManager;
using node::BlockMap; using node::BlockMap;
using node::CBlockIndexHeightOnlyComparator; using node::CBlockIndexHeightOnlyComparator;
using node::CBlockIndexWorkComparator; using node::CBlockIndexWorkComparator;
using node::CCoinsStats;
using node::CoinStatsHashType;
using node::fImporting; using node::fImporting;
using node::fPruneMode; using node::fPruneMode;
using node::fReindex; using node::fReindex;
using node::GetUTXOStats;
using node::nPruneTarget; using node::nPruneTarget;
using node::OpenBlockFile; using node::OpenBlockFile;
using node::ReadBlockFromDisk; using node::ReadBlockFromDisk;
@@ -4987,7 +4988,8 @@ bool ChainstateManager::PopulateAndValidateSnapshot(
CBlockIndex* snapshot_start_block = WITH_LOCK(::cs_main, return m_blockman.LookupBlockIndex(base_blockhash)); CBlockIndex* snapshot_start_block = WITH_LOCK(::cs_main, return m_blockman.LookupBlockIndex(base_blockhash));
if (!snapshot_start_block) { if (!snapshot_start_block) {
// Needed for GetUTXOStats and ExpectedAssumeutxo to determine the height and to avoid a crash when base_blockhash.IsNull() // Needed for ComputeUTXOStats and ExpectedAssumeutxo to determine the
// height and to avoid a crash when base_blockhash.IsNull()
LogPrintf("[snapshot] Did not find snapshot start blockheader %s\n", LogPrintf("[snapshot] Did not find snapshot start blockheader %s\n",
base_blockhash.ToString()); base_blockhash.ToString());
return false; return false;
@@ -5095,22 +5097,22 @@ bool ChainstateManager::PopulateAndValidateSnapshot(
assert(coins_cache.GetBestBlock() == base_blockhash); assert(coins_cache.GetBestBlock() == base_blockhash);
CCoinsStats stats{CoinStatsHashType::HASH_SERIALIZED};
auto breakpoint_fnc = [] { /* TODO insert breakpoint here? */ }; auto breakpoint_fnc = [] { /* TODO insert breakpoint here? */ };
// As above, okay to immediately release cs_main here since no other context knows // As above, okay to immediately release cs_main here since no other context knows
// about the snapshot_chainstate. // about the snapshot_chainstate.
CCoinsViewDB* snapshot_coinsdb = WITH_LOCK(::cs_main, return &snapshot_chainstate.CoinsDB()); CCoinsViewDB* snapshot_coinsdb = WITH_LOCK(::cs_main, return &snapshot_chainstate.CoinsDB());
if (!GetUTXOStats(snapshot_coinsdb, m_blockman, stats, breakpoint_fnc)) { const std::optional<CCoinsStats> maybe_stats = ComputeUTXOStats(CoinStatsHashType::HASH_SERIALIZED, snapshot_coinsdb, m_blockman, breakpoint_fnc);
if (!maybe_stats.has_value()) {
LogPrintf("[snapshot] failed to generate coins stats\n"); LogPrintf("[snapshot] failed to generate coins stats\n");
return false; return false;
} }
// Assert that the deserialized chainstate contents match the expected assumeutxo value. // Assert that the deserialized chainstate contents match the expected assumeutxo value.
if (AssumeutxoHash{stats.hashSerialized} != au_data.hash_serialized) { if (AssumeutxoHash{maybe_stats->hashSerialized} != au_data.hash_serialized) {
LogPrintf("[snapshot] bad snapshot content hash: expected %s, got %s\n", LogPrintf("[snapshot] bad snapshot content hash: expected %s, got %s\n",
au_data.hash_serialized.ToString(), stats.hashSerialized.ToString()); au_data.hash_serialized.ToString(), maybe_stats->hashSerialized.ToString());
return false; return false;
} }
+1 -2
View File
@@ -14,7 +14,6 @@ import sys
EXPECTED_CIRCULAR_DEPENDENCIES = ( EXPECTED_CIRCULAR_DEPENDENCIES = (
"chainparamsbase -> util/system -> chainparamsbase", "chainparamsbase -> util/system -> chainparamsbase",
"node/blockstorage -> validation -> node/blockstorage", "node/blockstorage -> validation -> node/blockstorage",
"index/coinstatsindex -> node/coinstats -> index/coinstatsindex",
"policy/fees -> txmempool -> policy/fees", "policy/fees -> txmempool -> policy/fees",
"qt/addresstablemodel -> qt/walletmodel -> qt/addresstablemodel", "qt/addresstablemodel -> qt/walletmodel -> qt/addresstablemodel",
"qt/recentrequeststablemodel -> qt/walletmodel -> qt/recentrequeststablemodel", "qt/recentrequeststablemodel -> qt/walletmodel -> qt/recentrequeststablemodel",
@@ -22,7 +21,7 @@ EXPECTED_CIRCULAR_DEPENDENCIES = (
"qt/transactiontablemodel -> qt/walletmodel -> qt/transactiontablemodel", "qt/transactiontablemodel -> qt/walletmodel -> qt/transactiontablemodel",
"wallet/fees -> wallet/wallet -> wallet/fees", "wallet/fees -> wallet/wallet -> wallet/fees",
"wallet/wallet -> wallet/walletdb -> wallet/wallet", "wallet/wallet -> wallet/walletdb -> wallet/wallet",
"node/coinstats -> validation -> node/coinstats", "kernel/coinstats -> validation -> kernel/coinstats",
) )
CODE_DIR = "src" CODE_DIR = "src"