mirror of
https://github.com/dashpay/dash.git
synced 2024-12-26 04:22:55 +01:00
merge bitcoin#22047: Coinstatsindex follow-ups
This commit is contained in:
parent
28dcd381cd
commit
d97dcb22e1
@ -25,14 +25,14 @@ struct DBVal {
|
|||||||
uint64_t bogo_size;
|
uint64_t bogo_size;
|
||||||
CAmount total_amount;
|
CAmount total_amount;
|
||||||
CAmount total_subsidy;
|
CAmount total_subsidy;
|
||||||
CAmount block_unspendable_amount;
|
CAmount total_unspendable_amount;
|
||||||
CAmount block_prevout_spent_amount;
|
CAmount total_prevout_spent_amount;
|
||||||
CAmount block_new_outputs_ex_coinbase_amount;
|
CAmount total_new_outputs_ex_coinbase_amount;
|
||||||
CAmount block_coinbase_amount;
|
CAmount total_coinbase_amount;
|
||||||
CAmount unspendables_genesis_block;
|
CAmount total_unspendables_genesis_block;
|
||||||
CAmount unspendables_bip30;
|
CAmount total_unspendables_bip30;
|
||||||
CAmount unspendables_scripts;
|
CAmount total_unspendables_scripts;
|
||||||
CAmount unspendables_unclaimed_rewards;
|
CAmount total_unspendables_unclaimed_rewards;
|
||||||
|
|
||||||
SERIALIZE_METHODS(DBVal, obj)
|
SERIALIZE_METHODS(DBVal, obj)
|
||||||
{
|
{
|
||||||
@ -41,14 +41,14 @@ struct DBVal {
|
|||||||
READWRITE(obj.bogo_size);
|
READWRITE(obj.bogo_size);
|
||||||
READWRITE(obj.total_amount);
|
READWRITE(obj.total_amount);
|
||||||
READWRITE(obj.total_subsidy);
|
READWRITE(obj.total_subsidy);
|
||||||
READWRITE(obj.block_unspendable_amount);
|
READWRITE(obj.total_unspendable_amount);
|
||||||
READWRITE(obj.block_prevout_spent_amount);
|
READWRITE(obj.total_prevout_spent_amount);
|
||||||
READWRITE(obj.block_new_outputs_ex_coinbase_amount);
|
READWRITE(obj.total_new_outputs_ex_coinbase_amount);
|
||||||
READWRITE(obj.block_coinbase_amount);
|
READWRITE(obj.total_coinbase_amount);
|
||||||
READWRITE(obj.unspendables_genesis_block);
|
READWRITE(obj.total_unspendables_genesis_block);
|
||||||
READWRITE(obj.unspendables_bip30);
|
READWRITE(obj.total_unspendables_bip30);
|
||||||
READWRITE(obj.unspendables_scripts);
|
READWRITE(obj.total_unspendables_scripts);
|
||||||
READWRITE(obj.unspendables_unclaimed_rewards);
|
READWRITE(obj.total_unspendables_unclaimed_rewards);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -123,9 +123,12 @@ bool CoinStatsIndex::WriteBlock(const CBlock& block, const CBlockIndex* pindex)
|
|||||||
|
|
||||||
uint256 expected_block_hash{pindex->pprev->GetBlockHash()};
|
uint256 expected_block_hash{pindex->pprev->GetBlockHash()};
|
||||||
if (read_out.first != expected_block_hash) {
|
if (read_out.first != expected_block_hash) {
|
||||||
|
LogPrintf("WARNING: previous block header belongs to unexpected block %s; expected %s\n",
|
||||||
|
read_out.first.ToString(), expected_block_hash.ToString());
|
||||||
|
|
||||||
if (!m_db->Read(DBHashKey(expected_block_hash), read_out)) {
|
if (!m_db->Read(DBHashKey(expected_block_hash), read_out)) {
|
||||||
return error("%s: previous block header belongs to unexpected block %s; expected %s",
|
return error("%s: previous block header not found; expected %s",
|
||||||
__func__, read_out.first.ToString(), expected_block_hash.ToString());
|
__func__, expected_block_hash.ToString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -139,29 +142,29 @@ bool CoinStatsIndex::WriteBlock(const CBlock& block, const CBlockIndex* pindex)
|
|||||||
|
|
||||||
// Skip duplicate txid coinbase transactions (BIP30).
|
// Skip duplicate txid coinbase transactions (BIP30).
|
||||||
if (is_bip30_block && tx->IsCoinBase()) {
|
if (is_bip30_block && tx->IsCoinBase()) {
|
||||||
m_block_unspendable_amount += block_subsidy;
|
m_total_unspendable_amount += block_subsidy;
|
||||||
m_unspendables_bip30 += block_subsidy;
|
m_total_unspendables_bip30 += block_subsidy;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (size_t j = 0; j < tx->vout.size(); ++j) {
|
for (uint32_t j = 0; j < tx->vout.size(); ++j) {
|
||||||
const CTxOut& out{tx->vout[j]};
|
const CTxOut& out{tx->vout[j]};
|
||||||
Coin coin{out, pindex->nHeight, tx->IsCoinBase()};
|
Coin coin{out, pindex->nHeight, tx->IsCoinBase()};
|
||||||
COutPoint outpoint{tx->GetHash(), static_cast<uint32_t>(j)};
|
COutPoint outpoint{tx->GetHash(), j};
|
||||||
|
|
||||||
// Skip unspendable coins
|
// Skip unspendable coins
|
||||||
if (coin.out.scriptPubKey.IsUnspendable()) {
|
if (coin.out.scriptPubKey.IsUnspendable()) {
|
||||||
m_block_unspendable_amount += coin.out.nValue;
|
m_total_unspendable_amount += coin.out.nValue;
|
||||||
m_unspendables_scripts += coin.out.nValue;
|
m_total_unspendables_scripts += coin.out.nValue;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_muhash.Insert(MakeUCharSpan(TxOutSer(outpoint, coin)));
|
m_muhash.Insert(MakeUCharSpan(TxOutSer(outpoint, coin)));
|
||||||
|
|
||||||
if (tx->IsCoinBase()) {
|
if (tx->IsCoinBase()) {
|
||||||
m_block_coinbase_amount += coin.out.nValue;
|
m_total_coinbase_amount += coin.out.nValue;
|
||||||
} else {
|
} else {
|
||||||
m_block_new_outputs_ex_coinbase_amount += coin.out.nValue;
|
m_total_new_outputs_ex_coinbase_amount += coin.out.nValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
++m_transaction_output_count;
|
++m_transaction_output_count;
|
||||||
@ -179,7 +182,7 @@ bool CoinStatsIndex::WriteBlock(const CBlock& block, const CBlockIndex* pindex)
|
|||||||
|
|
||||||
m_muhash.Remove(MakeUCharSpan(TxOutSer(outpoint, coin)));
|
m_muhash.Remove(MakeUCharSpan(TxOutSer(outpoint, coin)));
|
||||||
|
|
||||||
m_block_prevout_spent_amount += coin.out.nValue;
|
m_total_prevout_spent_amount += coin.out.nValue;
|
||||||
|
|
||||||
--m_transaction_output_count;
|
--m_transaction_output_count;
|
||||||
m_total_amount -= coin.out.nValue;
|
m_total_amount -= coin.out.nValue;
|
||||||
@ -189,17 +192,17 @@ bool CoinStatsIndex::WriteBlock(const CBlock& block, const CBlockIndex* pindex)
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// genesis block
|
// genesis block
|
||||||
m_block_unspendable_amount += block_subsidy;
|
m_total_unspendable_amount += block_subsidy;
|
||||||
m_unspendables_genesis_block += block_subsidy;
|
m_total_unspendables_genesis_block += block_subsidy;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If spent prevouts + block subsidy are still a higher amount than
|
// If spent prevouts + block subsidy are still a higher amount than
|
||||||
// new outputs + coinbase + current unspendable amount this means
|
// new outputs + coinbase + current unspendable amount this means
|
||||||
// the miner did not claim the full block reward. Unclaimed block
|
// the miner did not claim the full block reward. Unclaimed block
|
||||||
// rewards are also unspendable.
|
// rewards are also unspendable.
|
||||||
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)};
|
const CAmount unclaimed_rewards{(m_total_prevout_spent_amount + m_total_subsidy) - (m_total_new_outputs_ex_coinbase_amount + m_total_coinbase_amount + m_total_unspendable_amount)};
|
||||||
m_block_unspendable_amount += unclaimed_rewards;
|
m_total_unspendable_amount += unclaimed_rewards;
|
||||||
m_unspendables_unclaimed_rewards += unclaimed_rewards;
|
m_total_unspendables_unclaimed_rewards += unclaimed_rewards;
|
||||||
|
|
||||||
std::pair<uint256, DBVal> value;
|
std::pair<uint256, DBVal> value;
|
||||||
value.first = pindex->GetBlockHash();
|
value.first = pindex->GetBlockHash();
|
||||||
@ -207,20 +210,23 @@ bool CoinStatsIndex::WriteBlock(const CBlock& block, const CBlockIndex* pindex)
|
|||||||
value.second.bogo_size = m_bogo_size;
|
value.second.bogo_size = m_bogo_size;
|
||||||
value.second.total_amount = m_total_amount;
|
value.second.total_amount = m_total_amount;
|
||||||
value.second.total_subsidy = m_total_subsidy;
|
value.second.total_subsidy = m_total_subsidy;
|
||||||
value.second.block_unspendable_amount = m_block_unspendable_amount;
|
value.second.total_unspendable_amount = m_total_unspendable_amount;
|
||||||
value.second.block_prevout_spent_amount = m_block_prevout_spent_amount;
|
value.second.total_prevout_spent_amount = m_total_prevout_spent_amount;
|
||||||
value.second.block_new_outputs_ex_coinbase_amount = m_block_new_outputs_ex_coinbase_amount;
|
value.second.total_new_outputs_ex_coinbase_amount = m_total_new_outputs_ex_coinbase_amount;
|
||||||
value.second.block_coinbase_amount = m_block_coinbase_amount;
|
value.second.total_coinbase_amount = m_total_coinbase_amount;
|
||||||
value.second.unspendables_genesis_block = m_unspendables_genesis_block;
|
value.second.total_unspendables_genesis_block = m_total_unspendables_genesis_block;
|
||||||
value.second.unspendables_bip30 = m_unspendables_bip30;
|
value.second.total_unspendables_bip30 = m_total_unspendables_bip30;
|
||||||
value.second.unspendables_scripts = m_unspendables_scripts;
|
value.second.total_unspendables_scripts = m_total_unspendables_scripts;
|
||||||
value.second.unspendables_unclaimed_rewards = m_unspendables_unclaimed_rewards;
|
value.second.total_unspendables_unclaimed_rewards = m_total_unspendables_unclaimed_rewards;
|
||||||
|
|
||||||
uint256 out;
|
uint256 out;
|
||||||
m_muhash.Finalize(out);
|
m_muhash.Finalize(out);
|
||||||
value.second.muhash = out;
|
value.second.muhash = out;
|
||||||
|
|
||||||
return m_db->Write(DBHeightKey(pindex->nHeight), value) && m_db->Write(DB_MUHASH, m_muhash);
|
CDBBatch batch(*m_db);
|
||||||
|
batch.Write(DBHeightKey(pindex->nHeight), value);
|
||||||
|
batch.Write(DB_MUHASH, m_muhash);
|
||||||
|
return m_db->WriteBatch(batch);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool CopyHeightIndexToHashIndex(CDBIterator& db_it, CDBBatch& batch,
|
static bool CopyHeightIndexToHashIndex(CDBIterator& db_it, CDBBatch& batch,
|
||||||
@ -318,14 +324,14 @@ bool CoinStatsIndex::LookUpStats(const CBlockIndex* block_index, CCoinsStats& co
|
|||||||
coins_stats.nBogoSize = entry.bogo_size;
|
coins_stats.nBogoSize = entry.bogo_size;
|
||||||
coins_stats.nTotalAmount = entry.total_amount;
|
coins_stats.nTotalAmount = entry.total_amount;
|
||||||
coins_stats.total_subsidy = entry.total_subsidy;
|
coins_stats.total_subsidy = entry.total_subsidy;
|
||||||
coins_stats.block_unspendable_amount = entry.block_unspendable_amount;
|
coins_stats.total_unspendable_amount = entry.total_unspendable_amount;
|
||||||
coins_stats.block_prevout_spent_amount = entry.block_prevout_spent_amount;
|
coins_stats.total_prevout_spent_amount = entry.total_prevout_spent_amount;
|
||||||
coins_stats.block_new_outputs_ex_coinbase_amount = entry.block_new_outputs_ex_coinbase_amount;
|
coins_stats.total_new_outputs_ex_coinbase_amount = entry.total_new_outputs_ex_coinbase_amount;
|
||||||
coins_stats.block_coinbase_amount = entry.block_coinbase_amount;
|
coins_stats.total_coinbase_amount = entry.total_coinbase_amount;
|
||||||
coins_stats.unspendables_genesis_block = entry.unspendables_genesis_block;
|
coins_stats.total_unspendables_genesis_block = entry.total_unspendables_genesis_block;
|
||||||
coins_stats.unspendables_bip30 = entry.unspendables_bip30;
|
coins_stats.total_unspendables_bip30 = entry.total_unspendables_bip30;
|
||||||
coins_stats.unspendables_scripts = entry.unspendables_scripts;
|
coins_stats.total_unspendables_scripts = entry.total_unspendables_scripts;
|
||||||
coins_stats.unspendables_unclaimed_rewards = entry.unspendables_unclaimed_rewards;
|
coins_stats.total_unspendables_unclaimed_rewards = entry.total_unspendables_unclaimed_rewards;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -342,7 +348,8 @@ bool CoinStatsIndex::Init()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (BaseIndex::Init()) {
|
if (!BaseIndex::Init()) return false;
|
||||||
|
|
||||||
const CBlockIndex* pindex{CurrentIndex()};
|
const CBlockIndex* pindex{CurrentIndex()};
|
||||||
|
|
||||||
if (pindex) {
|
if (pindex) {
|
||||||
@ -355,20 +362,17 @@ bool CoinStatsIndex::Init()
|
|||||||
m_bogo_size = entry.bogo_size;
|
m_bogo_size = entry.bogo_size;
|
||||||
m_total_amount = entry.total_amount;
|
m_total_amount = entry.total_amount;
|
||||||
m_total_subsidy = entry.total_subsidy;
|
m_total_subsidy = entry.total_subsidy;
|
||||||
m_block_unspendable_amount = entry.block_unspendable_amount;
|
m_total_unspendable_amount = entry.total_unspendable_amount;
|
||||||
m_block_prevout_spent_amount = entry.block_prevout_spent_amount;
|
m_total_prevout_spent_amount = entry.total_prevout_spent_amount;
|
||||||
m_block_new_outputs_ex_coinbase_amount = entry.block_new_outputs_ex_coinbase_amount;
|
m_total_new_outputs_ex_coinbase_amount = entry.total_new_outputs_ex_coinbase_amount;
|
||||||
m_block_coinbase_amount = entry.block_coinbase_amount;
|
m_total_coinbase_amount = entry.total_coinbase_amount;
|
||||||
m_unspendables_genesis_block = entry.unspendables_genesis_block;
|
m_total_unspendables_genesis_block = entry.total_unspendables_genesis_block;
|
||||||
m_unspendables_bip30 = entry.unspendables_bip30;
|
m_total_unspendables_bip30 = entry.total_unspendables_bip30;
|
||||||
m_unspendables_scripts = entry.unspendables_scripts;
|
m_total_unspendables_scripts = entry.total_unspendables_scripts;
|
||||||
m_unspendables_unclaimed_rewards = entry.unspendables_unclaimed_rewards;
|
m_total_unspendables_unclaimed_rewards = entry.total_unspendables_unclaimed_rewards;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reverse a single block as part of a reorg
|
// Reverse a single block as part of a reorg
|
||||||
@ -392,9 +396,12 @@ bool CoinStatsIndex::ReverseBlock(const CBlock& block, const CBlockIndex* pindex
|
|||||||
|
|
||||||
uint256 expected_block_hash{pindex->pprev->GetBlockHash()};
|
uint256 expected_block_hash{pindex->pprev->GetBlockHash()};
|
||||||
if (read_out.first != expected_block_hash) {
|
if (read_out.first != expected_block_hash) {
|
||||||
|
LogPrintf("WARNING: previous block header belongs to unexpected block %s; expected %s\n",
|
||||||
|
read_out.first.ToString(), expected_block_hash.ToString());
|
||||||
|
|
||||||
if (!m_db->Read(DBHashKey(expected_block_hash), read_out)) {
|
if (!m_db->Read(DBHashKey(expected_block_hash), read_out)) {
|
||||||
return error("%s: previous block header belongs to unexpected block %s; expected %s",
|
return error("%s: previous block header not found; expected %s",
|
||||||
__func__, read_out.first.ToString(), expected_block_hash.ToString());
|
__func__, expected_block_hash.ToString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -403,24 +410,24 @@ bool CoinStatsIndex::ReverseBlock(const CBlock& block, const CBlockIndex* pindex
|
|||||||
for (size_t i = 0; i < block.vtx.size(); ++i) {
|
for (size_t i = 0; i < block.vtx.size(); ++i) {
|
||||||
const auto& tx{block.vtx.at(i)};
|
const auto& tx{block.vtx.at(i)};
|
||||||
|
|
||||||
for (size_t j = 0; j < tx->vout.size(); ++j) {
|
for (uint32_t j = 0; j < tx->vout.size(); ++j) {
|
||||||
const CTxOut& out{tx->vout[j]};
|
const CTxOut& out{tx->vout[j]};
|
||||||
COutPoint outpoint{tx->GetHash(), static_cast<uint32_t>(j)};
|
COutPoint outpoint{tx->GetHash(), j};
|
||||||
Coin coin{out, pindex->nHeight, tx->IsCoinBase()};
|
Coin coin{out, pindex->nHeight, tx->IsCoinBase()};
|
||||||
|
|
||||||
// Skip unspendable coins
|
// Skip unspendable coins
|
||||||
if (coin.out.scriptPubKey.IsUnspendable()) {
|
if (coin.out.scriptPubKey.IsUnspendable()) {
|
||||||
m_block_unspendable_amount -= coin.out.nValue;
|
m_total_unspendable_amount -= coin.out.nValue;
|
||||||
m_unspendables_scripts -= coin.out.nValue;
|
m_total_unspendables_scripts -= coin.out.nValue;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_muhash.Remove(MakeUCharSpan(TxOutSer(outpoint, coin)));
|
m_muhash.Remove(MakeUCharSpan(TxOutSer(outpoint, coin)));
|
||||||
|
|
||||||
if (tx->IsCoinBase()) {
|
if (tx->IsCoinBase()) {
|
||||||
m_block_coinbase_amount -= coin.out.nValue;
|
m_total_coinbase_amount -= coin.out.nValue;
|
||||||
} else {
|
} else {
|
||||||
m_block_new_outputs_ex_coinbase_amount -= coin.out.nValue;
|
m_total_new_outputs_ex_coinbase_amount -= coin.out.nValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
--m_transaction_output_count;
|
--m_transaction_output_count;
|
||||||
@ -438,7 +445,7 @@ bool CoinStatsIndex::ReverseBlock(const CBlock& block, const CBlockIndex* pindex
|
|||||||
|
|
||||||
m_muhash.Insert(MakeUCharSpan(TxOutSer(outpoint, coin)));
|
m_muhash.Insert(MakeUCharSpan(TxOutSer(outpoint, coin)));
|
||||||
|
|
||||||
m_block_prevout_spent_amount -= coin.out.nValue;
|
m_total_prevout_spent_amount -= coin.out.nValue;
|
||||||
|
|
||||||
m_transaction_output_count++;
|
m_transaction_output_count++;
|
||||||
m_total_amount += coin.out.nValue;
|
m_total_amount += coin.out.nValue;
|
||||||
@ -447,9 +454,9 @@ bool CoinStatsIndex::ReverseBlock(const CBlock& block, const CBlockIndex* pindex
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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)};
|
const CAmount unclaimed_rewards{(m_total_new_outputs_ex_coinbase_amount + m_total_coinbase_amount + m_total_unspendable_amount) - (m_total_prevout_spent_amount + m_total_subsidy)};
|
||||||
m_block_unspendable_amount -= unclaimed_rewards;
|
m_total_unspendable_amount -= unclaimed_rewards;
|
||||||
m_unspendables_unclaimed_rewards -= unclaimed_rewards;
|
m_total_unspendables_unclaimed_rewards -= unclaimed_rewards;
|
||||||
|
|
||||||
// Check that the rolled back internal values are consistent with the DB read out
|
// Check that the rolled back internal values are consistent with the DB read out
|
||||||
uint256 out;
|
uint256 out;
|
||||||
@ -460,14 +467,14 @@ bool CoinStatsIndex::ReverseBlock(const CBlock& block, const CBlockIndex* pindex
|
|||||||
Assert(m_total_amount == read_out.second.total_amount);
|
Assert(m_total_amount == read_out.second.total_amount);
|
||||||
Assert(m_bogo_size == read_out.second.bogo_size);
|
Assert(m_bogo_size == read_out.second.bogo_size);
|
||||||
Assert(m_total_subsidy == read_out.second.total_subsidy);
|
Assert(m_total_subsidy == read_out.second.total_subsidy);
|
||||||
Assert(m_block_unspendable_amount == read_out.second.block_unspendable_amount);
|
Assert(m_total_unspendable_amount == read_out.second.total_unspendable_amount);
|
||||||
Assert(m_block_prevout_spent_amount == read_out.second.block_prevout_spent_amount);
|
Assert(m_total_prevout_spent_amount == read_out.second.total_prevout_spent_amount);
|
||||||
Assert(m_block_new_outputs_ex_coinbase_amount == read_out.second.block_new_outputs_ex_coinbase_amount);
|
Assert(m_total_new_outputs_ex_coinbase_amount == read_out.second.total_new_outputs_ex_coinbase_amount);
|
||||||
Assert(m_block_coinbase_amount == read_out.second.block_coinbase_amount);
|
Assert(m_total_coinbase_amount == read_out.second.total_coinbase_amount);
|
||||||
Assert(m_unspendables_genesis_block == read_out.second.unspendables_genesis_block);
|
Assert(m_total_unspendables_genesis_block == read_out.second.total_unspendables_genesis_block);
|
||||||
Assert(m_unspendables_bip30 == read_out.second.unspendables_bip30);
|
Assert(m_total_unspendables_bip30 == read_out.second.total_unspendables_bip30);
|
||||||
Assert(m_unspendables_scripts == read_out.second.unspendables_scripts);
|
Assert(m_total_unspendables_scripts == read_out.second.total_unspendables_scripts);
|
||||||
Assert(m_unspendables_unclaimed_rewards == read_out.second.unspendables_unclaimed_rewards);
|
Assert(m_total_unspendables_unclaimed_rewards == read_out.second.total_unspendables_unclaimed_rewards);
|
||||||
|
|
||||||
return m_db->Write(DB_MUHASH, m_muhash);
|
return m_db->Write(DB_MUHASH, m_muhash);
|
||||||
}
|
}
|
||||||
|
@ -25,14 +25,14 @@ private:
|
|||||||
uint64_t m_bogo_size{0};
|
uint64_t m_bogo_size{0};
|
||||||
CAmount m_total_amount{0};
|
CAmount m_total_amount{0};
|
||||||
CAmount m_total_subsidy{0};
|
CAmount m_total_subsidy{0};
|
||||||
CAmount m_block_unspendable_amount{0};
|
CAmount m_total_unspendable_amount{0};
|
||||||
CAmount m_block_prevout_spent_amount{0};
|
CAmount m_total_prevout_spent_amount{0};
|
||||||
CAmount m_block_new_outputs_ex_coinbase_amount{0};
|
CAmount m_total_new_outputs_ex_coinbase_amount{0};
|
||||||
CAmount m_block_coinbase_amount{0};
|
CAmount m_total_coinbase_amount{0};
|
||||||
CAmount m_unspendables_genesis_block{0};
|
CAmount m_total_unspendables_genesis_block{0};
|
||||||
CAmount m_unspendables_bip30{0};
|
CAmount m_total_unspendables_bip30{0};
|
||||||
CAmount m_unspendables_scripts{0};
|
CAmount m_total_unspendables_scripts{0};
|
||||||
CAmount m_unspendables_unclaimed_rewards{0};
|
CAmount m_total_unspendables_unclaimed_rewards{0};
|
||||||
|
|
||||||
bool ReverseBlock(const CBlock& block, const CBlockIndex* pindex);
|
bool ReverseBlock(const CBlock& block, const CBlockIndex* pindex);
|
||||||
|
|
||||||
|
@ -45,15 +45,25 @@ struct CCoinsStats
|
|||||||
bool index_used{false};
|
bool index_used{false};
|
||||||
|
|
||||||
// Following values are only available from coinstats index
|
// Following values are only available from coinstats index
|
||||||
|
|
||||||
|
//! Total cumulative amount of block subsidies up to and including this block
|
||||||
CAmount total_subsidy{0};
|
CAmount total_subsidy{0};
|
||||||
CAmount block_unspendable_amount{0};
|
//! Total cumulative amount of unspendable coins up to and including this block
|
||||||
CAmount block_prevout_spent_amount{0};
|
CAmount total_unspendable_amount{0};
|
||||||
CAmount block_new_outputs_ex_coinbase_amount{0};
|
//! Total cumulative amount of prevouts spent up to and including this block
|
||||||
CAmount block_coinbase_amount{0};
|
CAmount total_prevout_spent_amount{0};
|
||||||
CAmount unspendables_genesis_block{0};
|
//! Total cumulative amount of outputs created up to and including this block
|
||||||
CAmount unspendables_bip30{0};
|
CAmount total_new_outputs_ex_coinbase_amount{0};
|
||||||
CAmount unspendables_scripts{0};
|
//! Total cumulative amount of coinbase outputs up to and including this block
|
||||||
CAmount unspendables_unclaimed_rewards{0};
|
CAmount total_coinbase_amount{0};
|
||||||
|
//! The unspendable coinbase amount from the genesis block
|
||||||
|
CAmount total_unspendables_genesis_block{0};
|
||||||
|
//! The two unspendable coinbase outputs total amount caused by BIP30
|
||||||
|
CAmount total_unspendables_bip30{0};
|
||||||
|
//! Total cumulative amount of outputs sent to unspendable scripts (OP_RETURN for example) up to and including this block
|
||||||
|
CAmount total_unspendables_scripts{0};
|
||||||
|
//! Total cumulative amount of coins lost due to unclaimed miner rewards up to and including this block
|
||||||
|
CAmount total_unspendables_unclaimed_rewards{0};
|
||||||
|
|
||||||
CCoinsStats(CoinStatsHashType hash_type) : m_hash_type(hash_type) {}
|
CCoinsStats(CoinStatsHashType hash_type) : m_hash_type(hash_type) {}
|
||||||
};
|
};
|
||||||
|
@ -1344,13 +1344,13 @@ static UniValue gettxoutsetinfo(const JSONRPCRequest& request)
|
|||||||
{RPCResult::Type::STR_AMOUNT, "total_unspendable_amount", "The total amount of coins permanently excluded from the UTXO set (only available if coinstatsindex is used)"},
|
{RPCResult::Type::STR_AMOUNT, "total_unspendable_amount", "The total amount of coins permanently excluded from the UTXO set (only available if coinstatsindex is used)"},
|
||||||
{RPCResult::Type::OBJ, "block_info", "Info on amounts in the block at this block height (only available if coinstatsindex is used)",
|
{RPCResult::Type::OBJ, "block_info", "Info on amounts in the block at this block height (only available if coinstatsindex is used)",
|
||||||
{
|
{
|
||||||
{RPCResult::Type::STR_AMOUNT, "prevout_spent", ""},
|
{RPCResult::Type::STR_AMOUNT, "prevout_spent", "Total amount of all prevouts spent in this block"},
|
||||||
{RPCResult::Type::STR_AMOUNT, "coinbase", ""},
|
{RPCResult::Type::STR_AMOUNT, "coinbase", "Coinbase subsidy amount of this block"},
|
||||||
{RPCResult::Type::STR_AMOUNT, "new_outputs_ex_coinbase", ""},
|
{RPCResult::Type::STR_AMOUNT, "new_outputs_ex_coinbase", "Total amount of new outputs created by this block"},
|
||||||
{RPCResult::Type::STR_AMOUNT, "unspendable", ""},
|
{RPCResult::Type::STR_AMOUNT, "unspendable", "Total amount of unspendable outputs created in this block"},
|
||||||
{RPCResult::Type::OBJ, "unspendables", "Detailed view of the unspendable categories",
|
{RPCResult::Type::OBJ, "unspendables", "Detailed view of the unspendable categories",
|
||||||
{
|
{
|
||||||
{RPCResult::Type::STR_AMOUNT, "genesis_block", ""},
|
{RPCResult::Type::STR_AMOUNT, "genesis_block", "The unspendable amount of the Genesis block subsidy"},
|
||||||
{RPCResult::Type::STR_AMOUNT, "bip30", "Transactions overridden by duplicates (no longer possible with BIP30)"},
|
{RPCResult::Type::STR_AMOUNT, "bip30", "Transactions overridden by duplicates (no longer possible with BIP30)"},
|
||||||
{RPCResult::Type::STR_AMOUNT, "scripts", "Amounts sent to scripts that are unspendable (for example OP_RETURN outputs)"},
|
{RPCResult::Type::STR_AMOUNT, "scripts", "Amounts sent to scripts that are unspendable (for example OP_RETURN outputs)"},
|
||||||
{RPCResult::Type::STR_AMOUNT, "unclaimed_rewards", "Fee rewards that miners did not claim in their coinbase transaction"},
|
{RPCResult::Type::STR_AMOUNT, "unclaimed_rewards", "Fee rewards that miners did not claim in their coinbase transaction"},
|
||||||
@ -1402,6 +1402,18 @@ static UniValue gettxoutsetinfo(const JSONRPCRequest& request)
|
|||||||
pindex = ParseHashOrHeight(request.params[1], chainman);
|
pindex = ParseHashOrHeight(request.params[1], chainman);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (stats.index_requested && g_coin_stats_index) {
|
||||||
|
if (!g_coin_stats_index->BlockUntilSyncedToCurrentChain()) {
|
||||||
|
const IndexSummary summary{g_coin_stats_index->GetSummary()};
|
||||||
|
|
||||||
|
// If a specific block was requested and the index has already synced past that height, we can return the
|
||||||
|
// data already even though the index is not fully synced yet.
|
||||||
|
if (pindex->nHeight > summary.best_block_height) {
|
||||||
|
throw JSONRPCError(RPC_INTERNAL_ERROR, strprintf("Unable to get data because coinstatsindex is still syncing. Current height: %d", summary.best_block_height));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (GetUTXOStats(coins_view, *blockman, stats, node.rpc_interruption_point, pindex)) {
|
if (GetUTXOStats(coins_view, *blockman, stats, node.rpc_interruption_point, pindex)) {
|
||||||
ret.pushKV("height", (int64_t)stats.nHeight);
|
ret.pushKV("height", (int64_t)stats.nHeight);
|
||||||
ret.pushKV("bestblock", stats.hashBlock.GetHex());
|
ret.pushKV("bestblock", stats.hashBlock.GetHex());
|
||||||
@ -1418,7 +1430,7 @@ static UniValue gettxoutsetinfo(const JSONRPCRequest& request)
|
|||||||
ret.pushKV("transactions", static_cast<int64_t>(stats.nTransactions));
|
ret.pushKV("transactions", static_cast<int64_t>(stats.nTransactions));
|
||||||
ret.pushKV("disk_size", stats.nDiskSize);
|
ret.pushKV("disk_size", stats.nDiskSize);
|
||||||
} else {
|
} else {
|
||||||
ret.pushKV("total_unspendable_amount", ValueFromAmount(stats.block_unspendable_amount));
|
ret.pushKV("total_unspendable_amount", ValueFromAmount(stats.total_unspendable_amount));
|
||||||
|
|
||||||
CCoinsStats prev_stats{hash_type};
|
CCoinsStats prev_stats{hash_type};
|
||||||
|
|
||||||
@ -1427,28 +1439,21 @@ static UniValue gettxoutsetinfo(const JSONRPCRequest& request)
|
|||||||
}
|
}
|
||||||
|
|
||||||
UniValue block_info(UniValue::VOBJ);
|
UniValue block_info(UniValue::VOBJ);
|
||||||
block_info.pushKV("prevout_spent", ValueFromAmount(stats.block_prevout_spent_amount - prev_stats.block_prevout_spent_amount));
|
block_info.pushKV("prevout_spent", ValueFromAmount(stats.total_prevout_spent_amount - prev_stats.total_prevout_spent_amount));
|
||||||
block_info.pushKV("coinbase", ValueFromAmount(stats.block_coinbase_amount - prev_stats.block_coinbase_amount));
|
block_info.pushKV("coinbase", ValueFromAmount(stats.total_coinbase_amount - prev_stats.total_coinbase_amount));
|
||||||
block_info.pushKV("new_outputs_ex_coinbase", ValueFromAmount(stats.block_new_outputs_ex_coinbase_amount - prev_stats.block_new_outputs_ex_coinbase_amount));
|
block_info.pushKV("new_outputs_ex_coinbase", ValueFromAmount(stats.total_new_outputs_ex_coinbase_amount - prev_stats.total_new_outputs_ex_coinbase_amount));
|
||||||
block_info.pushKV("unspendable", ValueFromAmount(stats.block_unspendable_amount - prev_stats.block_unspendable_amount));
|
block_info.pushKV("unspendable", ValueFromAmount(stats.total_unspendable_amount - prev_stats.total_unspendable_amount));
|
||||||
|
|
||||||
UniValue unspendables(UniValue::VOBJ);
|
UniValue unspendables(UniValue::VOBJ);
|
||||||
unspendables.pushKV("genesis_block", ValueFromAmount(stats.unspendables_genesis_block - prev_stats.unspendables_genesis_block));
|
unspendables.pushKV("genesis_block", ValueFromAmount(stats.total_unspendables_genesis_block - prev_stats.total_unspendables_genesis_block));
|
||||||
unspendables.pushKV("bip30", ValueFromAmount(stats.unspendables_bip30 - prev_stats.unspendables_bip30));
|
unspendables.pushKV("bip30", ValueFromAmount(stats.total_unspendables_bip30 - prev_stats.total_unspendables_bip30));
|
||||||
unspendables.pushKV("scripts", ValueFromAmount(stats.unspendables_scripts - prev_stats.unspendables_scripts));
|
unspendables.pushKV("scripts", ValueFromAmount(stats.total_unspendables_scripts - prev_stats.total_unspendables_scripts));
|
||||||
unspendables.pushKV("unclaimed_rewards", ValueFromAmount(stats.unspendables_unclaimed_rewards - prev_stats.unspendables_unclaimed_rewards));
|
unspendables.pushKV("unclaimed_rewards", ValueFromAmount(stats.total_unspendables_unclaimed_rewards - prev_stats.total_unspendables_unclaimed_rewards));
|
||||||
block_info.pushKV("unspendables", unspendables);
|
block_info.pushKV("unspendables", unspendables);
|
||||||
|
|
||||||
ret.pushKV("block_info", block_info);
|
ret.pushKV("block_info", block_info);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (g_coin_stats_index) {
|
|
||||||
const IndexSummary summary{g_coin_stats_index->GetSummary()};
|
|
||||||
|
|
||||||
if (!summary.synced) {
|
|
||||||
throw JSONRPCError(RPC_INTERNAL_ERROR, strprintf("Unable to read UTXO set because coinstatsindex is still syncing. Current height: %d", summary.best_block_height));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throw JSONRPCError(RPC_INTERNAL_ERROR, "Unable to read UTXO set");
|
throw JSONRPCError(RPC_INTERNAL_ERROR, "Unable to read UTXO set");
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -34,8 +34,6 @@ from test_framework.test_framework import BitcoinTestFramework
|
|||||||
from test_framework.util import (
|
from test_framework.util import (
|
||||||
assert_equal,
|
assert_equal,
|
||||||
assert_raises_rpc_error,
|
assert_raises_rpc_error,
|
||||||
try_rpc,
|
|
||||||
wait_until,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
class CoinStatsIndexTest(BitcoinTestFramework):
|
class CoinStatsIndexTest(BitcoinTestFramework):
|
||||||
@ -92,13 +90,11 @@ class CoinStatsIndexTest(BitcoinTestFramework):
|
|||||||
self.sync_blocks(timeout=120)
|
self.sync_blocks(timeout=120)
|
||||||
|
|
||||||
self.log.info("Test that gettxoutsetinfo() output is consistent with or without coinstatsindex option")
|
self.log.info("Test that gettxoutsetinfo() output is consistent with or without coinstatsindex option")
|
||||||
wait_until(lambda: not try_rpc(-32603, "Unable to read UTXO set", node.gettxoutsetinfo))
|
|
||||||
res0 = node.gettxoutsetinfo('none')
|
res0 = node.gettxoutsetinfo('none')
|
||||||
|
|
||||||
# The fields 'disk_size' and 'transactions' do not exist on the index
|
# The fields 'disk_size' and 'transactions' do not exist on the index
|
||||||
del res0['disk_size'], res0['transactions']
|
del res0['disk_size'], res0['transactions']
|
||||||
|
|
||||||
wait_until(lambda: not try_rpc(-32603, "Unable to read UTXO set", index_node.gettxoutsetinfo, 'muhash'))
|
|
||||||
for hash_option in index_hash_options:
|
for hash_option in index_hash_options:
|
||||||
res1 = index_node.gettxoutsetinfo(hash_option)
|
res1 = index_node.gettxoutsetinfo(hash_option)
|
||||||
# The fields 'block_info' and 'total_unspendable_amount' only exist on the index
|
# The fields 'block_info' and 'total_unspendable_amount' only exist on the index
|
||||||
@ -113,7 +109,6 @@ class CoinStatsIndexTest(BitcoinTestFramework):
|
|||||||
# Generate a new tip
|
# Generate a new tip
|
||||||
node.generate(5)
|
node.generate(5)
|
||||||
|
|
||||||
wait_until(lambda: not try_rpc(-32603, "Unable to read UTXO set", index_node.gettxoutsetinfo, 'muhash'))
|
|
||||||
for hash_option in index_hash_options:
|
for hash_option in index_hash_options:
|
||||||
# Fetch old stats by height
|
# Fetch old stats by height
|
||||||
res2 = index_node.gettxoutsetinfo(hash_option, 102)
|
res2 = index_node.gettxoutsetinfo(hash_option, 102)
|
||||||
@ -192,7 +187,6 @@ class CoinStatsIndexTest(BitcoinTestFramework):
|
|||||||
self.nodes[0].generate(1)
|
self.nodes[0].generate(1)
|
||||||
self.sync_all()
|
self.sync_all()
|
||||||
|
|
||||||
wait_until(lambda: not try_rpc(-32603, "Unable to read UTXO set", index_node.gettxoutsetinfo, 'muhash'))
|
|
||||||
for hash_option in index_hash_options:
|
for hash_option in index_hash_options:
|
||||||
# Check all amounts were registered correctly
|
# Check all amounts were registered correctly
|
||||||
res6 = index_node.gettxoutsetinfo(hash_option, 108)
|
res6 = index_node.gettxoutsetinfo(hash_option, 108)
|
||||||
@ -225,7 +219,6 @@ class CoinStatsIndexTest(BitcoinTestFramework):
|
|||||||
self.nodes[0].submitblock(ToHex(block))
|
self.nodes[0].submitblock(ToHex(block))
|
||||||
self.sync_all()
|
self.sync_all()
|
||||||
|
|
||||||
wait_until(lambda: not try_rpc(-32603, "Unable to read UTXO set", index_node.gettxoutsetinfo, 'muhash'))
|
|
||||||
for hash_option in index_hash_options:
|
for hash_option in index_hash_options:
|
||||||
res7 = index_node.gettxoutsetinfo(hash_option, 109)
|
res7 = index_node.gettxoutsetinfo(hash_option, 109)
|
||||||
assert_equal(res7['total_unspendable_amount'], Decimal('530.98999999'))
|
assert_equal(res7['total_unspendable_amount'], Decimal('530.98999999'))
|
||||||
@ -251,7 +244,6 @@ class CoinStatsIndexTest(BitcoinTestFramework):
|
|||||||
assert_equal(res8, res9)
|
assert_equal(res8, res9)
|
||||||
|
|
||||||
index_node.generate(1)
|
index_node.generate(1)
|
||||||
wait_until(lambda: not try_rpc(-32603, "Unable to read UTXO set", index_node.gettxoutsetinfo, 'muhash'))
|
|
||||||
res10 = index_node.gettxoutsetinfo('muhash')
|
res10 = index_node.gettxoutsetinfo('muhash')
|
||||||
assert(res8['txouts'] < res10['txouts'])
|
assert(res8['txouts'] < res10['txouts'])
|
||||||
|
|
||||||
@ -272,14 +264,12 @@ class CoinStatsIndexTest(BitcoinTestFramework):
|
|||||||
index_node = self.nodes[1]
|
index_node = self.nodes[1]
|
||||||
reorg_blocks = index_node.generatetoaddress(2, index_node.getnewaddress())
|
reorg_blocks = index_node.generatetoaddress(2, index_node.getnewaddress())
|
||||||
reorg_block = reorg_blocks[1]
|
reorg_block = reorg_blocks[1]
|
||||||
wait_until(lambda: not try_rpc(-32603, "Unable to read UTXO set", index_node.gettxoutsetinfo, 'muhash'))
|
|
||||||
res_invalid = index_node.gettxoutsetinfo('muhash')
|
res_invalid = index_node.gettxoutsetinfo('muhash')
|
||||||
index_node.invalidateblock(reorg_blocks[0])
|
index_node.invalidateblock(reorg_blocks[0])
|
||||||
assert_equal(index_node.gettxoutsetinfo('muhash')['height'], 110)
|
assert_equal(index_node.gettxoutsetinfo('muhash')['height'], 110)
|
||||||
|
|
||||||
# Add two new blocks
|
# Add two new blocks
|
||||||
block = index_node.generate(2)[1]
|
block = index_node.generate(2)[1]
|
||||||
wait_until(lambda: not try_rpc(-32603, "Unable to read UTXO set", index_node.gettxoutsetinfo, 'muhash'))
|
|
||||||
res = index_node.gettxoutsetinfo(hash_type='muhash', hash_or_height=None, use_index=False)
|
res = index_node.gettxoutsetinfo(hash_type='muhash', hash_or_height=None, use_index=False)
|
||||||
|
|
||||||
# Test that the result of the reorged block is not returned for its old block height
|
# Test that the result of the reorged block is not returned for its old block height
|
||||||
@ -300,9 +290,7 @@ class CoinStatsIndexTest(BitcoinTestFramework):
|
|||||||
# Ensure that removing and re-adding blocks yields consistent results
|
# Ensure that removing and re-adding blocks yields consistent results
|
||||||
block = index_node.getblockhash(99)
|
block = index_node.getblockhash(99)
|
||||||
index_node.invalidateblock(block)
|
index_node.invalidateblock(block)
|
||||||
wait_until(lambda: not try_rpc(-32603, "Unable to read UTXO set", index_node.gettxoutsetinfo, 'muhash'))
|
|
||||||
index_node.reconsiderblock(block)
|
index_node.reconsiderblock(block)
|
||||||
wait_until(lambda: not try_rpc(-32603, "Unable to read UTXO set", index_node.gettxoutsetinfo, 'muhash'))
|
|
||||||
res3 = index_node.gettxoutsetinfo(hash_type='muhash', hash_or_height=112)
|
res3 = index_node.gettxoutsetinfo(hash_type='muhash', hash_or_height=112)
|
||||||
assert_equal(res2, res3)
|
assert_equal(res2, res3)
|
||||||
|
|
||||||
@ -312,8 +300,7 @@ class CoinStatsIndexTest(BitcoinTestFramework):
|
|||||||
node.getblock(reorg_block)
|
node.getblock(reorg_block)
|
||||||
|
|
||||||
self.restart_node(0, ["-coinstatsindex"])
|
self.restart_node(0, ["-coinstatsindex"])
|
||||||
wait_until(lambda: not try_rpc(-32603, "Unable to read UTXO set", node.gettxoutsetinfo, 'muhash'))
|
assert_raises_rpc_error(-32603, "Unable to get data because coinstatsindex is still syncing.", node.gettxoutsetinfo, 'muhash', reorg_block)
|
||||||
assert_raises_rpc_error(-32603, "Unable to read UTXO set", node.gettxoutsetinfo, 'muhash', reorg_block)
|
|
||||||
|
|
||||||
def _test_index_rejects_hash_serialized(self):
|
def _test_index_rejects_hash_serialized(self):
|
||||||
self.log.info("Test that the rpc raises if the legacy hash is passed with the index")
|
self.log.info("Test that the rpc raises if the legacy hash is passed with the index")
|
||||||
|
Loading…
Reference in New Issue
Block a user