Repository: bitcoin/bitcoin
File: src/index/coinstatsindex.cpp (C++)

Code before:
        READWRITE(obj.block_hash);
    }
};

}; // namespace

std::unique_ptr<CoinStatsIndex> g_coin_stats_index;

CoinStatsIndex::CoinStatsIndex(std::unique_ptr<interfaces::Chain> chain, size_t n_cache_size, bool f_memory, bool f_wipe)
    : BaseIndex(std::move(chain), "coinstatsindex")
{
    fs::path path{gArgs.GetDataDirNet() / "indexes" / "coinstats"};
    fs::create_directories(path);

    m_db = std::make_unique<CoinStatsIndex::DB>(path / "db", n_cache_size, f_memory, f_wipe);
}

bool CoinStatsIndex::CustomAppend(const interfaces::BlockInfo& block)
{
    CBlockUndo block_undo;
    const CAmount block_subsidy{GetBlockSubsidy(block.height, Params().GetConsensus())};
    m_total_subsidy += block_subsidy;

    // Ignore genesis block
    if (block.height > 0) {
        block_undo = *Assert(block.undo_data);

        std::pair<uint256, DBVal> read_out;
        if (!m_db->Read(DBHeightKey(block.height - 1), read_out)) {
            return false;
        }

        uint256 expected_block_hash{*Assert(block.prev_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)) {
                LogError("%s: previous block header not found; expected %s\n",
                             __func__, expected_block_hash.ToString());
                return false;
            }
        }

        // Add the new utxos created from the block
        assert(block.data);
        for (size_t i = 0; i < block.data->vtx.size(); ++i) {
            const auto& tx{block.data->vtx.at(i)};

            // Skip duplicate txid coinbase transactions (BIP30).
            if (IsBIP30Unspendable(block.hash, block.height) && tx->IsCoinBase()) {

Review comment (performance):
In commit "index: remove CBlockIndex access from CustomAppend()" (3f390efa313f4ac43e9134d5299d7faec74cca60)

It's a bit inefficient to copy the undo data here, and the code seems overcomplicated because the `block_undo` variable is only referenced once, 60 lines below. Would suggest dropping the `block_undo` variable and just accessing `block.undo_data` directly below.

The `block_undo` variable in BlockFilterIndex::CustomAppend could also go away to simplify that function.

Code after:
        READWRITE(obj.block_hash);
    }
};

}; // namespace

std::unique_ptr<CoinStatsIndex> g_coin_stats_index;

CoinStatsIndex::CoinStatsIndex(std::unique_ptr<interfaces::Chain> chain, size_t n_cache_size, bool f_memory, bool f_wipe)
    : BaseIndex(std::move(chain), "coinstatsindex")
{
    fs::path path{gArgs.GetDataDirNet() / "indexes" / "coinstats"};
    fs::create_directories(path);

    m_db = std::make_unique<CoinStatsIndex::DB>(path / "db", n_cache_size, f_memory, f_wipe);
}

bool CoinStatsIndex::CustomAppend(const interfaces::BlockInfo& block)
{
    const CAmount block_subsidy{GetBlockSubsidy(block.height, Params().GetConsensus())};
    m_total_subsidy += block_subsidy;

    // Ignore genesis block
    if (block.height > 0) {
        std::pair<uint256, DBVal> read_out;
        if (!m_db->Read(DBHeightKey(block.height - 1), read_out)) {
            return false;
        }

        uint256 expected_block_hash{*Assert(block.prev_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)) {
                LogError("%s: previous block header not found; expected %s\n",
                             __func__, expected_block_hash.ToString());
                return false;
            }
        }

        // Add the new utxos created from the block
        assert(block.data);
        for (size_t i = 0; i < block.data->vtx.size(); ++i) {
            const auto& tx{block.data->vtx.at(i)};

            // Skip duplicate txid coinbase transactions (BIP30).
            if (IsBIP30Unspendable(block.hash, block.height) && tx->IsCoinBase()) {
                m_total_unspendable_amount += block_subsidy;
                m_total_unspendables_bip30 += block_subsidy;
                continue;