Bitcoin ABC  0.26.3
P2P Digital Currency
coinstatsindex.cpp
Go to the documentation of this file.
1 // Copyright (c) 2020-2021 The Bitcoin Core developers
2 // Distributed under the MIT software license, see the accompanying
3 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
4 
5 #include <index/coinstatsindex.h>
6 
7 #include <amount.h>
8 #include <chainparams.h>
9 #include <coins.h>
10 #include <crypto/muhash.h>
11 #include <node/blockstorage.h>
12 #include <primitives/blockhash.h>
13 #include <serialize.h>
14 #include <txdb.h>
15 #include <undo.h>
16 #include <util/check.h>
17 #include <validation.h>
18 
19 static constexpr char DB_BLOCK_HASH = 's';
20 static constexpr char DB_BLOCK_HEIGHT = 't';
21 static constexpr char DB_MUHASH = 'M';
22 
23 namespace {
24 
25 struct DBVal {
26  uint256 muhash;
27  uint64_t transaction_output_count;
28  uint64_t bogo_size;
29  Amount total_amount;
30  Amount total_subsidy;
31  Amount total_unspendable_amount;
32  Amount total_prevout_spent_amount;
33  Amount total_new_outputs_ex_coinbase_amount;
34  Amount total_coinbase_amount;
35  Amount total_unspendables_genesis_block;
36  Amount total_unspendables_bip30;
37  Amount total_unspendables_scripts;
38  Amount total_unspendables_unclaimed_rewards;
39 
40  SERIALIZE_METHODS(DBVal, obj) {
41  READWRITE(obj.muhash);
42  READWRITE(obj.transaction_output_count);
43  READWRITE(obj.bogo_size);
44  READWRITE(obj.total_amount);
45  READWRITE(obj.total_subsidy);
46  READWRITE(obj.total_unspendable_amount);
47  READWRITE(obj.total_prevout_spent_amount);
48  READWRITE(obj.total_new_outputs_ex_coinbase_amount);
49  READWRITE(obj.total_coinbase_amount);
50  READWRITE(obj.total_unspendables_genesis_block);
51  READWRITE(obj.total_unspendables_bip30);
52  READWRITE(obj.total_unspendables_scripts);
53  READWRITE(obj.total_unspendables_unclaimed_rewards);
54  }
55 };
56 
57 struct DBHeightKey {
58  int height;
59 
60  explicit DBHeightKey(int height_in) : height(height_in) {}
61 
62  template <typename Stream> void Serialize(Stream &s) const {
64  ser_writedata32be(s, height);
65  }
66 
67  template <typename Stream> void Unserialize(Stream &s) {
68  char prefix{static_cast<char>(ser_readdata8(s))};
69  if (prefix != DB_BLOCK_HEIGHT) {
70  throw std::ios_base::failure(
71  "Invalid format for coinstatsindex DB height key");
72  }
73  height = ser_readdata32be(s);
74  }
75 };
76 
77 struct DBHashKey {
78  BlockHash block_hash;
79 
80  explicit DBHashKey(const BlockHash &hash_in) : block_hash(hash_in) {}
81 
82  SERIALIZE_METHODS(DBHashKey, obj) {
83  char prefix{DB_BLOCK_HASH};
85  if (prefix != DB_BLOCK_HASH) {
86  throw std::ios_base::failure(
87  "Invalid format for coinstatsindex DB hash key");
88  }
89 
90  READWRITE(obj.block_hash);
91  }
92 };
93 
94 }; // namespace
95 
96 std::unique_ptr<CoinStatsIndex> g_coin_stats_index;
97 
98 CoinStatsIndex::CoinStatsIndex(size_t n_cache_size, bool f_memory,
99  bool f_wipe) {
100  fs::path path{gArgs.GetDataDirNet() / "indexes" / "coinstats"};
101  fs::create_directories(path);
102 
103  m_db = std::make_unique<CoinStatsIndex::DB>(path / "db", n_cache_size,
104  f_memory, f_wipe);
105 }
106 
108  const CBlockIndex *pindex) {
109  CBlockUndo block_undo;
110  const Amount block_subsidy{
111  GetBlockSubsidy(pindex->nHeight, Params().GetConsensus())};
112  m_total_subsidy += block_subsidy;
113 
114  // Ignore genesis block
115  if (pindex->nHeight > 0) {
116  if (!UndoReadFromDisk(block_undo, pindex)) {
117  return false;
118  }
119 
120  std::pair<BlockHash, DBVal> read_out;
121  if (!m_db->Read(DBHeightKey(pindex->nHeight - 1), read_out)) {
122  return false;
123  }
124 
125  BlockHash expected_block_hash{pindex->pprev->GetBlockHash()};
126  if (read_out.first != expected_block_hash) {
127  if (!m_db->Read(DBHashKey(expected_block_hash), read_out)) {
128  return error("%s: previous block header belongs to unexpected "
129  "block %s; expected %s",
130  __func__, read_out.first.ToString(),
131  expected_block_hash.ToString());
132  }
133  }
134 
135  // TODO: Deduplicate BIP30 related code
136  bool is_bip30_block{
137  (pindex->nHeight == 91722 &&
138  pindex->GetBlockHash() ==
139  BlockHash{uint256S("0x00000000000271a2dc26e7667f8419f2e15416dc"
140  "6955e5a6c6cdf3f2574dd08e")}) ||
141  (pindex->nHeight == 91812 &&
142  pindex->GetBlockHash() ==
143  BlockHash{uint256S("0x00000000000af0aed4792b1acee3d966af36cf5d"
144  "ef14935db8de83d6f9306f2f")})};
145 
146  // Add the new utxos created from the block
147  for (size_t i = 0; i < block.vtx.size(); ++i) {
148  const auto &tx{block.vtx.at(i)};
149 
150  // Skip duplicate txid coinbase transactions (BIP30).
151  if (is_bip30_block && tx->IsCoinBase()) {
152  m_total_unspendable_amount += block_subsidy;
153  m_total_unspendables_bip30 += block_subsidy;
154  continue;
155  }
156 
157  for (uint32_t j = 0; j < tx->vout.size(); ++j) {
158  const CTxOut &out{tx->vout[j]};
159  Coin coin{out, static_cast<uint32_t>(pindex->nHeight),
160  tx->IsCoinBase()};
161  COutPoint outpoint{tx->GetId(), j};
162 
163  // Skip unspendable coins
164  if (coin.GetTxOut().scriptPubKey.IsUnspendable()) {
165  m_total_unspendable_amount += coin.GetTxOut().nValue;
166  m_total_unspendables_scripts += coin.GetTxOut().nValue;
167  continue;
168  }
169 
170  m_muhash.Insert(MakeUCharSpan(TxOutSer(outpoint, coin)));
171 
172  if (tx->IsCoinBase()) {
173  m_total_coinbase_amount += coin.GetTxOut().nValue;
174  } else {
176  coin.GetTxOut().nValue;
177  }
178 
180  m_total_amount += coin.GetTxOut().nValue;
181  m_bogo_size += GetBogoSize(coin.GetTxOut().scriptPubKey);
182  }
183 
184  // The coinbase tx has no undo data since no former output is spent
185  if (!tx->IsCoinBase()) {
186  const auto &tx_undo{block_undo.vtxundo.at(i - 1)};
187 
188  for (size_t j = 0; j < tx_undo.vprevout.size(); ++j) {
189  Coin coin{tx_undo.vprevout[j]};
190  COutPoint outpoint{tx->vin[j].prevout.GetTxId(),
191  tx->vin[j].prevout.GetN()};
192 
193  m_muhash.Remove(MakeUCharSpan(TxOutSer(outpoint, coin)));
194 
195  m_total_prevout_spent_amount += coin.GetTxOut().nValue;
196 
198  m_total_amount -= coin.GetTxOut().nValue;
199  m_bogo_size -= GetBogoSize(coin.GetTxOut().scriptPubKey);
200  }
201  }
202  }
203  } else {
204  // genesis block
205  m_total_unspendable_amount += block_subsidy;
206  m_total_unspendables_genesis_block += block_subsidy;
207  }
208 
209  // If spent prevouts + block subsidy are still a higher amount than
210  // new outputs + coinbase + current unspendable amount this means
211  // the miner did not claim the full block reward. Unclaimed block
212  // rewards are also unspendable.
213  const Amount unclaimed_rewards{
217  m_total_unspendable_amount += unclaimed_rewards;
218  m_total_unspendables_unclaimed_rewards += unclaimed_rewards;
219 
220  std::pair<BlockHash, DBVal> value;
221  value.first = pindex->GetBlockHash();
222  value.second.transaction_output_count = m_transaction_output_count;
223  value.second.bogo_size = m_bogo_size;
224  value.second.total_amount = m_total_amount;
225  value.second.total_subsidy = m_total_subsidy;
226  value.second.total_unspendable_amount = m_total_unspendable_amount;
227  value.second.total_prevout_spent_amount = m_total_prevout_spent_amount;
228  value.second.total_new_outputs_ex_coinbase_amount =
230  value.second.total_coinbase_amount = m_total_coinbase_amount;
231  value.second.total_unspendables_genesis_block =
233  value.second.total_unspendables_bip30 = m_total_unspendables_bip30;
234  value.second.total_unspendables_scripts = m_total_unspendables_scripts;
235  value.second.total_unspendables_unclaimed_rewards =
237 
238  uint256 out;
239  m_muhash.Finalize(out);
240  value.second.muhash = out;
241 
242  CDBBatch batch(*m_db);
243  batch.Write(DBHeightKey(pindex->nHeight), value);
244  batch.Write(DB_MUHASH, m_muhash);
245  return m_db->WriteBatch(batch);
246 }
247 
249  const std::string &index_name,
250  int start_height, int stop_height) {
251  DBHeightKey key{start_height};
252  db_it.Seek(key);
253 
254  for (int height = start_height; height <= stop_height; ++height) {
255  if (!db_it.GetKey(key) || key.height != height) {
256  return error("%s: unexpected key in %s: expected (%c, %d)",
257  __func__, index_name, DB_BLOCK_HEIGHT, height);
258  }
259 
260  std::pair<BlockHash, DBVal> value;
261  if (!db_it.GetValue(value)) {
262  return error("%s: unable to read value in %s at key (%c, %d)",
263  __func__, index_name, DB_BLOCK_HEIGHT, height);
264  }
265 
266  batch.Write(DBHashKey(value.first), std::move(value.second));
267 
268  db_it.Next();
269  }
270  return true;
271 }
272 
273 bool CoinStatsIndex::Rewind(const CBlockIndex *current_tip,
274  const CBlockIndex *new_tip) {
275  assert(current_tip->GetAncestor(new_tip->nHeight) == new_tip);
276 
277  CDBBatch batch(*m_db);
278  std::unique_ptr<CDBIterator> db_it(m_db->NewIterator());
279 
280  // During a reorg, we need to copy all hash digests for blocks that are
281  // getting disconnected from the height index to the hash index so we can
282  // still find them when the height index entries are overwritten.
283  if (!CopyHeightIndexToHashIndex(*db_it, batch, m_name, new_tip->nHeight,
284  current_tip->nHeight)) {
285  return false;
286  }
287 
288  if (!m_db->WriteBatch(batch)) {
289  return false;
290  }
291 
292  {
293  LOCK(cs_main);
295  current_tip->GetBlockHash())};
296  const auto &consensus_params{Params().GetConsensus()};
297 
298  do {
299  CBlock block;
300 
301  if (!ReadBlockFromDisk(block, iter_tip, consensus_params)) {
302  return error("%s: Failed to read block %s from disk", __func__,
303  iter_tip->GetBlockHash().ToString());
304  }
305 
306  ReverseBlock(block, iter_tip);
307 
308  iter_tip = iter_tip->GetAncestor(iter_tip->nHeight - 1);
309  } while (new_tip != iter_tip);
310  }
311 
312  return BaseIndex::Rewind(current_tip, new_tip);
313 }
314 
315 static bool LookUpOne(const CDBWrapper &db, const CBlockIndex *block_index,
316  DBVal &result) {
317  // First check if the result is stored under the height index and the value
318  // there matches the block hash. This should be the case if the block is on
319  // the active chain.
320  std::pair<BlockHash, DBVal> read_out;
321  if (!db.Read(DBHeightKey(block_index->nHeight), read_out)) {
322  return false;
323  }
324  if (read_out.first == block_index->GetBlockHash()) {
325  result = std::move(read_out.second);
326  return true;
327  }
328 
329  // If value at the height index corresponds to an different block, the
330  // result will be stored in the hash index.
331  return db.Read(DBHashKey(block_index->GetBlockHash()), result);
332 }
333 
334 bool CoinStatsIndex::LookUpStats(const CBlockIndex *block_index,
335  CCoinsStats &coins_stats) const {
336  DBVal entry;
337  if (!LookUpOne(*m_db, block_index, entry)) {
338  return false;
339  }
340 
341  coins_stats.hashSerialized = entry.muhash;
342  coins_stats.nTransactionOutputs = entry.transaction_output_count;
343  coins_stats.nBogoSize = entry.bogo_size;
344  coins_stats.nTotalAmount = entry.total_amount;
345  coins_stats.total_subsidy = entry.total_subsidy;
346  coins_stats.total_unspendable_amount = entry.total_unspendable_amount;
347  coins_stats.total_prevout_spent_amount = entry.total_prevout_spent_amount;
349  entry.total_new_outputs_ex_coinbase_amount;
350  coins_stats.total_coinbase_amount = entry.total_coinbase_amount;
352  entry.total_unspendables_genesis_block;
353  coins_stats.total_unspendables_bip30 = entry.total_unspendables_bip30;
354  coins_stats.total_unspendables_scripts = entry.total_unspendables_scripts;
356  entry.total_unspendables_unclaimed_rewards;
357 
358  return true;
359 }
360 
362  if (!m_db->Read(DB_MUHASH, m_muhash)) {
363  // Check that the cause of the read failure is that the key does not
364  // exist. Any other errors indicate database corruption or a disk
365  // failure, and starting the index would cause further corruption.
366  if (m_db->Exists(DB_MUHASH)) {
367  return error(
368  "%s: Cannot read current %s state; index may be corrupted",
369  __func__, GetName());
370  }
371  }
372 
373  if (BaseIndex::Init()) {
374  const CBlockIndex *pindex{CurrentIndex()};
375 
376  if (pindex) {
377  DBVal entry;
378  if (!LookUpOne(*m_db, pindex, entry)) {
379  return false;
380  }
381 
382  m_transaction_output_count = entry.transaction_output_count;
383  m_bogo_size = entry.bogo_size;
384  m_total_amount = entry.total_amount;
385  m_total_subsidy = entry.total_subsidy;
386  m_total_unspendable_amount = entry.total_unspendable_amount;
387  m_total_prevout_spent_amount = entry.total_prevout_spent_amount;
389  entry.total_new_outputs_ex_coinbase_amount;
390  m_total_coinbase_amount = entry.total_coinbase_amount;
392  entry.total_unspendables_genesis_block;
393  m_total_unspendables_bip30 = entry.total_unspendables_bip30;
394  m_total_unspendables_scripts = entry.total_unspendables_scripts;
396  entry.total_unspendables_unclaimed_rewards;
397  }
398 
399  return true;
400  }
401 
402  return false;
403 }
404 
405 // Reverse a single block as part of a reorg
407  const CBlockIndex *pindex) {
408  CBlockUndo block_undo;
409  std::pair<BlockHash, DBVal> read_out;
410 
411  const Amount block_subsidy{
412  GetBlockSubsidy(pindex->nHeight, Params().GetConsensus())};
413  m_total_subsidy -= block_subsidy;
414 
415  // Ignore genesis block
416  if (pindex->nHeight > 0) {
417  if (!UndoReadFromDisk(block_undo, pindex)) {
418  return false;
419  }
420 
421  if (!m_db->Read(DBHeightKey(pindex->nHeight - 1), read_out)) {
422  return false;
423  }
424 
425  BlockHash expected_block_hash{pindex->pprev->GetBlockHash()};
426  if (read_out.first != expected_block_hash) {
427  if (!m_db->Read(DBHashKey(expected_block_hash), read_out)) {
428  return error("%s: previous block header belongs to unexpected "
429  "block %s; expected %s",
430  __func__, read_out.first.ToString(),
431  expected_block_hash.ToString());
432  }
433  }
434  }
435 
436  // Remove the new UTXOs that were created from the block
437  for (size_t i = 0; i < block.vtx.size(); ++i) {
438  const auto &tx{block.vtx.at(i)};
439 
440  for (uint32_t j = 0; j < tx->vout.size(); ++j) {
441  const CTxOut &out{tx->vout[j]};
442  COutPoint outpoint{tx->GetId(), j};
443  Coin coin{out, static_cast<uint32_t>(pindex->nHeight),
444  tx->IsCoinBase()};
445 
446  // Skip unspendable coins
447  if (coin.GetTxOut().scriptPubKey.IsUnspendable()) {
448  m_total_unspendable_amount -= coin.GetTxOut().nValue;
449  m_total_unspendables_scripts -= coin.GetTxOut().nValue;
450  continue;
451  }
452 
453  m_muhash.Remove(MakeUCharSpan(TxOutSer(outpoint, coin)));
454 
455  if (tx->IsCoinBase()) {
456  m_total_coinbase_amount -= coin.GetTxOut().nValue;
457  } else {
459  coin.GetTxOut().nValue;
460  }
461 
463  m_total_amount -= coin.GetTxOut().nValue;
464  m_bogo_size -= GetBogoSize(coin.GetTxOut().scriptPubKey);
465  }
466 
467  // The coinbase tx has no undo data since no former output is spent
468  if (!tx->IsCoinBase()) {
469  const auto &tx_undo{block_undo.vtxundo.at(i - 1)};
470 
471  for (size_t j = 0; j < tx_undo.vprevout.size(); ++j) {
472  Coin coin{tx_undo.vprevout[j]};
473  COutPoint outpoint{tx->vin[j].prevout.GetTxId(),
474  tx->vin[j].prevout.GetN()};
475 
476  m_muhash.Insert(MakeUCharSpan(TxOutSer(outpoint, coin)));
477 
478  m_total_prevout_spent_amount -= coin.GetTxOut().nValue;
479 
481  m_total_amount += coin.GetTxOut().nValue;
482  m_bogo_size += GetBogoSize(coin.GetTxOut().scriptPubKey);
483  }
484  }
485  }
486 
487  const Amount unclaimed_rewards{
491  m_total_unspendable_amount -= unclaimed_rewards;
492  m_total_unspendables_unclaimed_rewards -= unclaimed_rewards;
493 
494  // Check that the rolled back internal values are consistent with the DB
495  // read out
496  uint256 out;
497  m_muhash.Finalize(out);
498  Assert(read_out.second.muhash == out);
499 
501  read_out.second.transaction_output_count);
502  Assert(m_total_amount == read_out.second.total_amount);
503  Assert(m_bogo_size == read_out.second.bogo_size);
504  Assert(m_total_subsidy == read_out.second.total_subsidy);
506  read_out.second.total_unspendable_amount);
508  read_out.second.total_prevout_spent_amount);
510  read_out.second.total_new_outputs_ex_coinbase_amount);
511  Assert(m_total_coinbase_amount == read_out.second.total_coinbase_amount);
513  read_out.second.total_unspendables_genesis_block);
515  read_out.second.total_unspendables_bip30);
517  read_out.second.total_unspendables_scripts);
519  read_out.second.total_unspendables_unclaimed_rewards);
520 
521  return m_db->Write(DB_MUHASH, m_muhash);
522 }
bool UndoReadFromDisk(CBlockUndo &blockundo, const CBlockIndex *pindex)
bool ReadBlockFromDisk(CBlock &block, const FlatFilePos &pos, const Consensus::Params &params)
Functions for disk access for blocks.
RecursiveMutex cs_main
Global state.
Definition: validation.cpp:91
const CChainParams & Params()
Return the currently selected parameters.
#define Assert(val)
Identity function.
Definition: check.h:56
const fs::path & GetDataDirNet() const
Get data directory path with appended network identifier.
Definition: system.h:257
virtual bool Init()
Initialize internal state from the database and block index.
Definition: base.cpp:56
CChainState * m_chainstate
Definition: base.h:81
const CBlockIndex * CurrentIndex()
Definition: base.h:88
virtual bool Rewind(const CBlockIndex *current_tip, const CBlockIndex *new_tip)
Rewind index to an earlier chain tip during a chain reorg.
Definition: base.cpp:227
CBlockIndex * LookupBlockIndex(const BlockHash &hash) const EXCLUSIVE_LOCKS_REQUIRED(cs_main)
Definition: validation.cpp:127
Definition: block.h:55
std::vector< CTransactionRef > vtx
Definition: block.h:58
The block chain is a tree shaped structure starting with the genesis block at the root,...
Definition: blockindex.h:23
CBlockIndex * pprev
pointer to the index of the predecessor of this block
Definition: blockindex.h:30
CBlockIndex * GetAncestor(int height)
Efficiently find an ancestor of this block.
Definition: blockindex.cpp:71
BlockHash GetBlockHash() const
Definition: blockindex.h:142
int nHeight
height of the entry in the chain. The genesis block has height 0
Definition: blockindex.h:36
Undo information for a CBlock.
Definition: undo.h:73
std::vector< CTxUndo > vtxundo
Definition: undo.h:76
const Consensus::Params & GetConsensus() const
Definition: chainparams.h:86
BlockManager & m_blockman
Reference to a BlockManager instance which itself is shared across all CChainState instances.
Definition: validation.h:811
Batch of changes queued to be written to a CDBWrapper.
Definition: dbwrapper.h:48
void Write(const K &key, const V &value)
Definition: dbwrapper.h:73
bool GetValue(V &value)
Definition: dbwrapper.h:155
bool GetKey(K &key)
Definition: dbwrapper.h:143
void Seek(const K &key)
Definition: dbwrapper.h:133
void Next()
Definition: dbwrapper.cpp:252
An outpoint - a combination of a transaction hash and an index n into its vout.
Definition: transaction.h:22
const TxId & GetTxId() const
Definition: transaction.h:37
An output of a transaction.
Definition: transaction.h:130
A UTXO entry.
Definition: coins.h:27
bool IsCoinBase() const
Definition: coins.h:45
Amount m_total_amount
Amount m_total_prevout_spent_amount
Amount m_total_unspendables_bip30
const char * GetName() const override
Get the name of the index for display in logs.
bool LookUpStats(const CBlockIndex *block_index, CCoinsStats &coins_stats) const
Amount m_total_unspendables_genesis_block
uint64_t m_bogo_size
uint64_t m_transaction_output_count
bool Rewind(const CBlockIndex *current_tip, const CBlockIndex *new_tip) override
Rewind index to an earlier chain tip during a chain reorg.
Amount m_total_coinbase_amount
bool ReverseBlock(const CBlock &block, const CBlockIndex *pindex)
MuHash3072 m_muhash
std::unique_ptr< BaseIndex::DB > m_db
CoinStatsIndex(size_t n_cache_size, bool f_memory=false, bool f_wipe=false)
std::string m_name
Amount m_total_unspendables_scripts
bool WriteBlock(const CBlock &block, const CBlockIndex *pindex) override
Write update index entries for a newly connected block.
Amount m_total_new_outputs_ex_coinbase_amount
Amount m_total_unspendable_amount
Amount m_total_unspendables_unclaimed_rewards
Amount m_total_subsidy
bool Init() override
Initialize internal state from the database and block index.
MuHash3072 & Remove(Span< const uint8_t > in) noexcept
Definition: muhash.cpp:381
void Finalize(uint256 &out) noexcept
Definition: muhash.cpp:353
MuHash3072 & Insert(Span< const uint8_t > in) noexcept
Definition: muhash.cpp:376
Path class wrapper to prepare application code for transition from boost::filesystem library to std::...
Definition: fs.h:33
256-bit opaque blob.
Definition: uint256.h:127
CDataStream TxOutSer(const COutPoint &outpoint, const Coin &coin)
Definition: coinstats.cpp:26
uint64_t GetBogoSize(const CScript &script_pub_key)
Definition: coinstats.cpp:20
static bool LookUpOne(const CDBWrapper &db, const CBlockIndex *block_index, DBVal &result)
static constexpr char DB_BLOCK_HASH
std::unique_ptr< CoinStatsIndex > g_coin_stats_index
The global UTXO set hash object.
static constexpr char DB_BLOCK_HEIGHT
static bool CopyHeightIndexToHashIndex(CDBIterator &db_it, CDBBatch &batch, const std::string &index_name, int start_height, int stop_height)
static constexpr char DB_MUHASH
const char * prefix
Definition: rest.cpp:817
CAddrDb db
Definition: main.cpp:34
uint8_t ser_readdata8(Stream &s)
Definition: serialize.h:98
void ser_writedata32be(Stream &s, uint32_t obj)
Definition: serialize.h:89
#define SERIALIZE_METHODS(cls, obj)
Implement the Serialize and Unserialize methods by delegating to a single templated static method tha...
Definition: serialize.h:227
void Serialize(Stream &s, char a)
Definition: serialize.h:242
void Unserialize(Stream &s, char &a)
Definition: serialize.h:294
void ser_writedata8(Stream &s, uint8_t obj)
Lowest-level serialization and conversion.
Definition: serialize.h:70
uint32_t ser_readdata32be(Stream &s)
Definition: serialize.h:118
#define READWRITE(...)
Definition: serialize.h:180
constexpr auto MakeUCharSpan(V &&v) -> decltype(UCharSpanCast(MakeSpan(std::forward< V >(v))))
Like MakeSpan, but for (const) uint8_t member types only.
Definition: span.h:311
Definition: amount.h:19
A BlockHash is a unqiue identifier for a block.
Definition: blockhash.h:13
uint64_t nTransactionOutputs
Definition: coinstats.h:33
Amount total_new_outputs_ex_coinbase_amount
Definition: coinstats.h:51
uint256 hashSerialized
Definition: coinstats.h:35
Amount total_prevout_spent_amount
Definition: coinstats.h:50
uint64_t nBogoSize
Definition: coinstats.h:34
Amount total_unspendables_genesis_block
Definition: coinstats.h:53
Amount total_unspendables_unclaimed_rewards
Definition: coinstats.h:56
Amount nTotalAmount
Definition: coinstats.h:37
Amount total_subsidy
Definition: coinstats.h:48
Amount total_unspendable_amount
Definition: coinstats.h:49
Amount total_unspendables_scripts
Definition: coinstats.h:55
Amount total_unspendables_bip30
Definition: coinstats.h:54
Amount total_coinbase_amount
Definition: coinstats.h:52
#define LOCK(cs)
Definition: sync.h:243
ArgsManager gArgs
Definition: system.cpp:77
bool error(const char *fmt, const Args &...args)
Definition: system.h:46
uint256 uint256S(const char *str)
uint256 from const char *.
Definition: uint256.h:141
Amount GetBlockSubsidy(int nHeight, const Consensus::Params &consensusParams)
Definition: validation.cpp:799
assert(!tx.IsCoinBase())