Merge 73dd2c3acae58b8c9882dc7084fba0fd9f293c7b into db2c57ae9eebdb75c58cd165ac929919969c19a9

This commit is contained in:
Fabian Jahr 2025-03-17 10:40:49 +01:00 committed by GitHub
commit b36ab96b5b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 173 additions and 124 deletions

3
.gitignore vendored
View File

@ -21,3 +21,6 @@ target/
/guix-build-*
/ci/scratch/
# Test data that includes .log files
!test/functional/data/coinstatsindex_v0/*

View File

@ -94,6 +94,7 @@ private:
bool Rewind(const CBlockIndex* current_tip, const CBlockIndex* new_tip);
virtual bool AllowPrune() const = 0;
virtual uint32_t GetVersion() const = 0;
template <typename... Args>
void FatalErrorf(util::ConstevalFormatString<sizeof...(Args)> fmt, const Args&... args);

View File

@ -46,6 +46,7 @@ private:
uint256 m_last_header{};
bool AllowPrune() const override { return true; }
uint32_t GetVersion() const override { return 0; }
bool Write(const BlockFilter& filter, uint32_t block_height, const uint256& filter_header);

View File

@ -23,6 +23,7 @@ using kernel::RemoveCoinHash;
static constexpr uint8_t DB_BLOCK_HASH{'s'};
static constexpr uint8_t DB_BLOCK_HEIGHT{'t'};
static constexpr uint8_t DB_MUHASH{'M'};
static constexpr uint8_t DB_VERSION{'V'};
namespace {
@ -31,15 +32,17 @@ struct DBVal {
uint64_t transaction_output_count;
uint64_t bogo_size;
CAmount total_amount;
CAmount total_subsidy;
CAmount total_unspendable_amount;
CAmount total_prevout_spent_amount;
CAmount total_new_outputs_ex_coinbase_amount;
CAmount total_coinbase_amount;
CAmount total_unspendables_genesis_block;
CAmount total_unspendables_bip30;
CAmount total_unspendables_scripts;
CAmount total_unspendables_unclaimed_rewards;
CAmount block_subsidy;
CAmount block_prevout_spent_amount;
CAmount block_new_outputs_ex_coinbase_amount;
CAmount block_coinbase_amount;
CAmount block_unspendables_genesis_block;
CAmount block_unspendables_bip30;
CAmount block_unspendables_scripts;
CAmount block_unspendables_unclaimed_rewards;
SERIALIZE_METHODS(DBVal, obj)
{
@ -47,15 +50,15 @@ struct DBVal {
READWRITE(obj.transaction_output_count);
READWRITE(obj.bogo_size);
READWRITE(obj.total_amount);
READWRITE(obj.total_subsidy);
READWRITE(obj.total_unspendable_amount);
READWRITE(obj.total_prevout_spent_amount);
READWRITE(obj.total_new_outputs_ex_coinbase_amount);
READWRITE(obj.total_coinbase_amount);
READWRITE(obj.total_unspendables_genesis_block);
READWRITE(obj.total_unspendables_bip30);
READWRITE(obj.total_unspendables_scripts);
READWRITE(obj.total_unspendables_unclaimed_rewards);
READWRITE(obj.block_subsidy);
READWRITE(obj.block_prevout_spent_amount);
READWRITE(obj.block_new_outputs_ex_coinbase_amount);
READWRITE(obj.block_coinbase_amount);
READWRITE(obj.block_unspendables_genesis_block);
READWRITE(obj.block_unspendables_bip30);
READWRITE(obj.block_unspendables_scripts);
READWRITE(obj.block_unspendables_unclaimed_rewards);
}
};
@ -114,9 +117,18 @@ CoinStatsIndex::CoinStatsIndex(std::unique_ptr<interfaces::Chain> chain, size_t
bool CoinStatsIndex::CustomAppend(const interfaces::BlockInfo& block)
{
CBlockUndo block_undo;
const CAmount block_subsidy{GetBlockSubsidy(block.height, Params().GetConsensus())};
m_total_subsidy += block_subsidy;
CAmount block_unspendable{0};
CBlockUndo block_undo{};
m_block_prevout_spent_amount = 0;
m_block_new_outputs_ex_coinbase_amount = 0;
m_block_coinbase_amount = 0;
m_block_subsidy = GetBlockSubsidy(block.height, Params().GetConsensus());
m_block_unspendables_genesis_block = 0;
m_block_unspendables_bip30 = 0;
m_block_unspendables_scripts = 0;
m_block_unspendables_unclaimed_rewards = 0;
// Ignore genesis block
if (block.height > 0) {
@ -151,8 +163,8 @@ bool CoinStatsIndex::CustomAppend(const interfaces::BlockInfo& block)
// Skip duplicate txid coinbase transactions (BIP30).
if (IsBIP30Unspendable(*pindex) && tx->IsCoinBase()) {
m_total_unspendable_amount += block_subsidy;
m_total_unspendables_bip30 += block_subsidy;
block_unspendable += m_block_subsidy;
m_block_unspendables_bip30 += m_block_subsidy;
continue;
}
@ -163,17 +175,17 @@ bool CoinStatsIndex::CustomAppend(const interfaces::BlockInfo& block)
// Skip unspendable coins
if (coin.out.scriptPubKey.IsUnspendable()) {
m_total_unspendable_amount += coin.out.nValue;
m_total_unspendables_scripts += coin.out.nValue;
block_unspendable += coin.out.nValue;
m_block_unspendables_scripts += coin.out.nValue;
continue;
}
ApplyCoinHash(m_muhash, outpoint, coin);
if (tx->IsCoinBase()) {
m_total_coinbase_amount += coin.out.nValue;
m_block_coinbase_amount += coin.out.nValue;
} else {
m_total_new_outputs_ex_coinbase_amount += coin.out.nValue;
m_block_new_outputs_ex_coinbase_amount += coin.out.nValue;
}
++m_transaction_output_count;
@ -191,7 +203,7 @@ bool CoinStatsIndex::CustomAppend(const interfaces::BlockInfo& block)
RemoveCoinHash(m_muhash, outpoint, coin);
m_total_prevout_spent_amount += coin.out.nValue;
m_block_prevout_spent_amount += coin.out.nValue;
--m_transaction_output_count;
m_total_amount -= coin.out.nValue;
@ -201,32 +213,33 @@ bool CoinStatsIndex::CustomAppend(const interfaces::BlockInfo& block)
}
} else {
// genesis block
m_total_unspendable_amount += block_subsidy;
m_total_unspendables_genesis_block += block_subsidy;
block_unspendable += m_block_subsidy;
m_block_unspendables_genesis_block += m_block_subsidy;
}
// If spent prevouts + block subsidy are still a higher amount than
// new outputs + coinbase + current unspendable amount this means
// the miner did not claim the full block reward. Unclaimed block
// rewards are also unspendable.
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_total_unspendable_amount += unclaimed_rewards;
m_total_unspendables_unclaimed_rewards += unclaimed_rewards;
m_block_unspendables_unclaimed_rewards = (m_block_prevout_spent_amount + m_block_subsidy) - (m_block_new_outputs_ex_coinbase_amount + m_block_coinbase_amount + block_unspendable);
m_total_unspendable_amount += (m_block_unspendables_unclaimed_rewards + block_unspendable);
std::pair<uint256, DBVal> value;
value.first = block.hash;
value.second.transaction_output_count = m_transaction_output_count;
value.second.bogo_size = m_bogo_size;
value.second.total_amount = m_total_amount;
value.second.total_subsidy = m_total_subsidy;
value.second.total_unspendable_amount = m_total_unspendable_amount;
value.second.total_prevout_spent_amount = m_total_prevout_spent_amount;
value.second.total_new_outputs_ex_coinbase_amount = m_total_new_outputs_ex_coinbase_amount;
value.second.total_coinbase_amount = m_total_coinbase_amount;
value.second.total_unspendables_genesis_block = m_total_unspendables_genesis_block;
value.second.total_unspendables_bip30 = m_total_unspendables_bip30;
value.second.total_unspendables_scripts = m_total_unspendables_scripts;
value.second.total_unspendables_unclaimed_rewards = m_total_unspendables_unclaimed_rewards;
value.second.block_subsidy = m_block_subsidy;
value.second.block_prevout_spent_amount = m_block_prevout_spent_amount;
value.second.block_new_outputs_ex_coinbase_amount = m_block_new_outputs_ex_coinbase_amount;
value.second.block_coinbase_amount = m_block_coinbase_amount;
value.second.block_unspendables_genesis_block = m_block_unspendables_genesis_block;
value.second.block_unspendables_bip30 = m_block_unspendables_bip30;
value.second.block_unspendables_scripts = m_block_unspendables_scripts;
value.second.block_unspendables_unclaimed_rewards = m_block_unspendables_unclaimed_rewards;
uint256 out;
m_muhash.Finalize(out);
@ -337,21 +350,44 @@ std::optional<CCoinsStats> CoinStatsIndex::LookUpStats(const CBlockIndex& block_
stats.nTransactionOutputs = entry.transaction_output_count;
stats.nBogoSize = entry.bogo_size;
stats.total_amount = entry.total_amount;
stats.total_subsidy = entry.total_subsidy;
stats.total_unspendable_amount = entry.total_unspendable_amount;
stats.total_prevout_spent_amount = entry.total_prevout_spent_amount;
stats.total_new_outputs_ex_coinbase_amount = entry.total_new_outputs_ex_coinbase_amount;
stats.total_coinbase_amount = entry.total_coinbase_amount;
stats.total_unspendables_genesis_block = entry.total_unspendables_genesis_block;
stats.total_unspendables_bip30 = entry.total_unspendables_bip30;
stats.total_unspendables_scripts = entry.total_unspendables_scripts;
stats.total_unspendables_unclaimed_rewards = entry.total_unspendables_unclaimed_rewards;
stats.block_subsidy = entry.block_subsidy;
stats.block_prevout_spent_amount = entry.block_prevout_spent_amount;
stats.block_new_outputs_ex_coinbase_amount = entry.block_new_outputs_ex_coinbase_amount;
stats.block_coinbase_amount = entry.block_coinbase_amount;
stats.block_unspendables_genesis_block = entry.block_unspendables_genesis_block;
stats.block_unspendables_bip30 = entry.block_unspendables_bip30;
stats.block_unspendables_scripts = entry.block_unspendables_scripts;
stats.block_unspendables_unclaimed_rewards = entry.block_unspendables_unclaimed_rewards;
return stats;
}
bool CoinStatsIndex::CustomInit(const std::optional<interfaces::BlockRef>& block)
{
uint32_t code_version{GetVersion()};
uint32_t db_version{0};
// We are starting the index for the first time and write version first so
// we don't run into the version check later.
if (!block.has_value() && !m_db->Exists(DB_VERSION)) {
m_db->Write(DB_VERSION, code_version);
db_version = code_version;
}
// If we can't read a version this means the index has never been updated
// and needs to be reset now. Otherwise request a reset if we have a
// version mismatch.
if (m_db->Exists(DB_VERSION)) {
m_db->Read(DB_VERSION, db_version);
}
if (db_version != code_version) {
LogError("%s version mismatch: expected %s but %s was found. In order to rebuild the index, remove the indexes/coinstats directory in your datadir\n",
GetName(), code_version, db_version);
return false;
}
if (!m_db->Read(DB_MUHASH, m_muhash)) {
// Check that the cause of the read failure is that the key does not
// exist. Any other errors indicate database corruption or a disk
@ -382,15 +418,17 @@ bool CoinStatsIndex::CustomInit(const std::optional<interfaces::BlockRef>& block
m_transaction_output_count = entry.transaction_output_count;
m_bogo_size = entry.bogo_size;
m_total_amount = entry.total_amount;
m_total_subsidy = entry.total_subsidy;
m_total_unspendable_amount = entry.total_unspendable_amount;
m_total_prevout_spent_amount = entry.total_prevout_spent_amount;
m_total_new_outputs_ex_coinbase_amount = entry.total_new_outputs_ex_coinbase_amount;
m_total_coinbase_amount = entry.total_coinbase_amount;
m_total_unspendables_genesis_block = entry.total_unspendables_genesis_block;
m_total_unspendables_bip30 = entry.total_unspendables_bip30;
m_total_unspendables_scripts = entry.total_unspendables_scripts;
m_total_unspendables_unclaimed_rewards = entry.total_unspendables_unclaimed_rewards;
m_block_subsidy = entry.block_subsidy;
m_block_prevout_spent_amount = entry.block_prevout_spent_amount;
m_block_new_outputs_ex_coinbase_amount = entry.block_new_outputs_ex_coinbase_amount;
m_block_coinbase_amount = entry.block_coinbase_amount;
m_block_unspendables_genesis_block = entry.block_unspendables_genesis_block;
m_block_unspendables_bip30 = entry.block_unspendables_bip30;
m_block_unspendables_scripts = entry.block_unspendables_scripts;
m_block_unspendables_unclaimed_rewards = entry.block_unspendables_unclaimed_rewards;
}
return true;
@ -410,9 +448,6 @@ bool CoinStatsIndex::ReverseBlock(const CBlock& block, const CBlockIndex* pindex
CBlockUndo block_undo;
std::pair<uint256, DBVal> read_out;
const CAmount block_subsidy{GetBlockSubsidy(pindex->nHeight, Params().GetConsensus())};
m_total_subsidy -= block_subsidy;
// Ignore genesis block
if (pindex->nHeight > 0) {
if (!m_chainstate->m_blockman.ReadBlockUndo(block_undo, *pindex)) {
@ -448,18 +483,11 @@ bool CoinStatsIndex::ReverseBlock(const CBlock& block, const CBlockIndex* pindex
// Skip unspendable coins
if (coin.out.scriptPubKey.IsUnspendable()) {
m_total_unspendable_amount -= coin.out.nValue;
m_total_unspendables_scripts -= coin.out.nValue;
continue;
}
RemoveCoinHash(m_muhash, outpoint, coin);
if (tx->IsCoinBase()) {
m_total_coinbase_amount -= coin.out.nValue;
} else {
m_total_new_outputs_ex_coinbase_amount -= coin.out.nValue;
}
--m_transaction_output_count;
m_total_amount -= coin.out.nValue;
m_bogo_size -= GetBogoSize(coin.out.scriptPubKey);
@ -475,8 +503,6 @@ bool CoinStatsIndex::ReverseBlock(const CBlock& block, const CBlockIndex* pindex
ApplyCoinHash(m_muhash, outpoint, coin);
m_total_prevout_spent_amount -= coin.out.nValue;
m_transaction_output_count++;
m_total_amount += coin.out.nValue;
m_bogo_size += GetBogoSize(coin.out.scriptPubKey);
@ -484,27 +510,28 @@ bool CoinStatsIndex::ReverseBlock(const CBlock& block, const CBlockIndex* pindex
}
}
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_total_unspendable_amount -= 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 where possible, i.e. when total historical values are tracked.
// Otherwise just read the values from the index entry.
uint256 out;
m_muhash.Finalize(out);
Assert(read_out.second.muhash == out);
Assert(m_transaction_output_count == read_out.second.transaction_output_count);
Assert(m_total_amount == read_out.second.total_amount);
Assert(m_bogo_size == read_out.second.bogo_size);
Assert(m_total_subsidy == read_out.second.total_subsidy);
m_total_unspendable_amount -= m_block_unspendables_unclaimed_rewards;
Assert(m_total_unspendable_amount == read_out.second.total_unspendable_amount);
Assert(m_total_prevout_spent_amount == read_out.second.total_prevout_spent_amount);
Assert(m_total_new_outputs_ex_coinbase_amount == read_out.second.total_new_outputs_ex_coinbase_amount);
Assert(m_total_coinbase_amount == read_out.second.total_coinbase_amount);
Assert(m_total_unspendables_genesis_block == read_out.second.total_unspendables_genesis_block);
Assert(m_total_unspendables_bip30 == read_out.second.total_unspendables_bip30);
Assert(m_total_unspendables_scripts == read_out.second.total_unspendables_scripts);
Assert(m_total_unspendables_unclaimed_rewards == read_out.second.total_unspendables_unclaimed_rewards);
Assert(m_transaction_output_count == read_out.second.transaction_output_count);
Assert(m_bogo_size == read_out.second.bogo_size);
m_block_subsidy = read_out.second.block_subsidy;
m_block_prevout_spent_amount = read_out.second.block_prevout_spent_amount;
m_block_new_outputs_ex_coinbase_amount = read_out.second.block_new_outputs_ex_coinbase_amount;
m_block_coinbase_amount = read_out.second.block_coinbase_amount;
m_block_unspendables_genesis_block = read_out.second.block_unspendables_genesis_block;
m_block_unspendables_bip30 = read_out.second.block_unspendables_bip30;
m_block_unspendables_scripts = read_out.second.block_unspendables_scripts;
m_block_unspendables_unclaimed_rewards = read_out.second.block_unspendables_unclaimed_rewards;
return true;
}

View File

@ -28,19 +28,22 @@ private:
uint64_t m_transaction_output_count{0};
uint64_t m_bogo_size{0};
CAmount m_total_amount{0};
CAmount m_total_subsidy{0};
CAmount m_total_unspendable_amount{0};
CAmount m_total_prevout_spent_amount{0};
CAmount m_total_new_outputs_ex_coinbase_amount{0};
CAmount m_total_coinbase_amount{0};
CAmount m_total_unspendables_genesis_block{0};
CAmount m_total_unspendables_bip30{0};
CAmount m_total_unspendables_scripts{0};
CAmount m_total_unspendables_unclaimed_rewards{0};
CAmount m_block_subsidy{0};
CAmount m_block_prevout_spent_amount{0};
CAmount m_block_new_outputs_ex_coinbase_amount{0};
CAmount m_block_coinbase_amount{0};
CAmount m_block_unspendables_genesis_block{0};
CAmount m_block_unspendables_bip30{0};
CAmount m_block_unspendables_scripts{0};
CAmount m_block_unspendables_unclaimed_rewards{0};
[[nodiscard]] bool ReverseBlock(const CBlock& block, const CBlockIndex* pindex);
bool AllowPrune() const override { return true; }
uint32_t GetVersion() const override { return 1; }
protected:
bool CustomInit(const std::optional<interfaces::BlockRef>& block) override;

View File

@ -23,6 +23,7 @@ private:
const std::unique_ptr<DB> m_db;
bool AllowPrune() const override { return false; }
uint32_t GetVersion() const override { return 0; }
protected:
bool CustomAppend(const interfaces::BlockInfo& block) override;

View File

@ -48,24 +48,26 @@ struct CCoinsStats {
// Following values are only available from coinstats index
//! Total cumulative amount of block subsidies up to and including this block
CAmount total_subsidy{0};
//! Total cumulative amount of unspendable coins up to and including this block
//! Amount of unspendable coins in this block
CAmount total_unspendable_amount{0};
//! Total cumulative amount of prevouts spent up to and including this block
CAmount total_prevout_spent_amount{0};
//! Total cumulative amount of outputs created up to and including this block
CAmount total_new_outputs_ex_coinbase_amount{0};
//! Total cumulative amount of coinbase outputs up to and including this block
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};
//! Amount of block subsidies in this block
CAmount block_subsidy{0};
//! Amount of prevouts spent in this block
CAmount block_prevout_spent_amount{0};
//! Amount of outputs created in this block
CAmount block_new_outputs_ex_coinbase_amount{0};
//! Amount of coinbase outputs in this block
CAmount block_coinbase_amount{0};
//! The unspendable coinbase output amount from the genesis block
CAmount block_unspendables_genesis_block{0};
//! The unspendable coinbase output amounts caused by BIP30
CAmount block_unspendables_bip30{0};
//! Amount of outputs sent to unspendable scripts (OP_RETURN for example) in this block
CAmount block_unspendables_scripts{0};
//! Amount of coins lost due to unclaimed miner rewards in this block
CAmount block_unspendables_unclaimed_rewards{0};
CCoinsStats() = default;
CCoinsStats(int block_height, const uint256& block_hash);

View File

@ -1059,26 +1059,20 @@ static RPCHelpMan gettxoutsetinfo()
} else {
ret.pushKV("total_unspendable_amount", ValueFromAmount(stats.total_unspendable_amount));
CCoinsStats prev_stats{};
if (pindex->nHeight > 0) {
const std::optional<CCoinsStats> maybe_prev_stats = GetUTXOStats(coins_view, *blockman, hash_type, node.rpc_interruption_point, pindex->pprev, index_requested);
if (!maybe_prev_stats) {
throw JSONRPCError(RPC_INTERNAL_ERROR, "Unable to read UTXO set");
}
prev_stats = maybe_prev_stats.value();
}
UniValue block_info(UniValue::VOBJ);
block_info.pushKV("prevout_spent", ValueFromAmount(stats.total_prevout_spent_amount - prev_stats.total_prevout_spent_amount));
block_info.pushKV("coinbase", ValueFromAmount(stats.total_coinbase_amount - prev_stats.total_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.total_unspendable_amount - prev_stats.total_unspendable_amount));
block_info.pushKV("prevout_spent", ValueFromAmount(stats.block_prevout_spent_amount));
block_info.pushKV("coinbase", ValueFromAmount(stats.block_coinbase_amount));
block_info.pushKV("new_outputs_ex_coinbase", ValueFromAmount(stats.block_new_outputs_ex_coinbase_amount));
block_info.pushKV("unspendable", ValueFromAmount(stats.block_unspendables_genesis_block +
stats.block_unspendables_bip30 +
stats.block_unspendables_scripts +
stats.block_unspendables_unclaimed_rewards));
UniValue unspendables(UniValue::VOBJ);
unspendables.pushKV("genesis_block", ValueFromAmount(stats.total_unspendables_genesis_block - prev_stats.total_unspendables_genesis_block));
unspendables.pushKV("bip30", ValueFromAmount(stats.total_unspendables_bip30 - prev_stats.total_unspendables_bip30));
unspendables.pushKV("scripts", ValueFromAmount(stats.total_unspendables_scripts - prev_stats.total_unspendables_scripts));
unspendables.pushKV("unclaimed_rewards", ValueFromAmount(stats.total_unspendables_unclaimed_rewards - prev_stats.total_unspendables_unclaimed_rewards));
unspendables.pushKV("genesis_block", ValueFromAmount(stats.block_unspendables_genesis_block));
unspendables.pushKV("bip30", ValueFromAmount(stats.block_unspendables_bip30));
unspendables.pushKV("scripts", ValueFromAmount(stats.block_unspendables_scripts));
unspendables.pushKV("unclaimed_rewards", ValueFromAmount(stats.block_unspendables_unclaimed_rewards));
block_info.pushKV("unspendables", std::move(unspendables));
ret.pushKV("block_info", std::move(block_info));

Binary file not shown.

View File

@ -0,0 +1 @@
MANIFEST-000002

Binary file not shown.

View File

@ -10,6 +10,9 @@ the index.
"""
from decimal import Decimal
import os
from pathlib import Path
import shutil
from test_framework.blocktools import (
COINBASE_MATURITY,
@ -53,6 +56,7 @@ class CoinStatsIndexTest(BitcoinTestFramework):
self._test_reorg_index()
self._test_index_rejects_hash_serialized()
self._test_init_index_after_reorg()
self._test_outdated_index_version()
def block_sanity_check(self, block_info):
block_subsidy = 50
@ -322,6 +326,18 @@ class CoinStatsIndexTest(BitcoinTestFramework):
res1 = index_node.gettxoutsetinfo(hash_type='muhash', hash_or_height=None, use_index=True)
assert_equal(res["muhash"], res1["muhash"])
def _test_outdated_index_version(self):
self.log.info("Test a node doesn't start with an outdated index version")
index_node = self.nodes[1]
self.stop_node(1)
index_db = index_node.chain_path / 'indexes' / 'coinstats' / 'db'
shutil.rmtree(index_db)
v0_index_db = Path(os.path.dirname(os.path.realpath(__file__))) / 'data' / 'coinstatsindex_v0'
shutil.copytree(v0_index_db, index_db)
msg = "[error] coinstatsindex version mismatch: expected 1 but 0 was found. In order to rebuild the index, remove the indexes/coinstats directory in your datadir"
with index_node.assert_debug_log(expected_msgs=[msg]):
index_node.assert_start_raises_init_error(extra_args=["-coinstatsindex"])
if __name__ == '__main__':
CoinStatsIndexTest(__file__).main()