Repository: bitcoin/bitcoin
File: src/test/fuzz/coinscache_sim.cpp (C++)

Code before:
    CacheEntry entry[NUM_OUTPOINTS];

    void Wipe() {
        for (uint32_t i = 0; i < NUM_OUTPOINTS; ++i) {
            entry[i].entrytype = EntryType::NONE;
        }
    }
};

/** Class for the base of the hierarchy (roughly simulating a memory-backed CCoinsViewDB).
 *
 * The initial state consists of the empty UTXO set, though coins whose output index
 * is 3 (mod 5) always have GetCoin() succeed (but returning an IsSpent() coin unless a UTXO
 * exists). Coins whose output index is 4 (mod 5) have GetCoin() always succeed after being spent.
 * This exercises code paths with spent, non-DIRTY cache entries.
 */
class CoinsViewBottom final : public CCoinsView
{
    std::map<COutPoint, Coin> m_data;

public:
    bool GetCoin(const COutPoint& outpoint, Coin& coin) const final
    {
        auto it = m_data.find(outpoint);
        if (it == m_data.end()) {
            return false;
        } else {
            coin = it->second;
            return true;
        }
    }

    bool HaveCoin(const COutPoint& outpoint) const final
    {
        return m_data.count(outpoint);
    }

    uint256 GetBestBlock() const final { return {}; }
    std::vector<uint256> GetHeadBlocks() const final { return {}; }
    std::unique_ptr<CCoinsViewCursor> Cursor() const final { return {}; }
    size_t EstimateSize() const final { return m_data.size(); }

    bool BatchWrite(CoinsViewCacheCursor& cursor, const uint256&) final
    {
        for (auto it{cursor.Begin()}; it != cursor.End(); it = cursor.NextAndMaybeErase(*it)) {
            if (it->second.IsDirty()) {
                if (it->second.coin.IsSpent() && (it->first.n % 5) != 4) {
                    m_data.erase(it->first);
                } else if (cursor.WillErase(*it)) {
                    m_data[it->first] = std::move(it->second.coin);
                } else {

Review comment (refactor):
In commit 6243ceb65b83ea8dc3a689fdcc868a3a94b86e80 ` refactor: Remove unrealistic simulation state `

The comment above should be removed in this commit as well, since it is no longer accurate.

Code after:
    CacheEntry entry[NUM_OUTPOINTS];

    void Wipe() {
        for (uint32_t i = 0; i < NUM_OUTPOINTS; ++i) {
            entry[i].entrytype = EntryType::NONE;
        }
    }
};

/** Class for the base of the hierarchy (roughly simulating a memory-backed CCoinsViewDB).
 *
 * The initial state consists of the empty UTXO set.
 * Coins whose output index is 4 (mod 5) have GetCoin() always succeed after being spent.
 * This exercises code paths with spent, non-DIRTY cache entries.
 */
class CoinsViewBottom final : public CCoinsView
{
    std::map<COutPoint, Coin> m_data;

public:
    std::optional<Coin> GetCoin(const COutPoint& outpoint) const final
    {
        // TODO GetCoin shouldn't return spent coins
        if (auto it = m_data.find(outpoint); it != m_data.end()) return it->second;
        return std::nullopt;
    }

    bool HaveCoin(const COutPoint& outpoint) const final
    {
        return m_data.count(outpoint);
    }

    uint256 GetBestBlock() const final { return {}; }
    std::vector<uint256> GetHeadBlocks() const final { return {}; }
    std::unique_ptr<CCoinsViewCursor> Cursor() const final { return {}; }
    size_t EstimateSize() const final { return m_data.size(); }

    bool BatchWrite(CoinsViewCacheCursor& cursor, const uint256&) final
    {
        for (auto it{cursor.Begin()}; it != cursor.End(); it = cursor.NextAndMaybeErase(*it)) {
            if (it->second.IsDirty()) {
                if (it->second.coin.IsSpent() && (it->first.n % 5) != 4) {
                    m_data.erase(it->first);
                } else if (cursor.WillErase(*it)) {
                    m_data[it->first] = std::move(it->second.coin);
                } else {
                    m_data[it->first] = it->second.coin;
                }
            } else {
                /* For non-dirty entries being written, compare them with what we have. */
                auto it2 = m_data.find(it->first);