Changeset View
Changeset View
Standalone View
Standalone View
src/coins.cpp
Show First 20 Lines • Show All 111 Lines • ▼ Show 20 Lines | std::tie(it, inserted) = | ||||
cacheCoins.emplace(std::piecewise_construct, | cacheCoins.emplace(std::piecewise_construct, | ||||
std::forward_as_tuple(outpoint), std::tuple<>()); | std::forward_as_tuple(outpoint), std::tuple<>()); | ||||
bool fresh = false; | bool fresh = false; | ||||
if (!inserted) { | if (!inserted) { | ||||
cachedCoinsUsage -= it->second.coin.DynamicMemoryUsage(); | cachedCoinsUsage -= it->second.coin.DynamicMemoryUsage(); | ||||
} | } | ||||
if (!possible_overwrite) { | if (!possible_overwrite) { | ||||
if (!it->second.coin.IsSpent()) { | if (!it->second.coin.IsSpent()) { | ||||
throw std::logic_error( | throw std::logic_error("Attempted to overwrite an unspent coin " | ||||
"Adding new coin that replaces non-pruned entry"); | "(when possible_overwrite is false)"); | ||||
} | } | ||||
// If the coin exists in this cache as a spent coin and is DIRTY, then | |||||
// its spentness hasn't been flushed to the parent cache. We're | |||||
// re-adding the coin to this cache now but we can't mark it as FRESH. | |||||
// If we mark it FRESH and then spend it before the cache is flushed | |||||
// we would remove it from this cache and would never flush spentness | |||||
// to the parent cache. | |||||
// | |||||
// Re-adding a spent coin can happen in the case of a re-org (the coin | |||||
// is 'spent' when the block adding it is disconnected and then | |||||
// re-added when it is also added in a newly connected block). | |||||
// | |||||
// If the coin doesn't exist in the current cache, or is spent but not | |||||
// DIRTY, then it can be marked FRESH. | |||||
fresh = !(it->second.flags & CCoinsCacheEntry::DIRTY); | fresh = !(it->second.flags & CCoinsCacheEntry::DIRTY); | ||||
} | } | ||||
it->second.coin = std::move(coin); | it->second.coin = std::move(coin); | ||||
it->second.flags |= | it->second.flags |= | ||||
CCoinsCacheEntry::DIRTY | (fresh ? CCoinsCacheEntry::FRESH : 0); | CCoinsCacheEntry::DIRTY | (fresh ? CCoinsCacheEntry::FRESH : 0); | ||||
cachedCoinsUsage += it->second.coin.DynamicMemoryUsage(); | cachedCoinsUsage += it->second.coin.DynamicMemoryUsage(); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 66 Lines • ▼ Show 20 Lines | bool CCoinsViewCache::BatchWrite(CCoinsMap &mapCoins, | ||||
for (CCoinsMap::iterator it = mapCoins.begin(); it != mapCoins.end(); | for (CCoinsMap::iterator it = mapCoins.begin(); it != mapCoins.end(); | ||||
it = mapCoins.erase(it)) { | it = mapCoins.erase(it)) { | ||||
// Ignore non-dirty entries (optimization). | // Ignore non-dirty entries (optimization). | ||||
if (!(it->second.flags & CCoinsCacheEntry::DIRTY)) { | if (!(it->second.flags & CCoinsCacheEntry::DIRTY)) { | ||||
continue; | continue; | ||||
} | } | ||||
CCoinsMap::iterator itUs = cacheCoins.find(it->first); | CCoinsMap::iterator itUs = cacheCoins.find(it->first); | ||||
if (itUs == cacheCoins.end()) { | if (itUs == cacheCoins.end()) { | ||||
// The parent cache does not have an entry, while the child does | // The parent cache does not have an entry, while the child cache | ||||
// We can ignore it if it's both FRESH and pruned in the child | // does. We can ignore it if it's both spent and FRESH in the child | ||||
if (!(it->second.flags & CCoinsCacheEntry::FRESH && | if (!(it->second.flags & CCoinsCacheEntry::FRESH && | ||||
it->second.coin.IsSpent())) { | it->second.coin.IsSpent())) { | ||||
// Otherwise we will need to create it in the parent and | // Create the coin in the parent cache, move the data up | ||||
// move the data up and mark it as dirty | // and mark it as dirty. | ||||
CCoinsCacheEntry &entry = cacheCoins[it->first]; | CCoinsCacheEntry &entry = cacheCoins[it->first]; | ||||
entry.coin = std::move(it->second.coin); | entry.coin = std::move(it->second.coin); | ||||
cachedCoinsUsage += entry.coin.DynamicMemoryUsage(); | cachedCoinsUsage += entry.coin.DynamicMemoryUsage(); | ||||
entry.flags = CCoinsCacheEntry::DIRTY; | entry.flags = CCoinsCacheEntry::DIRTY; | ||||
// We can mark it FRESH in the parent if it was FRESH in the | // We can mark it FRESH in the parent if it was FRESH in the | ||||
// child. Otherwise it might have just been flushed from the | // child. Otherwise it might have just been flushed from the | ||||
// parent's cache and already exist in the grandparent | // parent's cache and already exist in the grandparent | ||||
if (it->second.flags & CCoinsCacheEntry::FRESH) { | if (it->second.flags & CCoinsCacheEntry::FRESH) { | ||||
entry.flags |= CCoinsCacheEntry::FRESH; | entry.flags |= CCoinsCacheEntry::FRESH; | ||||
} | } | ||||
} | } | ||||
} else { | } else { | ||||
// Assert that the child cache entry was not marked FRESH if the | // Found the entry in the parent cache | ||||
// parent cache entry has unspent outputs. If this ever happens, | |||||
// it means the FRESH flag was misapplied and there is a logic | |||||
// error in the calling code. | |||||
if ((it->second.flags & CCoinsCacheEntry::FRESH) && | if ((it->second.flags & CCoinsCacheEntry::FRESH) && | ||||
!itUs->second.coin.IsSpent()) { | !itUs->second.coin.IsSpent()) { | ||||
throw std::logic_error("FRESH flag misapplied to cache " | // The coin was marked FRESH in the child cache, but the coin | ||||
"entry for base transaction with " | // exists in the parent cache. If this ever happens, it means | ||||
"spendable outputs"); | // the FRESH flag was misapplied and there is a logic error in | ||||
// the calling code. | |||||
throw std::logic_error("FRESH flag misapplied to coin that " | |||||
"exists in parent cache"); | |||||
} | } | ||||
// Found the entry in the parent cache | |||||
if ((itUs->second.flags & CCoinsCacheEntry::FRESH) && | if ((itUs->second.flags & CCoinsCacheEntry::FRESH) && | ||||
it->second.coin.IsSpent()) { | it->second.coin.IsSpent()) { | ||||
// The grandparent does not have an entry, and the child is | // The grandparent cache does not have an entry, and the coin | ||||
// modified and being pruned. This means we can just delete | // has been spent. We can just delete it from the parent cache. | ||||
// it from the parent. | |||||
cachedCoinsUsage -= itUs->second.coin.DynamicMemoryUsage(); | cachedCoinsUsage -= itUs->second.coin.DynamicMemoryUsage(); | ||||
cacheCoins.erase(itUs); | cacheCoins.erase(itUs); | ||||
} else { | } else { | ||||
// A normal modification. | // A normal modification. | ||||
cachedCoinsUsage -= itUs->second.coin.DynamicMemoryUsage(); | cachedCoinsUsage -= itUs->second.coin.DynamicMemoryUsage(); | ||||
itUs->second.coin = std::move(it->second.coin); | itUs->second.coin = std::move(it->second.coin); | ||||
cachedCoinsUsage += itUs->second.coin.DynamicMemoryUsage(); | cachedCoinsUsage += itUs->second.coin.DynamicMemoryUsage(); | ||||
itUs->second.flags |= CCoinsCacheEntry::DIRTY; | itUs->second.flags |= CCoinsCacheEntry::DIRTY; | ||||
// NOTE: It is possible the child has a FRESH flag here in | // NOTE: It isn't safe to mark the coin as FRESH in the parent | ||||
// the event the entry we found in the parent is pruned. But | // cache. If it already existed and was spent in the parent | ||||
// we must not copy that FRESH flag to the parent as that | // cache then marking it FRESH would prevent that spentness | ||||
// pruned state likely still needs to be communicated to the | // from being flushed to the grandparent. | ||||
// grandparent. | |||||
} | } | ||||
} | } | ||||
} | } | ||||
hashBlock = hashBlockIn; | hashBlock = hashBlockIn; | ||||
return true; | return true; | ||||
} | } | ||||
bool CCoinsViewCache::Flush() { | bool CCoinsViewCache::Flush() { | ||||
▲ Show 20 Lines • Show All 77 Lines • Show Last 20 Lines |