diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -503,6 +503,7 @@ net.cpp net_processing.cpp node/coin.cpp + node/coinstats.cpp node/psbt.cpp node/transaction.cpp noui.cpp diff --git a/src/Makefile.am b/src/Makefile.am --- a/src/Makefile.am +++ b/src/Makefile.am @@ -182,6 +182,7 @@ netmessagemaker.h \ node/coin.h \ node/context.h \ + node/coinstats.h \ node/psbt.h \ node/transaction.h \ noui.h \ @@ -310,6 +311,7 @@ net.cpp \ net_processing.cpp \ node/coin.cpp \ + node/coinstats.cpp \ node/psbt.cpp \ node/transaction.cpp \ noui.cpp \ diff --git a/src/node/coinstats.h b/src/node/coinstats.h new file mode 100644 --- /dev/null +++ b/src/node/coinstats.h @@ -0,0 +1,35 @@ +// Copyright (c) 2010 Satoshi Nakamoto +// Copyright (c) 2009-2019 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_NODE_COINSTATS_H +#define BITCOIN_NODE_COINSTATS_H + +#include +#include +#include + +#include + +class CCoinsView; + +struct CCoinsStats { + int nHeight; + BlockHash hashBlock; + uint64_t nTransactions; + uint64_t nTransactionOutputs; + uint64_t nBogoSize; + uint256 hashSerialized; + uint64_t nDiskSize; + Amount nTotalAmount; + + CCoinsStats() + : nHeight(0), nTransactions(0), nTransactionOutputs(0), nBogoSize(0), + nDiskSize(0), nTotalAmount() {} +}; + +//! Calculate statistics about the unspent transaction output set +bool GetUTXOStats(CCoinsView *view, CCoinsStats &stats); + +#endif // BITCOIN_NODE_COINSTATS_H diff --git a/src/node/coinstats.cpp b/src/node/coinstats.cpp new file mode 100644 --- /dev/null +++ b/src/node/coinstats.cpp @@ -0,0 +1,79 @@ +// Copyright (c) 2010 Satoshi Nakamoto +// Copyright (c) 2009-2019 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 + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +static void ApplyStats(CCoinsStats &stats, CHashWriter &ss, const uint256 &hash, + const std::map &outputs) { + assert(!outputs.empty()); + ss << hash; + ss << VARINT(outputs.begin()->second.GetHeight() * 2 + + outputs.begin()->second.IsCoinBase()); + stats.nTransactions++; + for (const auto &output : outputs) { + ss << VARINT(output.first + 1); + ss << output.second.GetTxOut().scriptPubKey; + ss << VARINT(output.second.GetTxOut().nValue / SATOSHI, + VarIntMode::NONNEGATIVE_SIGNED); + stats.nTransactionOutputs++; + stats.nTotalAmount += output.second.GetTxOut().nValue; + stats.nBogoSize += + 32 /* txid */ + 4 /* vout index */ + 4 /* height + coinbase */ + + 8 /* amount */ + 2 /* scriptPubKey len */ + + output.second.GetTxOut().scriptPubKey.size() /* scriptPubKey */; + } + ss << VARINT(0u); +} + +//! Calculate statistics about the unspent transaction output set +bool GetUTXOStats(CCoinsView *view, CCoinsStats &stats) { + std::unique_ptr pcursor(view->Cursor()); + assert(pcursor); + + CHashWriter ss(SER_GETHASH, PROTOCOL_VERSION); + stats.hashBlock = pcursor->GetBestBlock(); + { + LOCK(cs_main); + stats.nHeight = LookupBlockIndex(stats.hashBlock)->nHeight; + } + ss << stats.hashBlock; + uint256 prevkey; + std::map outputs; + while (pcursor->Valid()) { + boost::this_thread::interruption_point(); + COutPoint key; + Coin coin; + if (pcursor->GetKey(key) && pcursor->GetValue(coin)) { + if (!outputs.empty() && key.GetTxId() != prevkey) { + ApplyStats(stats, ss, prevkey, outputs); + outputs.clear(); + } + prevkey = key.GetTxId(); + outputs[key.GetN()] = std::move(coin); + } else { + return error("%s: unable to read value", __func__); + } + pcursor->Next(); + } + if (!outputs.empty()) { + ApplyStats(stats, ss, prevkey, outputs); + } + stats.hashSerialized = ss.GetHash(); + stats.nDiskSize = view->EstimateSize(); + return true; +} 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 #include #include +#include #include #include #include @@ -1043,81 +1044,6 @@ verbosity >= 2); } -struct CCoinsStats { - int nHeight; - BlockHash hashBlock; - uint64_t nTransactions; - uint64_t nTransactionOutputs; - uint64_t nBogoSize; - uint256 hashSerialized; - uint64_t nDiskSize; - Amount nTotalAmount; - - CCoinsStats() - : nHeight(0), nTransactions(0), nTransactionOutputs(0), nBogoSize(0), - nDiskSize(0), nTotalAmount() {} -}; - -static void ApplyStats(CCoinsStats &stats, CHashWriter &ss, const uint256 &hash, - const std::map &outputs) { - assert(!outputs.empty()); - ss << hash; - ss << VARINT(outputs.begin()->second.GetHeight() * 2 + - outputs.begin()->second.IsCoinBase()); - stats.nTransactions++; - for (const auto &output : outputs) { - ss << VARINT(output.first + 1); - ss << output.second.GetTxOut().scriptPubKey; - ss << VARINT(output.second.GetTxOut().nValue / SATOSHI, - VarIntMode::NONNEGATIVE_SIGNED); - stats.nTransactionOutputs++; - stats.nTotalAmount += output.second.GetTxOut().nValue; - stats.nBogoSize += - 32 /* txid */ + 4 /* vout index */ + 4 /* height + coinbase */ + - 8 /* amount */ + 2 /* scriptPubKey len */ + - output.second.GetTxOut().scriptPubKey.size() /* scriptPubKey */; - } - ss << VARINT(0u); -} - -//! Calculate statistics about the unspent transaction output set -static bool GetUTXOStats(CCoinsView *view, CCoinsStats &stats) { - std::unique_ptr pcursor(view->Cursor()); - assert(pcursor); - - CHashWriter ss(SER_GETHASH, PROTOCOL_VERSION); - stats.hashBlock = pcursor->GetBestBlock(); - { - LOCK(cs_main); - stats.nHeight = LookupBlockIndex(stats.hashBlock)->nHeight; - } - ss << stats.hashBlock; - uint256 prevkey; - std::map outputs; - while (pcursor->Valid()) { - boost::this_thread::interruption_point(); - COutPoint key; - Coin coin; - if (pcursor->GetKey(key) && pcursor->GetValue(coin)) { - if (!outputs.empty() && key.GetTxId() != prevkey) { - ApplyStats(stats, ss, prevkey, outputs); - outputs.clear(); - } - prevkey = key.GetTxId(); - outputs[key.GetN()] = std::move(coin); - } else { - return error("%s: unable to read value", __func__); - } - pcursor->Next(); - } - if (!outputs.empty()) { - ApplyStats(stats, ss, prevkey, outputs); - } - stats.hashSerialized = ss.GetHash(); - stats.nDiskSize = view->EstimateSize(); - return true; -} - static UniValue pruneblockchain(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() != 1) {