Merge bitcoin/bitcoin#34124: validation: make CCoinsView a pure virtual interface

8783cc8056 refactor: inline `CCoinsViewBacked` implementation (Lőrinc)
86296f276d coins: make `CCoinsView` methods pure virtual (Lőrinc)
b637566c8d coins: add explicit `CoinsViewEmpty` noop backend (Lőrinc)
90c635c01c fuzz: keep backend assertions aligned to active backend (Lőrinc)
a9f92e3497 refactor: normalize CCoinsView whitespace and signatures (Lőrinc)
38a99f3344 scripted-diff: normalize `CCoinsView` naming (Lőrinc)
06172ef0d5 refactor: rename `hashBlock` to `m_block_hash` to avoid shadowing (Lőrinc)

Pull request description:

  ### Problem
  `CCoinsView` is the coins view interface, but historically it also provided built-in no-op behavior:

  * It provided default no-op implementations (returning `std::nullopt`, `uint256()`, `false`, or `nullptr`) instead of being pure virtual.
  * Callers could instantiate a bare `CCoinsView` and get silent no-op behavior.
  * Mixing the interface definition with a built-in dummy implementation blurred the abstraction boundary.

  ### Context
  This is part of the ongoing coins caching cleanup in #34280.

  ### Fix
  This PR separates the interface from no-op behavior and makes `CCoinsView` pure virtual in incremental steps:
  * Add `CoinsViewEmpty` as an explicit no-op coins view for tests, benchmarks, and temporary backends.
  * Replace direct bare-`CCoinsView` test and dummy instantiations with `CoinsViewEmpty`.
  * Make all `CCoinsView` methods pure virtual (`PeekCoin`, `GetCoin`, `HaveCoin`, `GetBestBlock`, `GetHeadBlocks`, `BatchWrite`, `Cursor`, `EstimateSize`).
  * Remove the legacy default implementations from `coins.cpp`.
  * Update fuzz and dummy backends to use `CoinsViewEmpty` explicitly.

ACKs for top commit:
  w0xlt:
    reACK 8783cc8056
  ryanofsky:
    Code review ACK 8783cc8056. Just updated comments and variable name since last review. The fuzz test code is much clearer now IMO
  andrewtoth-exo:
    ACK 8783cc8056
  ajtowns:
    ACK 8783cc8056

Tree-SHA512: cfc831578aa309788c1b5dafbfecca3de388cc4215534c3f3df24f90d7770ed37b1fd7aa134df91d611d0a1ca75929accb98d5ed7df7b52851c259e04f08e4a3
This commit is contained in:
Ryan Ofsky
2026-04-13 08:18:30 -04:00
18 changed files with 158 additions and 166 deletions

View File

@@ -437,7 +437,7 @@ class MemPoolAccept
public:
explicit MemPoolAccept(CTxMemPool& mempool, Chainstate& active_chainstate) :
m_pool(mempool),
m_view(&m_dummy),
m_view(&CoinsViewEmpty::Get()),
m_viewmempool(&active_chainstate.CoinsTip(), m_pool),
m_active_chainstate(active_chainstate)
{
@@ -737,10 +737,6 @@ private:
/** When m_view is connected to m_viewmempool as its backend, it can pull coins from the mempool and from the UTXO
* set. This is also where temporary coins are stored. */
CCoinsViewMemPool m_viewmempool;
/** When m_view is connected to m_dummy, it can no longer look up coins from the mempool or UTXO set (meaning no disk
* operations happen), but can still return coins it accessed previously. Useful for keeping track of which coins
* were pulled from disk. */
CCoinsView m_dummy;
Chainstate& m_active_chainstate;
@@ -867,14 +863,14 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
}
}
// This is const, but calls into the back end CoinsViews. The CCoinsViewDB at the bottom of the
// hierarchy brings the best block into scope. See CCoinsViewDB::GetBestBlock().
m_view.GetBestBlock();
// This is const, but calls into `CCoinsViewCache::GetBestBlock()` to refresh
// the cached best block through `m_viewmempool` after caching inputs.
(void)m_view.GetBestBlock();
// we have all inputs cached now, so switch back to dummy (to protect
// against bugs where we pull more inputs from disk that miss being added
// to coins_to_uncache)
m_view.SetBackend(m_dummy);
// All required inputs are cached now, so switch m_view to the empty backend.
// This keeps already-fetched cache entries for later checks and prevents new
// backend lookups (which would avoid coins_to_uncache tracking).
m_view.SetBackend(CoinsViewEmpty::Get());
assert(m_active_chainstate.m_blockman.LookupBlockIndex(m_view.GetBestBlock()) == m_active_chainstate.m_chain.Tip());