Bitcoin ABC  0.28.12
P2P Digital Currency
coinstats.cpp
Go to the documentation of this file.
1 // Copyright (c) 2010 Satoshi Nakamoto
2 // Copyright (c) 2009-2019 The Bitcoin Core developers
3 // Distributed under the MIT software license, see the accompanying
4 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
5 
6 #include <node/coinstats.h>
7 
8 #include <coins.h>
9 #include <crypto/muhash.h>
10 #include <hash.h>
11 #include <index/coinstatsindex.h>
12 #include <optional>
13 #include <primitives/txid.h>
14 #include <serialize.h>
15 #include <util/check.h>
16 #include <util/system.h>
17 #include <validation.h>
18 
19 #include <map>
20 
21 namespace node {
22 CCoinsStats::CCoinsStats(int block_height, const BlockHash &block_hash)
23  : nHeight(block_height), hashBlock(block_hash) {}
24 
25 uint64_t GetBogoSize(const CScript &script_pub_key) {
26  return 32 /* txid */ + 4 /* vout index */ + 4 /* height + coinbase */ +
27  8 /* amount */ + 2 /* scriptPubKey len */ +
28  script_pub_key.size() /* scriptPubKey */;
29 }
30 
31 CDataStream TxOutSer(const COutPoint &outpoint, const Coin &coin) {
33  ss << outpoint;
34  ss << static_cast<uint32_t>(coin.GetHeight() * 2 + coin.IsCoinBase());
35  ss << coin.GetTxOut();
36  return ss;
37 }
38 
51 static void ApplyHash(CHashWriter &ss, const TxId &txid,
52  const std::map<uint32_t, Coin> &outputs) {
53  for (auto it = outputs.begin(); it != outputs.end(); ++it) {
54  if (it == outputs.begin()) {
55  ss << txid;
56  ss << VARINT(it->second.GetHeight() * 2 + it->second.IsCoinBase());
57  }
58 
59  ss << VARINT(it->first + 1);
60  ss << it->second.GetTxOut().scriptPubKey;
61  ss << VARINT_MODE(it->second.GetTxOut().nValue / SATOSHI,
63 
64  if (it == std::prev(outputs.end())) {
65  ss << VARINT(0u);
66  }
67  }
68 }
69 
70 static void ApplyHash(std::nullptr_t, const TxId &txid,
71  const std::map<uint32_t, Coin> &outputs) {}
72 
73 static void ApplyHash(MuHash3072 &muhash, const TxId &txid,
74  const std::map<uint32_t, Coin> &outputs) {
75  for (auto it = outputs.begin(); it != outputs.end(); ++it) {
76  COutPoint outpoint = COutPoint(txid, it->first);
77  Coin coin = it->second;
78  muhash.Insert(MakeUCharSpan(TxOutSer(outpoint, coin)));
79  }
80 }
81 
82 static void ApplyStats(CCoinsStats &stats, const TxId &txid,
83  const std::map<uint32_t, Coin> &outputs) {
84  assert(!outputs.empty());
85  stats.nTransactions++;
86  for (auto it = outputs.begin(); it != outputs.end(); ++it) {
87  stats.nTransactionOutputs++;
88  stats.nTotalAmount += it->second.GetTxOut().nValue;
89  stats.nBogoSize += GetBogoSize(it->second.GetTxOut().scriptPubKey);
90  }
91 }
92 
94 template <typename T>
95 static bool ComputeUTXOStats(CCoinsView *view, CCoinsStats &stats, T hash_obj,
96  const std::function<void()> &interruption_point) {
97  std::unique_ptr<CCoinsViewCursor> pcursor(view->Cursor());
98  assert(pcursor);
99 
100  PrepareHash(hash_obj, stats);
101 
102  TxId prevkey;
103  std::map<uint32_t, Coin> outputs;
104  while (pcursor->Valid()) {
105  interruption_point();
106  COutPoint key;
107  Coin coin;
108  if (pcursor->GetKey(key) && pcursor->GetValue(coin)) {
109  if (!outputs.empty() && key.GetTxId() != prevkey) {
110  ApplyStats(stats, prevkey, outputs);
111  ApplyHash(hash_obj, prevkey, outputs);
112  outputs.clear();
113  }
114  prevkey = key.GetTxId();
115  outputs[key.GetN()] = std::move(coin);
116  stats.coins_count++;
117  } else {
118  return error("%s: unable to read value", __func__);
119  }
120  pcursor->Next();
121  }
122  if (!outputs.empty()) {
123  ApplyStats(stats, prevkey, outputs);
124  ApplyHash(hash_obj, prevkey, outputs);
125  }
126 
127  FinalizeHash(hash_obj, stats);
128 
129  stats.nDiskSize = view->EstimateSize();
130 
131  return true;
132 }
133 
134 std::optional<CCoinsStats>
136  BlockManager &blockman,
137  const std::function<void()> &interruption_point) {
138  CBlockIndex *pindex = WITH_LOCK(
139  ::cs_main, return blockman.LookupBlockIndex(view->GetBestBlock()));
140  CCoinsStats stats{Assert(pindex)->nHeight, pindex->GetBlockHash()};
141 
142  bool success = [&]() -> bool {
143  switch (hash_type) {
146  return ComputeUTXOStats(view, stats, ss, interruption_point);
147  }
148  case (CoinStatsHashType::MUHASH): {
149  MuHash3072 muhash;
150  return ComputeUTXOStats(view, stats, muhash,
151  interruption_point);
152  }
153  case (CoinStatsHashType::NONE): {
154  return ComputeUTXOStats(view, stats, nullptr,
155  interruption_point);
156  }
157  } // no default case, so the compiler can warn about missing cases
158  assert(false);
159  }();
160 
161  if (!success) {
162  return std::nullopt;
163  }
164  return stats;
165 }
166 
167 // The legacy hash serializes the hashBlock
168 static void PrepareHash(CHashWriter &ss, const CCoinsStats &stats) {
169  ss << stats.hashBlock;
170 }
171 // MuHash does not need the prepare step
172 static void PrepareHash(MuHash3072 &muhash, CCoinsStats &stats) {}
173 static void PrepareHash(std::nullptr_t, CCoinsStats &stats) {}
174 
175 static void FinalizeHash(CHashWriter &ss, CCoinsStats &stats) {
176  stats.hashSerialized = ss.GetHash();
177 }
178 static void FinalizeHash(MuHash3072 &muhash, CCoinsStats &stats) {
179  uint256 out;
180  muhash.Finalize(out);
181  stats.hashSerialized = out;
182 }
183 static void FinalizeHash(std::nullptr_t, CCoinsStats &stats) {}
184 
185 std::optional<CCoinsStats>
187  CoinStatsHashType hash_type,
188  const std::function<void()> &interruption_point,
189  const CBlockIndex *pindex, bool index_requested) {
190  // Use CoinStatsIndex if it is requested and available and a hash_type of
191  // Muhash or None was requested
192  if ((hash_type == CoinStatsHashType::MUHASH ||
193  hash_type == CoinStatsHashType::NONE) &&
194  g_coin_stats_index && index_requested) {
195  if (pindex) {
196  return g_coin_stats_index->LookUpStats(pindex);
197  } else {
198  CBlockIndex *block_index = WITH_LOCK(
199  ::cs_main,
200  return blockman.LookupBlockIndex(view->GetBestBlock()));
201  return g_coin_stats_index->LookUpStats(block_index);
202  }
203  }
204 
205  // If the coinstats index isn't requested or is otherwise not usable, the
206  // pindex should either be null or equal to the view's best block. This is
207  // because without the coinstats index we can only get coinstats about the
208  // best block.
209  assert(!pindex || pindex->GetBlockHash() == view->GetBestBlock());
210 
211  return ComputeUTXOStats(hash_type, view, blockman, interruption_point);
212 }
213 } // namespace node
static constexpr Amount SATOSHI
Definition: amount.h:143
#define Assert(val)
Identity function.
Definition: check.h:84
The block chain is a tree shaped structure starting with the genesis block at the root,...
Definition: blockindex.h:26
BlockHash GetBlockHash() const
Definition: blockindex.h:147
Abstract view on the open txout dataset.
Definition: coins.h:147
virtual CCoinsViewCursor * Cursor() const
Get a cursor to iterate over the whole state.
Definition: coins.cpp:25
virtual BlockHash GetBestBlock() const
Retrieve the block hash whose state this CCoinsView currently represents.
Definition: coins.cpp:16
virtual size_t EstimateSize() const
Estimate database size (0 if not implemented)
Definition: coins.h:180
Double ended buffer combining vector and stream-like interfaces.
Definition: streams.h:199
A writer stream (for serialization) that computes a 256-bit hash.
Definition: hash.h:99
uint256 GetHash()
Compute the double-SHA256 hash of all data written to this object.
Definition: hash.h:122
An outpoint - a combination of a transaction hash and an index n into its vout.
Definition: transaction.h:20
uint32_t GetN() const
Definition: transaction.h:36
const TxId & GetTxId() const
Definition: transaction.h:35
Serialized script, used inside transaction inputs and outputs.
Definition: script.h:431
A UTXO entry.
Definition: coins.h:27
uint32_t GetHeight() const
Definition: coins.h:44
bool IsCoinBase() const
Definition: coins.h:45
CTxOut & GetTxOut()
Definition: coins.h:48
A class representing MuHash sets.
Definition: muhash.h:96
void Finalize(uint256 &out) noexcept
Definition: muhash.cpp:353
MuHash3072 & Insert(Span< const uint8_t > in) noexcept
Definition: muhash.cpp:376
Maintains a tree of blocks (stored in m_block_index) which is consulted to determine where the most-w...
Definition: blockstorage.h:68
CBlockIndex * LookupBlockIndex(const BlockHash &hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
size_type size() const
Definition: prevector.h:386
256-bit opaque blob.
Definition: uint256.h:127
std::unique_ptr< CoinStatsIndex > g_coin_stats_index
The global UTXO set hash object.
RecursiveMutex cs_main
Mutex to guard access to validation specific variables, such as reading or changing the chainstate.
Definition: cs_main.cpp:7
unsigned int nHeight
Definition: init.h:28
static void FinalizeHash(CHashWriter &ss, CCoinsStats &stats)
Definition: coinstats.cpp:175
CoinStatsHashType
Definition: coinstats.h:26
static void ApplyHash(CHashWriter &ss, const TxId &txid, const std::map< uint32_t, Coin > &outputs)
Warning: be very careful when changing this! assumeutxo and UTXO snapshot validation commitments are ...
Definition: coinstats.cpp:51
static bool ComputeUTXOStats(CCoinsView *view, CCoinsStats &stats, T hash_obj, const std::function< void()> &interruption_point)
Calculate statistics about the unspent transaction output set.
Definition: coinstats.cpp:95
CDataStream TxOutSer(const COutPoint &outpoint, const Coin &coin)
Definition: coinstats.cpp:31
uint64_t GetBogoSize(const CScript &script_pub_key)
Definition: coinstats.cpp:25
static void PrepareHash(CHashWriter &ss, const CCoinsStats &stats)
Definition: coinstats.cpp:168
static void ApplyStats(CCoinsStats &stats, const TxId &txid, const std::map< uint32_t, Coin > &outputs)
Definition: coinstats.cpp:82
std::optional< CCoinsStats > GetUTXOStats(CCoinsView *view, BlockManager &blockman, CoinStatsHashType hash_type, const std::function< void()> &interruption_point, const CBlockIndex *pindex, bool index_requested)
Calculate statistics about the unspent transaction output set.
Definition: coinstats.cpp:186
#define VARINT(obj)
Definition: serialize.h:597
#define VARINT_MODE(obj, mode)
Definition: serialize.h:596
@ NONNEGATIVE_SIGNED
@ SER_DISK
Definition: serialize.h:167
@ SER_GETHASH
Definition: serialize.h:168
constexpr auto MakeUCharSpan(V &&v) -> decltype(UCharSpanCast(Span{std::forward< V >(v)}))
Like the Span constructor, but for (const) uint8_t member types only.
Definition: span.h:307
A BlockHash is a unqiue identifier for a block.
Definition: blockhash.h:13
A TxId is the identifier of a transaction.
Definition: txid.h:14
Amount nTotalAmount
Definition: coinstats.h:40
uint64_t nBogoSize
Definition: coinstats.h:37
uint64_t nDiskSize
Definition: coinstats.h:39
CCoinsStats()=default
BlockHash hashBlock
Definition: coinstats.h:34
uint64_t nTransactions
Definition: coinstats.h:35
uint64_t nTransactionOutputs
Definition: coinstats.h:36
uint256 hashSerialized
Definition: coinstats.h:38
uint64_t coins_count
The number of coins contained.
Definition: coinstats.h:43
#define WITH_LOCK(cs, code)
Run code while locking a mutex.
Definition: sync.h:357
bool error(const char *fmt, const Args &...args)
Definition: system.h:45
assert(!tx.IsCoinBase())
static const int PROTOCOL_VERSION
network protocol versioning
Definition: version.h:11