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

Code before:
        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")
{
    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) {
            LogWarning("previous block header belongs to unexpected block %s; expected %s",
                      read_out.first.ToString(), expected_block_hash.ToString());

            if (!m_db->Read(DBHashKey(expected_block_hash), read_out)) {
                LogError("previous block header not found; expected %s",
                          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)};


Review comment (refactor):
In commit https://github.com/bitcoin/bitcoin/pull/30469/commits/e7b111a63067cf6c66140228c28ab1fa96f7ff18 (_refactor, index: Remove member variables in coinstatsindex_):

for other reviewers: `read_out.second.total_subidy` is updated below:

https://github.com/bitcoin/bitcoin/blob/e7b111a63067cf6c66140228c28ab1fa96f7ff18/src/index/coinstatsindex.cpp#L200

Code after:
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);

    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());