We need this method because of the specialized database access which doesn't need to deserialize the read value:
Otherwise we could of course replace it with !!GetCoin(outpoint):
We're not even really using the former in production code anyway (see #34132), we could probably easily inline the !!GetCoin(outpoint) to the few remaining call sites instead. Regardless, the current change should simplify usage and we can investigate that later.
Also, the underlying problem is that having a separate CCoinsView makes adding defaults weird, there's no logical default behavior.
But we could inline it into CCoinsViewBacked (and delete CCoinsView and rename CCoinsViewBacked back to CCoinsView) and use that as the base for the other caches, with the default implementation simply delegating to base, something like:
diff --git a/src/coins.cpp b/src/coins.cpp
index 9bdcb42f45..4f66ed447b 100644
--- a/src/coins.cpp
+++ b/src/coins.cpp
@@ -14,7 +14,7 @@ TRACEPOINT_SEMAPHORE(utxocache, spent);
TRACEPOINT_SEMAPHORE(utxocache, uncache);
CCoinsViewCache::CCoinsViewCache(CCoinsView* baseIn, bool deterministic) :
- CCoinsViewBacked(baseIn), m_deterministic(deterministic),
+ CCoinsView(baseIn), m_deterministic(deterministic),
cacheCoins(0, SaltedOutpointHasher(/*deterministic=*/deterministic), CCoinsMap::key_equal{}, &m_cache_coins_memory_resource)
{
m_sentinel.second.SelfRef(m_sentinel);
@@ -362,10 +362,10 @@ static ReturnType ExecuteBackedWrapper(Func func, const std::vector<std::functio
std::optional<Coin> CCoinsViewErrorCatcher::GetCoin(const COutPoint& outpoint) const
{
- return ExecuteBackedWrapper<std::optional<Coin>>([&]() { return CCoinsViewBacked::GetCoin(outpoint); }, m_err_callbacks);
+ return ExecuteBackedWrapper<std::optional<Coin>>([&]() { return CCoinsView::GetCoin(outpoint); }, m_err_callbacks);
}
bool CCoinsViewErrorCatcher::HaveCoin(const COutPoint& outpoint) const
{
- return ExecuteBackedWrapper<bool>([&]() { return CCoinsViewBacked::HaveCoin(outpoint); }, m_err_callbacks);
+ return ExecuteBackedWrapper<bool>([&]() { return CCoinsView::HaveCoin(outpoint); }, m_err_callbacks);
}
diff --git a/src/coins.h b/src/coins.h
index a64f30376d..7e8120f710 100644
--- a/src/coins.h
+++ b/src/coins.h
@@ -303,37 +303,46 @@ private:
bool m_will_erase;
};
-/** Pure abstract view on the open txout dataset. */
+/** Coins view backed by another CCoinsView. */
class CCoinsView
{
+protected:
+ CCoinsView* base{nullptr};
+
+ CCoinsView() = default;
+
public:
+ explicit CCoinsView(CCoinsView* viewIn) : base(Assert(viewIn)) {}
+
//! As we use CCoinsViews polymorphically, have a virtual destructor
virtual ~CCoinsView() = default;
+ void SetBackend(CCoinsView& viewIn) { base = &viewIn; }
+
//! Retrieve the Coin (unspent transaction output) for a given outpoint.
- virtual std::optional<Coin> GetCoin(const COutPoint& outpoint) const = 0;
+ virtual std::optional<Coin> GetCoin(const COutPoint& outpoint) const { return base->GetCoin(outpoint); }
//! Just check whether a given outpoint is unspent.
- virtual bool HaveCoin(const COutPoint& outpoint) const = 0;
+ virtual bool HaveCoin(const COutPoint& outpoint) const { return base->HaveCoin(outpoint); }
//! Retrieve the block hash whose state this CCoinsView currently represents
- virtual uint256 GetBestBlock() const = 0;
+ virtual uint256 GetBestBlock() const { return base->GetBestBlock(); }
//! 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 = 0;
+ virtual std::vector<uint256> GetHeadBlocks() const { return base->GetHeadBlocks(); }
//! Do a bulk modification (multiple Coin changes + BestBlock change).
//! The passed cursor is used to iterate through the coins.
- virtual bool BatchWrite(CoinsViewCacheCursor& cursor, const uint256& hashBlock) = 0;
+ virtual bool BatchWrite(CoinsViewCacheCursor& cursor, const uint256& hashBlock) { return base->BatchWrite(cursor, hashBlock); }
//! Get a cursor to iterate over the whole state
- virtual std::unique_ptr<CCoinsViewCursor> Cursor() const = 0;
+ virtual std::unique_ptr<CCoinsViewCursor> Cursor() const { return base->Cursor(); }
//! Estimate database size
- virtual size_t EstimateSize() const = 0;
+ virtual size_t EstimateSize() const { return base->EstimateSize(); }
};
/** Noop coins view. */
@@ -361,28 +370,8 @@ public:
size_t EstimateSize() const override { return 0; }
};
-/** CCoinsView backed by another CCoinsView */
-class CCoinsViewBacked : public CCoinsView
-{
-protected:
- CCoinsView* base;
-
-public:
- explicit CCoinsViewBacked(CCoinsView* viewIn) : base{viewIn} {}
-
- void SetBackend(CCoinsView& viewIn) { base = &viewIn; }
-
- std::optional<Coin> GetCoin(const COutPoint& outpoint) const override { return base->GetCoin(outpoint); }
- bool HaveCoin(const COutPoint& outpoint) const override { return base->HaveCoin(outpoint); }
- uint256 GetBestBlock() const override { return base->GetBestBlock(); }
- std::vector<uint256> GetHeadBlocks() const override { return base->GetHeadBlocks(); }
- bool BatchWrite(CoinsViewCacheCursor& cursor, const uint256& hashBlock) override { return base->BatchWrite(cursor, hashBlock); }
- std::unique_ptr<CCoinsViewCursor> Cursor() const override { return base->Cursor(); }
- size_t EstimateSize() const override { return base->EstimateSize(); }
-};
-
/** CCoinsView that adds a memory cache for transactions to another CCoinsView */
-class CCoinsViewCache : public CCoinsViewBacked
+class CCoinsViewCache : public CCoinsView
{
private:
const bool m_deterministic;
@@ -534,10 +523,10 @@ const Coin& AccessByTxid(const CCoinsViewCache& cache, const Txid& txid);
*
* Writes do not need similar protection, as failure to write is handled by the caller.
*/
-class CCoinsViewErrorCatcher final : public CCoinsViewBacked
+class CCoinsViewErrorCatcher final : public CCoinsView
{
public:
- explicit CCoinsViewErrorCatcher(CCoinsView* view) : CCoinsViewBacked(view) {}
+ explicit CCoinsViewErrorCatcher(CCoinsView* view) : CCoinsView(view) {}
void AddReadErrCallback(std::function<void()> f) {
m_err_callbacks.emplace_back(std::move(f));
diff --git a/src/txmempool.cpp b/src/txmempool.cpp
index 4dc692bfd8..3e47428e8e 100644
--- a/src/txmempool.cpp
+++ b/src/txmempool.cpp
@@ -720,7 +720,7 @@ bool CTxMemPool::HasNoInputsOf(const CTransaction &tx) const
return true;
}
-CCoinsViewMemPool::CCoinsViewMemPool(CCoinsView* baseIn, const CTxMemPool& mempoolIn) : CCoinsViewBacked(baseIn), mempool(mempoolIn) { }
+CCoinsViewMemPool::CCoinsViewMemPool(CCoinsView* baseIn, const CTxMemPool& mempoolIn) : CCoinsView(baseIn), mempool(mempoolIn) { }
std::optional<Coin> CCoinsViewMemPool::GetCoin(const COutPoint& outpoint) const
{
diff --git a/src/txmempool.h b/src/txmempool.h
index 166a024823..4c24766c77 100644
--- a/src/txmempool.h
+++ b/src/txmempool.h
@@ -778,7 +778,7 @@ public:
* signrawtransactionwithkey and signrawtransactionwithwallet,
* as long as the conflicting transaction is not yet confirmed.
*/
-class CCoinsViewMemPool : public CCoinsViewBacked
+class CCoinsViewMemPool : public CCoinsView
{
/**
* Coins made available by transactions being validated. Tracking these allows for package
This would introduce a sensible default as the base class behavior (always delegating) and we'd instantiate it with the empty sigleton, which would always do nothin'.
I haven't included this yet, we can discuss if it's a good idea.