@@ -23,13 +23,31 @@ struct DBVal {
23
23
uint64_t transaction_output_count;
24
24
uint64_t bogo_size;
25
25
CAmount total_amount;
26
+ CAmount total_subsidy;
27
+ CAmount block_unspendable_amount;
28
+ CAmount block_prevout_spent_amount;
29
+ CAmount block_new_outputs_ex_coinbase_amount;
30
+ CAmount block_coinbase_amount;
31
+ CAmount unspendables_genesis_block;
32
+ CAmount unspendables_bip30;
33
+ CAmount unspendables_scripts;
34
+ CAmount unspendables_unclaimed_rewards;
26
35
27
36
SERIALIZE_METHODS (DBVal, obj)
28
37
{
29
38
READWRITE (obj.muhash );
30
39
READWRITE (obj.transaction_output_count );
31
40
READWRITE (obj.bogo_size );
32
41
READWRITE (obj.total_amount );
42
+ READWRITE (obj.total_subsidy );
43
+ READWRITE (obj.block_unspendable_amount );
44
+ READWRITE (obj.block_prevout_spent_amount );
45
+ READWRITE (obj.block_new_outputs_ex_coinbase_amount );
46
+ READWRITE (obj.block_coinbase_amount );
47
+ READWRITE (obj.unspendables_genesis_block );
48
+ READWRITE (obj.unspendables_bip30 );
49
+ READWRITE (obj.unspendables_scripts );
50
+ READWRITE (obj.unspendables_unclaimed_rewards );
33
51
}
34
52
};
35
53
@@ -88,6 +106,8 @@ CoinStatsIndex::CoinStatsIndex(size_t n_cache_size, bool f_memory, bool f_wipe)
88
106
bool CoinStatsIndex::WriteBlock (const CBlock& block, const CBlockIndex* pindex)
89
107
{
90
108
CBlockUndo block_undo;
109
+ const CAmount block_subsidy{GetBlockSubsidy (pindex->nHeight , Params ().GetConsensus ())};
110
+ m_total_subsidy += block_subsidy;
91
111
92
112
// Ignore genesis block
93
113
if (pindex->nHeight > 0 ) {
@@ -118,6 +138,8 @@ bool CoinStatsIndex::WriteBlock(const CBlock& block, const CBlockIndex* pindex)
118
138
119
139
// Skip duplicate txid coinbase transactions (BIP30).
120
140
if (is_bip30_block && tx->IsCoinBase ()) {
141
+ m_block_unspendable_amount += block_subsidy;
142
+ m_unspendables_bip30 += block_subsidy;
121
143
continue ;
122
144
}
123
145
@@ -127,10 +149,20 @@ bool CoinStatsIndex::WriteBlock(const CBlock& block, const CBlockIndex* pindex)
127
149
COutPoint outpoint{tx->GetHash (), static_cast <uint32_t >(j)};
128
150
129
151
// Skip unspendable coins
130
- if (coin.out .scriptPubKey .IsUnspendable ()) continue ;
152
+ if (coin.out .scriptPubKey .IsUnspendable ()) {
153
+ m_block_unspendable_amount += coin.out .nValue ;
154
+ m_unspendables_scripts += coin.out .nValue ;
155
+ continue ;
156
+ }
131
157
132
158
m_muhash.Insert (MakeUCharSpan (TxOutSer (outpoint, coin)));
133
159
160
+ if (tx->IsCoinBase ()) {
161
+ m_block_coinbase_amount += coin.out .nValue ;
162
+ } else {
163
+ m_block_new_outputs_ex_coinbase_amount += coin.out .nValue ;
164
+ }
165
+
134
166
++m_transaction_output_count;
135
167
m_total_amount += coin.out .nValue ;
136
168
m_bogo_size += GetBogoSize (coin.out .scriptPubKey );
@@ -146,19 +178,42 @@ bool CoinStatsIndex::WriteBlock(const CBlock& block, const CBlockIndex* pindex)
146
178
147
179
m_muhash.Remove (MakeUCharSpan (TxOutSer (outpoint, coin)));
148
180
181
+ m_block_prevout_spent_amount += coin.out .nValue ;
182
+
149
183
--m_transaction_output_count;
150
184
m_total_amount -= coin.out .nValue ;
151
185
m_bogo_size -= GetBogoSize (coin.out .scriptPubKey );
152
186
}
153
187
}
154
188
}
189
+ } else {
190
+ // genesis block
191
+ m_block_unspendable_amount += block_subsidy;
192
+ m_unspendables_genesis_block += block_subsidy;
155
193
}
156
194
195
+ // If spent prevouts + block subsidy are still a higher amount than
196
+ // new outputs + coinbase + current unspendable amount this means
197
+ // the miner did not claim the full block reward. Unclaimed block
198
+ // rewards are also unspendable.
199
+ const CAmount unclaimed_rewards{(m_block_prevout_spent_amount + m_total_subsidy) - (m_block_new_outputs_ex_coinbase_amount + m_block_coinbase_amount + m_block_unspendable_amount)};
200
+ m_block_unspendable_amount += unclaimed_rewards;
201
+ m_unspendables_unclaimed_rewards += unclaimed_rewards;
202
+
157
203
std::pair<uint256, DBVal> value;
158
204
value.first = pindex->GetBlockHash ();
159
205
value.second .transaction_output_count = m_transaction_output_count;
160
206
value.second .bogo_size = m_bogo_size;
161
207
value.second .total_amount = m_total_amount;
208
+ value.second .total_subsidy = m_total_subsidy;
209
+ value.second .block_unspendable_amount = m_block_unspendable_amount;
210
+ value.second .block_prevout_spent_amount = m_block_prevout_spent_amount;
211
+ value.second .block_new_outputs_ex_coinbase_amount = m_block_new_outputs_ex_coinbase_amount;
212
+ value.second .block_coinbase_amount = m_block_coinbase_amount;
213
+ value.second .unspendables_genesis_block = m_unspendables_genesis_block;
214
+ value.second .unspendables_bip30 = m_unspendables_bip30;
215
+ value.second .unspendables_scripts = m_unspendables_scripts;
216
+ value.second .unspendables_unclaimed_rewards = m_unspendables_unclaimed_rewards;
162
217
163
218
uint256 out;
164
219
m_muhash.Finalize (out);
@@ -261,6 +316,15 @@ bool CoinStatsIndex::LookUpStats(const CBlockIndex* block_index, CCoinsStats& co
261
316
coins_stats.nTransactionOutputs = entry.transaction_output_count ;
262
317
coins_stats.nBogoSize = entry.bogo_size ;
263
318
coins_stats.nTotalAmount = entry.total_amount ;
319
+ coins_stats.total_subsidy = entry.total_subsidy ;
320
+ coins_stats.block_unspendable_amount = entry.block_unspendable_amount ;
321
+ coins_stats.block_prevout_spent_amount = entry.block_prevout_spent_amount ;
322
+ coins_stats.block_new_outputs_ex_coinbase_amount = entry.block_new_outputs_ex_coinbase_amount ;
323
+ coins_stats.block_coinbase_amount = entry.block_coinbase_amount ;
324
+ coins_stats.unspendables_genesis_block = entry.unspendables_genesis_block ;
325
+ coins_stats.unspendables_bip30 = entry.unspendables_bip30 ;
326
+ coins_stats.unspendables_scripts = entry.unspendables_scripts ;
327
+ coins_stats.unspendables_unclaimed_rewards = entry.unspendables_unclaimed_rewards ;
264
328
265
329
return true ;
266
330
}
@@ -289,6 +353,15 @@ bool CoinStatsIndex::Init()
289
353
m_transaction_output_count = entry.transaction_output_count ;
290
354
m_bogo_size = entry.bogo_size ;
291
355
m_total_amount = entry.total_amount ;
356
+ m_total_subsidy = entry.total_subsidy ;
357
+ m_block_unspendable_amount = entry.block_unspendable_amount ;
358
+ m_block_prevout_spent_amount = entry.block_prevout_spent_amount ;
359
+ m_block_new_outputs_ex_coinbase_amount = entry.block_new_outputs_ex_coinbase_amount ;
360
+ m_block_coinbase_amount = entry.block_coinbase_amount ;
361
+ m_unspendables_genesis_block = entry.unspendables_genesis_block ;
362
+ m_unspendables_bip30 = entry.unspendables_bip30 ;
363
+ m_unspendables_scripts = entry.unspendables_scripts ;
364
+ m_unspendables_unclaimed_rewards = entry.unspendables_unclaimed_rewards ;
292
365
}
293
366
294
367
return true ;
@@ -303,6 +376,9 @@ bool CoinStatsIndex::ReverseBlock(const CBlock& block, const CBlockIndex* pindex
303
376
CBlockUndo block_undo;
304
377
std::pair<uint256, DBVal> read_out;
305
378
379
+ const CAmount block_subsidy{GetBlockSubsidy (pindex->nHeight , Params ().GetConsensus ())};
380
+ m_total_subsidy -= block_subsidy;
381
+
306
382
// Ignore genesis block
307
383
if (pindex->nHeight > 0 ) {
308
384
if (!UndoReadFromDisk (block_undo, pindex)) {
@@ -332,9 +408,23 @@ bool CoinStatsIndex::ReverseBlock(const CBlock& block, const CBlockIndex* pindex
332
408
Coin coin{out, pindex->nHeight , tx->IsCoinBase ()};
333
409
334
410
// Skip unspendable coins
335
- if (coin.out .scriptPubKey .IsUnspendable ()) continue ;
411
+ if (coin.out .scriptPubKey .IsUnspendable ()) {
412
+ m_block_unspendable_amount -= coin.out .nValue ;
413
+ m_unspendables_scripts -= coin.out .nValue ;
414
+ continue ;
415
+ }
336
416
337
417
m_muhash.Remove (MakeUCharSpan (TxOutSer (outpoint, coin)));
418
+
419
+ if (tx->IsCoinBase ()) {
420
+ m_block_coinbase_amount -= coin.out .nValue ;
421
+ } else {
422
+ m_block_new_outputs_ex_coinbase_amount -= coin.out .nValue ;
423
+ }
424
+
425
+ --m_transaction_output_count;
426
+ m_total_amount -= coin.out .nValue ;
427
+ m_bogo_size -= GetBogoSize (coin.out .scriptPubKey );
338
428
}
339
429
340
430
// The coinbase tx has no undo data since no former output is spent
@@ -346,18 +436,37 @@ bool CoinStatsIndex::ReverseBlock(const CBlock& block, const CBlockIndex* pindex
346
436
COutPoint outpoint{tx->vin [j].prevout .hash , tx->vin [j].prevout .n };
347
437
348
438
m_muhash.Insert (MakeUCharSpan (TxOutSer (outpoint, coin)));
439
+
440
+ m_block_prevout_spent_amount -= coin.out .nValue ;
441
+
442
+ m_transaction_output_count++;
443
+ m_total_amount += coin.out .nValue ;
444
+ m_bogo_size += GetBogoSize (coin.out .scriptPubKey );
349
445
}
350
446
}
351
447
}
352
448
353
- // Check that the rolled back internal value of muhash is consistent with the DB read out
449
+ const CAmount unclaimed_rewards{(m_block_new_outputs_ex_coinbase_amount + m_block_coinbase_amount + m_block_unspendable_amount) - (m_block_prevout_spent_amount + m_total_subsidy)};
450
+ m_block_unspendable_amount -= unclaimed_rewards;
451
+ m_unspendables_unclaimed_rewards -= unclaimed_rewards;
452
+
453
+ // Check that the rolled back internal values are consistent with the DB read out
354
454
uint256 out;
355
455
m_muhash.Finalize (out);
356
456
Assert (read_out.second .muhash == out);
357
457
358
- m_transaction_output_count = read_out.second .transaction_output_count ;
359
- m_total_amount = read_out.second .total_amount ;
360
- m_bogo_size = read_out.second .bogo_size ;
458
+ Assert (m_transaction_output_count == read_out.second .transaction_output_count );
459
+ Assert (m_total_amount == read_out.second .total_amount );
460
+ Assert (m_bogo_size == read_out.second .bogo_size );
461
+ Assert (m_total_subsidy == read_out.second .total_subsidy );
462
+ Assert (m_block_unspendable_amount == read_out.second .block_unspendable_amount );
463
+ Assert (m_block_prevout_spent_amount == read_out.second .block_prevout_spent_amount );
464
+ Assert (m_block_new_outputs_ex_coinbase_amount == read_out.second .block_new_outputs_ex_coinbase_amount );
465
+ Assert (m_block_coinbase_amount == read_out.second .block_coinbase_amount );
466
+ Assert (m_unspendables_genesis_block == read_out.second .unspendables_genesis_block );
467
+ Assert (m_unspendables_bip30 == read_out.second .unspendables_bip30 );
468
+ Assert (m_unspendables_scripts == read_out.second .unspendables_scripts );
469
+ Assert (m_unspendables_unclaimed_rewards == read_out.second .unspendables_unclaimed_rewards );
361
470
362
471
return m_db->Write (DB_MUHASH, m_muhash);
363
472
}
0 commit comments