coins: Only update `cachedCoinsUsage` when entry is inserted in `EmplaceCoinInternalDANGER`

The `EmplaceCoinInternalDANGER` method was unconditionally adding to `cachedCoinsUsage`, but should only do so when `try_emplace` actually inserts a new entry. If the entry already exists, no memory is allocated and usage should not change.

Adds test coverage by randomly calling `EmplaceCoinInternalDANGER` in `SimulationTest` to verify it remains correct.

Co-authored-by: Pieter Wuille <pieter@wuille.net>
Co-authored-by: Andrew Toth <andrewstoth@gmail.com>
This commit is contained in:
Lőrinc 2025-09-30 17:38:36 -04:00
parent b510893d00
commit f377bd7468
2 changed files with 10 additions and 4 deletions

View File

@ -111,9 +111,12 @@ void CCoinsViewCache::AddCoin(const COutPoint &outpoint, Coin&& coin, bool possi
} }
void CCoinsViewCache::EmplaceCoinInternalDANGER(COutPoint&& outpoint, Coin&& coin) { void CCoinsViewCache::EmplaceCoinInternalDANGER(COutPoint&& outpoint, Coin&& coin) {
cachedCoinsUsage += coin.DynamicMemoryUsage(); const auto mem_usage{coin.DynamicMemoryUsage()};
auto [it, inserted] = cacheCoins.try_emplace(std::move(outpoint), std::move(coin)); auto [it, inserted] = cacheCoins.try_emplace(std::move(outpoint), std::move(coin));
if (inserted) CCoinsCacheEntry::SetDirty(*it, m_sentinel); if (inserted) {
CCoinsCacheEntry::SetDirty(*it, m_sentinel);
cachedCoinsUsage += mem_usage;
}
} }
void AddCoins(CCoinsViewCache& cache, const CTransaction &tx, int nHeight, bool check_for_overwrite) { void AddCoins(CCoinsViewCache& cache, const CTransaction &tx, int nHeight, bool check_for_overwrite) {

View File

@ -194,8 +194,11 @@ void SimulationTest(CCoinsView* base, bool fake_best_block)
(coin.IsSpent() ? added_an_entry : updated_an_entry) = true; (coin.IsSpent() ? added_an_entry : updated_an_entry) = true;
coin = newcoin; coin = newcoin;
} }
bool is_overwrite = !coin.IsSpent() || m_rng.rand32() & 1; if (COutPoint op(txid, 0); !stack.back()->map().contains(op) && !coin.IsSpent() && m_rng.randbool()) {
stack.back()->AddCoin(COutPoint(txid, 0), std::move(newcoin), is_overwrite); stack.back()->EmplaceCoinInternalDANGER(std::move(op), std::move(newcoin));
} else {
stack.back()->AddCoin(std::move(op), std::move(newcoin), /*possible_overwrite=*/!coin.IsSpent() || m_rng.randbool());
}
} else { } else {
// Spend the coin. // Spend the coin.
removed_an_entry = true; removed_an_entry = true;