coins: make CCoinsView methods pure virtual

`CCoinsView` provided default no-op implementations, which allowed constructing a bare view and silently getting dummy behavior.
Make all interface methods pure virtual and remove the legacy default definitions from `coins.cpp` so callers must choose an explicit implementation.
Move the virtual destructor to the beginning to avoid mixing it between the methods.
No-op backing behavior remains available via `CoinsViewEmpty`.
This commit is contained in:
Lőrinc
2026-02-20 14:19:58 +01:00
parent b637566c8d
commit 86296f276d
6 changed files with 34 additions and 32 deletions

View File

@@ -14,22 +14,6 @@ TRACEPOINT_SEMAPHORE(utxocache, add);
TRACEPOINT_SEMAPHORE(utxocache, spent);
TRACEPOINT_SEMAPHORE(utxocache, uncache);
std::optional<Coin> CCoinsView::GetCoin(const COutPoint& outpoint) const { return std::nullopt; }
std::optional<Coin> CCoinsView::PeekCoin(const COutPoint& outpoint) const { return GetCoin(outpoint); }
uint256 CCoinsView::GetBestBlock() const { return uint256(); }
std::vector<uint256> CCoinsView::GetHeadBlocks() const { return std::vector<uint256>(); }
void CCoinsView::BatchWrite(CoinsViewCacheCursor& cursor, const uint256& block_hash)
{
for (auto it{cursor.Begin()}; it != cursor.End(); it = cursor.NextAndMaybeErase(*it)) { }
}
std::unique_ptr<CCoinsViewCursor> CCoinsView::Cursor() const { return nullptr; }
bool CCoinsView::HaveCoin(const COutPoint& outpoint) const
{
return GetCoin(outpoint).has_value();
}
CCoinsViewBacked::CCoinsViewBacked(CCoinsView* in_view) : base(in_view) { }
std::optional<Coin> CCoinsViewBacked::GetCoin(const COutPoint& outpoint) const { return base->GetCoin(outpoint); }
std::optional<Coin> CCoinsViewBacked::PeekCoin(const COutPoint& outpoint) const { return base->PeekCoin(outpoint); }

View File

@@ -303,43 +303,43 @@ private:
bool m_will_erase;
};
/** Abstract view on the open txout dataset. */
/** Pure abstract view on the open txout dataset. */
class CCoinsView
{
public:
//! As we use CCoinsViews polymorphically, have a virtual destructor
virtual ~CCoinsView() = default;
//! Retrieve the Coin (unspent transaction output) for a given outpoint.
//! May populate the cache. Use PeekCoin() to perform a non-caching lookup.
virtual std::optional<Coin> GetCoin(const COutPoint& outpoint) const;
virtual std::optional<Coin> GetCoin(const COutPoint& outpoint) const = 0;
//! Retrieve the Coin (unspent transaction output) for a given outpoint, without caching results.
//! Does not populate the cache. Use GetCoin() to cache the result.
virtual std::optional<Coin> PeekCoin(const COutPoint& outpoint) const;
virtual std::optional<Coin> PeekCoin(const COutPoint& outpoint) const = 0;
//! Just check whether a given outpoint is unspent.
//! May populate the cache. Use PeekCoin() to perform a non-caching lookup.
virtual bool HaveCoin(const COutPoint& outpoint) const;
virtual bool HaveCoin(const COutPoint& outpoint) const = 0;
//! Retrieve the block hash whose state this CCoinsView currently represents
virtual uint256 GetBestBlock() const;
virtual uint256 GetBestBlock() const = 0;
//! Retrieve the range of blocks that may have been only partially written.
//! If the database is in a consistent state, the result is the empty vector.
//! Otherwise, a two-element vector is returned consisting of the new and
//! the old block hash, in that order.
virtual std::vector<uint256> GetHeadBlocks() const;
virtual std::vector<uint256> GetHeadBlocks() const = 0;
//! Do a bulk modification (multiple Coin changes + BestBlock change).
//! The passed cursor is used to iterate through the coins.
virtual void BatchWrite(CoinsViewCacheCursor& cursor, const uint256& block_hash);
virtual void BatchWrite(CoinsViewCacheCursor& cursor, const uint256& block_hash) = 0;
//! Get a cursor to iterate over the whole state
virtual std::unique_ptr<CCoinsViewCursor> Cursor() const;
//! Get a cursor to iterate over the whole state. Implementations may return nullptr.
virtual std::unique_ptr<CCoinsViewCursor> Cursor() const = 0;
//! As we use CCoinsViews polymorphically, have a virtual destructor
virtual ~CCoinsView() = default;
//! Estimate database size (0 if not implemented)
virtual size_t EstimateSize() const { return 0; }
//! Estimate database size
virtual size_t EstimateSize() const = 0;
};
/** Noop coins view. */
@@ -353,6 +353,18 @@ public:
CoinsViewEmpty(const CoinsViewEmpty&) = delete;
CoinsViewEmpty& operator=(const CoinsViewEmpty&) = delete;
std::optional<Coin> GetCoin(const COutPoint&) const override { return {}; }
std::optional<Coin> PeekCoin(const COutPoint& outpoint) const override { return GetCoin(outpoint); }
bool HaveCoin(const COutPoint& outpoint) const override { return !!GetCoin(outpoint); }
uint256 GetBestBlock() const override { return {}; }
std::vector<uint256> GetHeadBlocks() const override { return {}; }
void BatchWrite(CoinsViewCacheCursor& cursor, const uint256&) override
{
for (auto it{cursor.Begin()}; it != cursor.End(); it = cursor.NextAndMaybeErase(*it)) { }
}
std::unique_ptr<CCoinsViewCursor> Cursor() const override { return {}; }
size_t EstimateSize() const override { return 0; }
};
/** CCoinsView backed by another CCoinsView */

View File

@@ -247,7 +247,7 @@ FUZZ_TARGET(coinscache_sim)
CallOneOf(
provider,
[&]() { // GetCoin
[&]() { // PeekCoin/GetCoin
uint32_t outpointidx = provider.ConsumeIntegralInRange<uint32_t>(0, NUM_OUTPOINTS - 1);
// Look up in simulation data.
auto sim = lookup(outpointidx);

View File

@@ -78,6 +78,11 @@ std::optional<Coin> CCoinsViewDB::GetCoin(const COutPoint& outpoint) const
return std::nullopt;
}
std::optional<Coin> CCoinsViewDB::PeekCoin(const COutPoint& outpoint) const
{
return GetCoin(outpoint);
}
bool CCoinsViewDB::HaveCoin(const COutPoint& outpoint) const
{
return m_db->Exists(CoinEntry(&outpoint));

View File

@@ -41,6 +41,7 @@ public:
explicit CCoinsViewDB(DBParams db_params, CoinsViewOptions options);
std::optional<Coin> GetCoin(const COutPoint& outpoint) const override;
std::optional<Coin> PeekCoin(const COutPoint& outpoint) const override;
bool HaveCoin(const COutPoint& outpoint) const override;
uint256 GetBestBlock() const override;
std::vector<uint256> GetHeadBlocks() const override;

View File

@@ -768,7 +768,7 @@ protected:
public:
CCoinsViewMemPool(CCoinsView* baseIn, const CTxMemPool& mempoolIn);
/** GetCoin, returning whether it exists and is not spent. Also updates m_non_base_coins if the
* coin is not fetched from base. */
* coin is not fetched from base. May populate the base view on cache misses. */
std::optional<Coin> GetCoin(const COutPoint& outpoint) const override;
/** Add the coins created by this transaction. These coins are only temporarily stored in
* m_temp_added and cannot be flushed to the back end. Only used for package validation. */