mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-02-21 14:34:49 +01:00
index: Add Coinstats index
The index holds the values previously calculated in coinstats.cpp for each block, representing the state of the UTXO set at each height.
This commit is contained in:
parent
a8a46c4b3c
commit
dd58a4de21
6 changed files with 437 additions and 8 deletions
|
@ -152,6 +152,7 @@ BITCOIN_CORE_H = \
|
||||||
i2p.h \
|
i2p.h \
|
||||||
index/base.h \
|
index/base.h \
|
||||||
index/blockfilterindex.h \
|
index/blockfilterindex.h \
|
||||||
|
index/coinstatsindex.h \
|
||||||
index/disktxpos.h \
|
index/disktxpos.h \
|
||||||
index/txindex.h \
|
index/txindex.h \
|
||||||
indirectmap.h \
|
indirectmap.h \
|
||||||
|
@ -318,6 +319,7 @@ libbitcoin_server_a_SOURCES = \
|
||||||
i2p.cpp \
|
i2p.cpp \
|
||||||
index/base.cpp \
|
index/base.cpp \
|
||||||
index/blockfilterindex.cpp \
|
index/blockfilterindex.cpp \
|
||||||
|
index/coinstatsindex.cpp \
|
||||||
index/txindex.cpp \
|
index/txindex.cpp \
|
||||||
init.cpp \
|
init.cpp \
|
||||||
mapport.cpp \
|
mapport.cpp \
|
||||||
|
|
|
@ -81,6 +81,8 @@ protected:
|
||||||
|
|
||||||
void ChainStateFlushed(const CBlockLocator& locator) override;
|
void ChainStateFlushed(const CBlockLocator& locator) override;
|
||||||
|
|
||||||
|
const CBlockIndex* CurrentIndex() { return m_best_block_index.load(); };
|
||||||
|
|
||||||
/// Initialize internal state from the database and block index.
|
/// Initialize internal state from the database and block index.
|
||||||
virtual bool Init();
|
virtual bool Init();
|
||||||
|
|
||||||
|
|
363
src/index/coinstatsindex.cpp
Normal file
363
src/index/coinstatsindex.cpp
Normal file
|
@ -0,0 +1,363 @@
|
||||||
|
// Copyright (c) 2020-2021 The Bitcoin Core developers
|
||||||
|
// Distributed under the MIT software license, see the accompanying
|
||||||
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
|
#include <chainparams.h>
|
||||||
|
#include <coins.h>
|
||||||
|
#include <crypto/muhash.h>
|
||||||
|
#include <index/coinstatsindex.h>
|
||||||
|
#include <node/blockstorage.h>
|
||||||
|
#include <serialize.h>
|
||||||
|
#include <txdb.h>
|
||||||
|
#include <undo.h>
|
||||||
|
#include <validation.h>
|
||||||
|
|
||||||
|
static constexpr char DB_BLOCK_HASH = 's';
|
||||||
|
static constexpr char DB_BLOCK_HEIGHT = 't';
|
||||||
|
static constexpr char DB_MUHASH = 'M';
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
struct DBVal {
|
||||||
|
uint256 muhash;
|
||||||
|
uint64_t transaction_output_count;
|
||||||
|
uint64_t bogo_size;
|
||||||
|
CAmount total_amount;
|
||||||
|
|
||||||
|
SERIALIZE_METHODS(DBVal, obj)
|
||||||
|
{
|
||||||
|
READWRITE(obj.muhash);
|
||||||
|
READWRITE(obj.transaction_output_count);
|
||||||
|
READWRITE(obj.bogo_size);
|
||||||
|
READWRITE(obj.total_amount);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DBHeightKey {
|
||||||
|
int height;
|
||||||
|
|
||||||
|
explicit DBHeightKey(int height_in) : height(height_in) {}
|
||||||
|
|
||||||
|
template <typename Stream>
|
||||||
|
void Serialize(Stream& s) const
|
||||||
|
{
|
||||||
|
ser_writedata8(s, DB_BLOCK_HEIGHT);
|
||||||
|
ser_writedata32be(s, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Stream>
|
||||||
|
void Unserialize(Stream& s)
|
||||||
|
{
|
||||||
|
char prefix{static_cast<char>(ser_readdata8(s))};
|
||||||
|
if (prefix != DB_BLOCK_HEIGHT) {
|
||||||
|
throw std::ios_base::failure("Invalid format for coinstatsindex DB height key");
|
||||||
|
}
|
||||||
|
height = ser_readdata32be(s);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DBHashKey {
|
||||||
|
uint256 block_hash;
|
||||||
|
|
||||||
|
explicit DBHashKey(const uint256& hash_in) : block_hash(hash_in) {}
|
||||||
|
|
||||||
|
SERIALIZE_METHODS(DBHashKey, obj)
|
||||||
|
{
|
||||||
|
char prefix{DB_BLOCK_HASH};
|
||||||
|
READWRITE(prefix);
|
||||||
|
if (prefix != DB_BLOCK_HASH) {
|
||||||
|
throw std::ios_base::failure("Invalid format for coinstatsindex DB hash key");
|
||||||
|
}
|
||||||
|
|
||||||
|
READWRITE(obj.block_hash);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}; // namespace
|
||||||
|
|
||||||
|
std::unique_ptr<CoinStatsIndex> g_coin_stats_index;
|
||||||
|
|
||||||
|
CoinStatsIndex::CoinStatsIndex(size_t n_cache_size, bool f_memory, bool f_wipe)
|
||||||
|
{
|
||||||
|
fs::path path{GetDataDir() / "indexes" / "coinstats"};
|
||||||
|
fs::create_directories(path);
|
||||||
|
|
||||||
|
m_db = std::make_unique<CoinStatsIndex::DB>(path / "db", n_cache_size, f_memory, f_wipe);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CoinStatsIndex::WriteBlock(const CBlock& block, const CBlockIndex* pindex)
|
||||||
|
{
|
||||||
|
CBlockUndo block_undo;
|
||||||
|
|
||||||
|
// Ignore genesis block
|
||||||
|
if (pindex->nHeight > 0) {
|
||||||
|
if (!UndoReadFromDisk(block_undo, pindex)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<uint256, DBVal> read_out;
|
||||||
|
if (!m_db->Read(DBHeightKey(pindex->nHeight - 1), read_out)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint256 expected_block_hash{pindex->pprev->GetBlockHash()};
|
||||||
|
if (read_out.first != expected_block_hash) {
|
||||||
|
if (!m_db->Read(DBHashKey(expected_block_hash), read_out)) {
|
||||||
|
return error("%s: previous block header belongs to unexpected block %s; expected %s",
|
||||||
|
__func__, read_out.first.ToString(), expected_block_hash.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Deduplicate BIP30 related code
|
||||||
|
bool is_bip30_block{(pindex->nHeight == 91722 && pindex->GetBlockHash() == uint256S("0x00000000000271a2dc26e7667f8419f2e15416dc6955e5a6c6cdf3f2574dd08e")) ||
|
||||||
|
(pindex->nHeight == 91812 && pindex->GetBlockHash() == uint256S("0x00000000000af0aed4792b1acee3d966af36cf5def14935db8de83d6f9306f2f"))};
|
||||||
|
|
||||||
|
// Add the new utxos created from the block
|
||||||
|
for (size_t i = 0; i < block.vtx.size(); ++i) {
|
||||||
|
const auto& tx{block.vtx.at(i)};
|
||||||
|
|
||||||
|
// Skip duplicate txid coinbase transactions (BIP30).
|
||||||
|
if (is_bip30_block && tx->IsCoinBase()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t j = 0; j < tx->vout.size(); ++j) {
|
||||||
|
const CTxOut& out{tx->vout[j]};
|
||||||
|
Coin coin{out, pindex->nHeight, tx->IsCoinBase()};
|
||||||
|
COutPoint outpoint{tx->GetHash(), static_cast<uint32_t>(j)};
|
||||||
|
|
||||||
|
// Skip unspendable coins
|
||||||
|
if (coin.out.scriptPubKey.IsUnspendable()) continue;
|
||||||
|
|
||||||
|
m_muhash.Insert(MakeUCharSpan(TxOutSer(outpoint, coin)));
|
||||||
|
|
||||||
|
++m_transaction_output_count;
|
||||||
|
m_total_amount += coin.out.nValue;
|
||||||
|
m_bogo_size += GetBogoSize(coin.out.scriptPubKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The coinbase tx has no undo data since no former output is spent
|
||||||
|
if (!tx->IsCoinBase()) {
|
||||||
|
const auto& tx_undo{block_undo.vtxundo.at(i - 1)};
|
||||||
|
|
||||||
|
for (size_t j = 0; j < tx_undo.vprevout.size(); ++j) {
|
||||||
|
Coin coin{tx_undo.vprevout[j]};
|
||||||
|
COutPoint outpoint{tx->vin[j].prevout.hash, tx->vin[j].prevout.n};
|
||||||
|
|
||||||
|
m_muhash.Remove(MakeUCharSpan(TxOutSer(outpoint, coin)));
|
||||||
|
|
||||||
|
--m_transaction_output_count;
|
||||||
|
m_total_amount -= coin.out.nValue;
|
||||||
|
m_bogo_size -= GetBogoSize(coin.out.scriptPubKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<uint256, DBVal> value;
|
||||||
|
value.first = pindex->GetBlockHash();
|
||||||
|
value.second.transaction_output_count = m_transaction_output_count;
|
||||||
|
value.second.bogo_size = m_bogo_size;
|
||||||
|
value.second.total_amount = m_total_amount;
|
||||||
|
|
||||||
|
uint256 out;
|
||||||
|
m_muhash.Finalize(out);
|
||||||
|
value.second.muhash = out;
|
||||||
|
|
||||||
|
return m_db->Write(DBHeightKey(pindex->nHeight), value) && m_db->Write(DB_MUHASH, m_muhash);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool CopyHeightIndexToHashIndex(CDBIterator& db_it, CDBBatch& batch,
|
||||||
|
const std::string& index_name,
|
||||||
|
int start_height, int stop_height)
|
||||||
|
{
|
||||||
|
DBHeightKey key{start_height};
|
||||||
|
db_it.Seek(key);
|
||||||
|
|
||||||
|
for (int height = start_height; height <= stop_height; ++height) {
|
||||||
|
if (!db_it.GetKey(key) || key.height != height) {
|
||||||
|
return error("%s: unexpected key in %s: expected (%c, %d)",
|
||||||
|
__func__, index_name, DB_BLOCK_HEIGHT, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<uint256, DBVal> value;
|
||||||
|
if (!db_it.GetValue(value)) {
|
||||||
|
return error("%s: unable to read value in %s at key (%c, %d)",
|
||||||
|
__func__, index_name, DB_BLOCK_HEIGHT, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
batch.Write(DBHashKey(value.first), std::move(value.second));
|
||||||
|
|
||||||
|
db_it.Next();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CoinStatsIndex::Rewind(const CBlockIndex* current_tip, const CBlockIndex* new_tip)
|
||||||
|
{
|
||||||
|
assert(current_tip->GetAncestor(new_tip->nHeight) == new_tip);
|
||||||
|
|
||||||
|
CDBBatch batch(*m_db);
|
||||||
|
std::unique_ptr<CDBIterator> db_it(m_db->NewIterator());
|
||||||
|
|
||||||
|
// During a reorg, we need to copy all hash digests for blocks that are
|
||||||
|
// getting disconnected from the height index to the hash index so we can
|
||||||
|
// still find them when the height index entries are overwritten.
|
||||||
|
if (!CopyHeightIndexToHashIndex(*db_it, batch, m_name, new_tip->nHeight, current_tip->nHeight)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_db->WriteBatch(batch)) return false;
|
||||||
|
|
||||||
|
{
|
||||||
|
LOCK(cs_main);
|
||||||
|
CBlockIndex* iter_tip{g_chainman.m_blockman.LookupBlockIndex(current_tip->GetBlockHash())};
|
||||||
|
const auto& consensus_params{Params().GetConsensus()};
|
||||||
|
|
||||||
|
do {
|
||||||
|
CBlock block;
|
||||||
|
|
||||||
|
if (!ReadBlockFromDisk(block, iter_tip, consensus_params)) {
|
||||||
|
return error("%s: Failed to read block %s from disk",
|
||||||
|
__func__, iter_tip->GetBlockHash().ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
ReverseBlock(block, iter_tip);
|
||||||
|
|
||||||
|
iter_tip = iter_tip->GetAncestor(iter_tip->nHeight - 1);
|
||||||
|
} while (new_tip != iter_tip);
|
||||||
|
}
|
||||||
|
|
||||||
|
return BaseIndex::Rewind(current_tip, new_tip);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool LookUpOne(const CDBWrapper& db, const CBlockIndex* block_index, DBVal& result)
|
||||||
|
{
|
||||||
|
// First check if the result is stored under the height index and the value
|
||||||
|
// there matches the block hash. This should be the case if the block is on
|
||||||
|
// the active chain.
|
||||||
|
std::pair<uint256, DBVal> read_out;
|
||||||
|
if (!db.Read(DBHeightKey(block_index->nHeight), read_out)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (read_out.first == block_index->GetBlockHash()) {
|
||||||
|
result = std::move(read_out.second);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If value at the height index corresponds to an different block, the
|
||||||
|
// result will be stored in the hash index.
|
||||||
|
return db.Read(DBHashKey(block_index->GetBlockHash()), result);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CoinStatsIndex::LookUpStats(const CBlockIndex* block_index, CCoinsStats& coins_stats) const
|
||||||
|
{
|
||||||
|
DBVal entry;
|
||||||
|
if (!LookUpOne(*m_db, block_index, entry)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
coins_stats.hashSerialized = entry.muhash;
|
||||||
|
coins_stats.nTransactionOutputs = entry.transaction_output_count;
|
||||||
|
coins_stats.nBogoSize = entry.bogo_size;
|
||||||
|
coins_stats.nTotalAmount = entry.total_amount;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CoinStatsIndex::Init()
|
||||||
|
{
|
||||||
|
if (!m_db->Read(DB_MUHASH, m_muhash)) {
|
||||||
|
// Check that the cause of the read failure is that the key does not
|
||||||
|
// exist. Any other errors indicate database corruption or a disk
|
||||||
|
// failure, and starting the index would cause further corruption.
|
||||||
|
if (m_db->Exists(DB_MUHASH)) {
|
||||||
|
return error("%s: Cannot read current %s state; index may be corrupted",
|
||||||
|
__func__, GetName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (BaseIndex::Init()) {
|
||||||
|
const CBlockIndex* pindex{CurrentIndex()};
|
||||||
|
|
||||||
|
if (pindex) {
|
||||||
|
DBVal entry;
|
||||||
|
if (!LookUpOne(*m_db, pindex, entry)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_transaction_output_count = entry.transaction_output_count;
|
||||||
|
m_bogo_size = entry.bogo_size;
|
||||||
|
m_total_amount = entry.total_amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reverse a single block as part of a reorg
|
||||||
|
bool CoinStatsIndex::ReverseBlock(const CBlock& block, const CBlockIndex* pindex)
|
||||||
|
{
|
||||||
|
CBlockUndo block_undo;
|
||||||
|
std::pair<uint256, DBVal> read_out;
|
||||||
|
|
||||||
|
// Ignore genesis block
|
||||||
|
if (pindex->nHeight > 0) {
|
||||||
|
if (!UndoReadFromDisk(block_undo, pindex)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_db->Read(DBHeightKey(pindex->nHeight - 1), read_out)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint256 expected_block_hash{pindex->pprev->GetBlockHash()};
|
||||||
|
if (read_out.first != expected_block_hash) {
|
||||||
|
if (!m_db->Read(DBHashKey(expected_block_hash), read_out)) {
|
||||||
|
return error("%s: previous block header belongs to unexpected block %s; expected %s",
|
||||||
|
__func__, read_out.first.ToString(), expected_block_hash.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove the new UTXOs that were created from the block
|
||||||
|
for (size_t i = 0; i < block.vtx.size(); ++i) {
|
||||||
|
const auto& tx{block.vtx.at(i)};
|
||||||
|
|
||||||
|
for (size_t j = 0; j < tx->vout.size(); ++j) {
|
||||||
|
const CTxOut& out{tx->vout[j]};
|
||||||
|
COutPoint outpoint{tx->GetHash(), static_cast<uint32_t>(j)};
|
||||||
|
Coin coin{out, pindex->nHeight, tx->IsCoinBase()};
|
||||||
|
|
||||||
|
// Skip unspendable coins
|
||||||
|
if (coin.out.scriptPubKey.IsUnspendable()) continue;
|
||||||
|
|
||||||
|
m_muhash.Remove(MakeUCharSpan(TxOutSer(outpoint, coin)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// The coinbase tx has no undo data since no former output is spent
|
||||||
|
if (!tx->IsCoinBase()) {
|
||||||
|
const auto& tx_undo{block_undo.vtxundo.at(i - 1)};
|
||||||
|
|
||||||
|
for (size_t j = 0; j < tx_undo.vprevout.size(); ++j) {
|
||||||
|
Coin coin{tx_undo.vprevout[j]};
|
||||||
|
COutPoint outpoint{tx->vin[j].prevout.hash, tx->vin[j].prevout.n};
|
||||||
|
|
||||||
|
m_muhash.Insert(MakeUCharSpan(TxOutSer(outpoint, coin)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that the rolled back internal value of muhash is consistent with the DB read out
|
||||||
|
uint256 out;
|
||||||
|
m_muhash.Finalize(out);
|
||||||
|
Assert(read_out.second.muhash == out);
|
||||||
|
|
||||||
|
m_transaction_output_count = read_out.second.transaction_output_count;
|
||||||
|
m_total_amount = read_out.second.total_amount;
|
||||||
|
m_bogo_size = read_out.second.bogo_size;
|
||||||
|
|
||||||
|
return m_db->Write(DB_MUHASH, m_muhash);
|
||||||
|
}
|
52
src/index/coinstatsindex.h
Normal file
52
src/index/coinstatsindex.h
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
// Copyright (c) 2020-2021 The Bitcoin Core developers
|
||||||
|
// Distributed under the MIT software license, see the accompanying
|
||||||
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
|
#ifndef BITCOIN_INDEX_COINSTATSINDEX_H
|
||||||
|
#define BITCOIN_INDEX_COINSTATSINDEX_H
|
||||||
|
|
||||||
|
#include <chain.h>
|
||||||
|
#include <crypto/muhash.h>
|
||||||
|
#include <flatfile.h>
|
||||||
|
#include <index/base.h>
|
||||||
|
#include <node/coinstats.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CoinStatsIndex maintains statistics on the UTXO set.
|
||||||
|
*/
|
||||||
|
class CoinStatsIndex final : public BaseIndex
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
std::string m_name;
|
||||||
|
std::unique_ptr<BaseIndex::DB> m_db;
|
||||||
|
|
||||||
|
MuHash3072 m_muhash;
|
||||||
|
uint64_t m_transaction_output_count{0};
|
||||||
|
uint64_t m_bogo_size{0};
|
||||||
|
CAmount m_total_amount{0};
|
||||||
|
|
||||||
|
bool ReverseBlock(const CBlock& block, const CBlockIndex* pindex);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool Init() override;
|
||||||
|
|
||||||
|
bool WriteBlock(const CBlock& block, const CBlockIndex* pindex) override;
|
||||||
|
|
||||||
|
bool Rewind(const CBlockIndex* current_tip, const CBlockIndex* new_tip) override;
|
||||||
|
|
||||||
|
BaseIndex::DB& GetDB() const override { return *m_db; }
|
||||||
|
|
||||||
|
const char* GetName() const override { return "coinstatsindex"; }
|
||||||
|
|
||||||
|
public:
|
||||||
|
// Constructs the index, which becomes available to be queried.
|
||||||
|
explicit CoinStatsIndex(size_t n_cache_size, bool f_memory = false, bool f_wipe = false);
|
||||||
|
|
||||||
|
// Look up stats for a specific block using CBlockIndex
|
||||||
|
bool LookUpStats(const CBlockIndex* block_index, CCoinsStats& coins_stats) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// The global UTXO set hash object.
|
||||||
|
extern std::unique_ptr<CoinStatsIndex> g_coin_stats_index;
|
||||||
|
|
||||||
|
#endif // BITCOIN_INDEX_COINSTATSINDEX_H
|
|
@ -16,14 +16,22 @@
|
||||||
#include <map>
|
#include <map>
|
||||||
|
|
||||||
// Database-independent metric indicating the UTXO set size
|
// Database-independent metric indicating the UTXO set size
|
||||||
static uint64_t GetBogoSize(const CScript& scriptPubKey)
|
uint64_t GetBogoSize(const CScript& script_pub_key)
|
||||||
{
|
{
|
||||||
return 32 /* txid */ +
|
return 32 /* txid */ +
|
||||||
4 /* vout index */ +
|
4 /* vout index */ +
|
||||||
4 /* height + coinbase */ +
|
4 /* height + coinbase */ +
|
||||||
8 /* amount */ +
|
8 /* amount */ +
|
||||||
2 /* scriptPubKey len */ +
|
2 /* scriptPubKey len */ +
|
||||||
scriptPubKey.size() /* scriptPubKey */;
|
script_pub_key.size() /* scriptPubKey */;
|
||||||
|
}
|
||||||
|
|
||||||
|
CDataStream TxOutSer(const COutPoint& outpoint, const Coin& coin) {
|
||||||
|
CDataStream ss(SER_DISK, PROTOCOL_VERSION);
|
||||||
|
ss << outpoint;
|
||||||
|
ss << static_cast<uint32_t>(coin.nHeight * 2 + coin.fCoinBase);
|
||||||
|
ss << coin.out;
|
||||||
|
return ss;
|
||||||
}
|
}
|
||||||
|
|
||||||
//! Warning: be very careful when changing this! assumeutxo and UTXO snapshot
|
//! Warning: be very careful when changing this! assumeutxo and UTXO snapshot
|
||||||
|
@ -63,12 +71,7 @@ static void ApplyHash(MuHash3072& muhash, const uint256& hash, const std::map<ui
|
||||||
for (auto it = outputs.begin(); it != outputs.end(); ++it) {
|
for (auto it = outputs.begin(); it != outputs.end(); ++it) {
|
||||||
COutPoint outpoint = COutPoint(hash, it->first);
|
COutPoint outpoint = COutPoint(hash, it->first);
|
||||||
Coin coin = it->second;
|
Coin coin = it->second;
|
||||||
|
muhash.Insert(MakeUCharSpan(TxOutSer(outpoint, coin)));
|
||||||
CDataStream ss(SER_DISK, PROTOCOL_VERSION);
|
|
||||||
ss << outpoint;
|
|
||||||
ss << static_cast<uint32_t>(coin.nHeight * 2 + coin.fCoinBase);
|
|
||||||
ss << coin.out;
|
|
||||||
muhash.Insert(MakeUCharSpan(ss));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,9 @@
|
||||||
#define BITCOIN_NODE_COINSTATS_H
|
#define BITCOIN_NODE_COINSTATS_H
|
||||||
|
|
||||||
#include <amount.h>
|
#include <amount.h>
|
||||||
|
#include <chain.h>
|
||||||
|
#include <coins.h>
|
||||||
|
#include <streams.h>
|
||||||
#include <uint256.h>
|
#include <uint256.h>
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
@ -42,4 +45,8 @@ struct CCoinsStats
|
||||||
//! Calculate statistics about the unspent transaction output set
|
//! Calculate statistics about the unspent transaction output set
|
||||||
bool GetUTXOStats(CCoinsView* view, BlockManager& blockman, CCoinsStats& stats, const std::function<void()>& interruption_point = {});
|
bool GetUTXOStats(CCoinsView* view, BlockManager& blockman, CCoinsStats& stats, const std::function<void()>& interruption_point = {});
|
||||||
|
|
||||||
|
uint64_t GetBogoSize(const CScript& script_pub_key);
|
||||||
|
|
||||||
|
CDataStream TxOutSer(const COutPoint& outpoint, const Coin& coin);
|
||||||
|
|
||||||
#endif // BITCOIN_NODE_COINSTATS_H
|
#endif // BITCOIN_NODE_COINSTATS_H
|
||||||
|
|
Loading…
Add table
Reference in a new issue