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

Code before:
        ser_writedata32be(s, height);
    }

    template <typename Stream>
    void Unserialize(Stream& s)
    {
        const uint8_t prefix{ser_readdata8(s)};
        if (prefix != DB_BLOCK_HEIGHT) {
            throw std::ios_base::failure("Invalid format for coinstatsindex DB height key");
        }
        height = ser_readdata32be(s);
    }
};

struct DBHashKey {
    uint256 block_hash;

    explicit DBHashKey(const uint256& hash_in) : block_hash(hash_in) {}

    SERIALIZE_METHODS(DBHashKey, obj)
    {
        uint8_t prefix{DB_BLOCK_HASH};
        READWRITE(prefix);
        if (prefix != DB_BLOCK_HASH) {
            throw std::ios_base::failure("Invalid format for coinstatsindex DB hash key");
        }

        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")
{
    // An earlier version of the index used "indexes/coinstats" but it contained
    // a bug and is superseded by a fixed version at "indexes/coinstatsindex".
    // The original index is kept around until the next release in case users
    // decide to downgrade their node.
    auto old_path = gArgs.GetDataDirNet() / "indexes" / "coinstats";
    if (fs::exists(old_path)) {
        // TODO: Change this to deleting the old index with v31.
        LogWarning("Old version of coinstatsindex found at %s. This folder can be safely deleted unless you " \
            "plan to downgrade your node to version 29 or lower.", fs::PathToString(old_path));
    }
    fs::path path{gArgs.GetDataDirNet() / "indexes" / "coinstatsindex"};
    fs::create_directories(path);


Review comment (suggestion):
It's not true that they aren't used for anything. The point of reading the block from disk is to check if it's the prev block that is expected because the values that are written for the current block depend on the prev block values. They are used not from the prev block but from the members instead though. I am not sure right now if this check can just be removed, but if they are still needed the expected prev block hash could probably be written into a member too and the DB read could be avoided this way.

Code after:
    if (fs::exists(old_path)) {
        // TODO: Change this to deleting the old index with v31.
        LogWarning("Old version of coinstatsindex found at %s. This folder can be safely deleted unless you " \
            "plan to downgrade your node to version 29 or lower.", fs::PathToString(old_path));
    }
    fs::path path{gArgs.GetDataDirNet() / "indexes" / "coinstatsindex"};
    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) {
        uint256 expected_block_hash{*Assert(block.prev_hash)};
        if (m_current_block_hash != expected_block_hash) {
            LogError("previous block header belongs to unexpected block %s; expected %s",
                      m_current_block_hash.ToString(), 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)};
            const bool is_coinbase{tx->IsCoinBase()};

            // Skip duplicate txid coinbase transactions (BIP30).
            if (is_coinbase && IsBIP30Unspendable(block.hash, block.height)) {
                m_total_unspendables_bip30 += block_subsidy;
                continue;
            }

            for (uint32_t j = 0; j < tx->vout.size(); ++j) {
                const CTxOut& out{tx->vout[j]};
                const Coin coin{out, block.height, is_coinbase};
                const COutPoint outpoint{tx->GetHash(), j};

                // Skip unspendable coins
                if (coin.out.scriptPubKey.IsUnspendable()) {
                    m_total_unspendables_scripts += coin.out.nValue;
                    continue;
                }

                ApplyCoinHash(m_muhash, outpoint, coin);

                if (is_coinbase) {