mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-03-02 09:46:14 +00:00
coins: add PeekCoin()
Introduce a helper to look up a Coin through a stack of CCoinsViewCache layers without populating parent caches. This is useful for ephemeral views (e.g. during ConnectBlock) that want to avoid polluting CoinsTip() when validating invalid blocks. Co-authored-by: l0rinc <pap.lorinc@gmail.com> Co-authored-by: Pieter Wuille <pieter@wuille.net> Co-authored-by: Ryan Ofsky <ryan@ofsky.org>
This commit is contained in:
parent
6750744eb3
commit
69b01af0eb
@ -15,6 +15,7 @@ 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& hashBlock)
|
||||
@ -31,6 +32,7 @@ bool CCoinsView::HaveCoin(const COutPoint &outpoint) const
|
||||
|
||||
CCoinsViewBacked::CCoinsViewBacked(CCoinsView *viewIn) : base(viewIn) { }
|
||||
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); }
|
||||
bool CCoinsViewBacked::HaveCoin(const COutPoint &outpoint) const { return base->HaveCoin(outpoint); }
|
||||
uint256 CCoinsViewBacked::GetBestBlock() const { return base->GetBestBlock(); }
|
||||
std::vector<uint256> CCoinsViewBacked::GetHeadBlocks() const { return base->GetHeadBlocks(); }
|
||||
@ -39,6 +41,14 @@ void CCoinsViewBacked::BatchWrite(CoinsViewCacheCursor& cursor, const uint256& h
|
||||
std::unique_ptr<CCoinsViewCursor> CCoinsViewBacked::Cursor() const { return base->Cursor(); }
|
||||
size_t CCoinsViewBacked::EstimateSize() const { return base->EstimateSize(); }
|
||||
|
||||
std::optional<Coin> CCoinsViewCache::PeekCoin(const COutPoint& outpoint) const
|
||||
{
|
||||
if (auto it{cacheCoins.find(outpoint)}; it != cacheCoins.end()) {
|
||||
return it->second.coin.IsSpent() ? std::nullopt : std::optional{it->second.coin};
|
||||
}
|
||||
return base->PeekCoin(outpoint);
|
||||
}
|
||||
|
||||
CCoinsViewCache::CCoinsViewCache(CCoinsView* baseIn, bool deterministic) :
|
||||
CCoinsViewBacked(baseIn), m_deterministic(deterministic),
|
||||
cacheCoins(0, SaltedOutpointHasher(/*deterministic=*/deterministic), CCoinsMap::key_equal{}, &m_cache_coins_memory_resource)
|
||||
@ -393,3 +403,8 @@ bool CCoinsViewErrorCatcher::HaveCoin(const COutPoint& outpoint) const
|
||||
{
|
||||
return ExecuteBackedWrapper<bool>([&]() { return CCoinsViewBacked::HaveCoin(outpoint); }, m_err_callbacks);
|
||||
}
|
||||
|
||||
std::optional<Coin> CCoinsViewErrorCatcher::PeekCoin(const COutPoint& outpoint) const
|
||||
{
|
||||
return ExecuteBackedWrapper<std::optional<Coin>>([&]() { return CCoinsViewBacked::PeekCoin(outpoint); }, m_err_callbacks);
|
||||
}
|
||||
|
||||
@ -302,9 +302,15 @@ class CCoinsView
|
||||
{
|
||||
public:
|
||||
//! 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;
|
||||
|
||||
//! 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;
|
||||
|
||||
//! 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;
|
||||
|
||||
//! Retrieve the block hash whose state this CCoinsView currently represents
|
||||
@ -340,6 +346,7 @@ protected:
|
||||
public:
|
||||
CCoinsViewBacked(CCoinsView *viewIn);
|
||||
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;
|
||||
@ -386,6 +393,7 @@ public:
|
||||
|
||||
// Standard CCoinsView methods
|
||||
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;
|
||||
void SetBestBlock(const uint256 &hashBlock);
|
||||
@ -536,6 +544,7 @@ public:
|
||||
|
||||
std::optional<Coin> GetCoin(const COutPoint& outpoint) const override;
|
||||
bool HaveCoin(const COutPoint &outpoint) const override;
|
||||
std::optional<Coin> PeekCoin(const COutPoint& outpoint) const override;
|
||||
|
||||
private:
|
||||
/** A list of callbacks to execute upon leveldb read error. */
|
||||
|
||||
@ -1159,4 +1159,25 @@ BOOST_AUTO_TEST_CASE(ccoins_reset_guard)
|
||||
BOOST_CHECK(!root_cache.HaveCoinInCache(outpoint));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(ccoins_peekcoin)
|
||||
{
|
||||
CCoinsViewTest base{m_rng};
|
||||
|
||||
// Populate the base view with a coin.
|
||||
const COutPoint outpoint{Txid::FromUint256(m_rng.rand256()), m_rng.rand32()};
|
||||
const Coin coin{CTxOut{m_rng.randrange(10), CScript{}}, 1, false};
|
||||
{
|
||||
CCoinsViewCache cache{&base};
|
||||
cache.AddCoin(outpoint, Coin{coin}, /*possible_overwrite=*/false);
|
||||
cache.Flush();
|
||||
}
|
||||
|
||||
// Verify PeekCoin can read through the cache stack without mutating the intermediate cache.
|
||||
CCoinsViewCacheTest main_cache{&base};
|
||||
const auto fetched{main_cache.PeekCoin(outpoint)};
|
||||
BOOST_CHECK(fetched.has_value());
|
||||
BOOST_CHECK(*fetched == coin);
|
||||
BOOST_CHECK(!main_cache.HaveCoinInCache(outpoint));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
||||
@ -257,7 +257,9 @@ FUZZ_TARGET(coinscache_sim)
|
||||
// Look up in simulation data.
|
||||
auto sim = lookup(outpointidx);
|
||||
// Look up in real caches.
|
||||
auto realcoin = caches.back()->GetCoin(data.outpoints[outpointidx]);
|
||||
auto realcoin = provider.ConsumeBool() ?
|
||||
caches.back()->PeekCoin(data.outpoints[outpointidx]) :
|
||||
caches.back()->GetCoin(data.outpoints[outpointidx]);
|
||||
// Compare results.
|
||||
if (!sim.has_value()) {
|
||||
assert(!realcoin);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user