diff options
Diffstat (limited to 'src/coins.cpp')
-rw-r--r-- | src/coins.cpp | 85 |
1 files changed, 62 insertions, 23 deletions
diff --git a/src/coins.cpp b/src/coins.cpp index 9632e67f20..9d60089bf5 100644 --- a/src/coins.cpp +++ b/src/coins.cpp @@ -71,18 +71,6 @@ CCoinsKeyHasher::CCoinsKeyHasher() : salt(GetRandHash()) {} CCoinsViewCache::CCoinsViewCache(CCoinsView &baseIn, bool fDummy) : CCoinsViewBacked(baseIn), hasModifier(false), hashBlock(0) { } -bool CCoinsViewCache::GetCoins(const uint256 &txid, CCoins &coins) const { - if (cacheCoins.count(txid)) { - coins = cacheCoins[txid]; - return true; - } - if (base->GetCoins(txid, coins)) { - cacheCoins[txid] = coins; - return true; - } - return false; -} - CCoinsViewCache::~CCoinsViewCache() { assert(!hasModifier); @@ -93,21 +81,43 @@ CCoinsMap::const_iterator CCoinsViewCache::FetchCoins(const uint256 &txid) const if (it != cacheCoins.end()) return it; CCoins tmp; - if (!base->GetCoins(txid,tmp)) + if (!base->GetCoins(txid, tmp)) return cacheCoins.end(); - CCoinsMap::iterator ret = cacheCoins.insert(it, std::make_pair(txid, CCoins())); - tmp.swap(ret->second); + CCoinsMap::iterator ret = cacheCoins.insert(std::make_pair(txid, CCoinsCacheEntry())).first; + tmp.swap(ret->second.coins); + if (ret->second.coins.IsPruned()) { + // The parent only has an empty entry for this txid; we can consider our + // version as fresh. + ret->second.flags = CCoinsCacheEntry::FRESH; + } return ret; } +bool CCoinsViewCache::GetCoins(const uint256 &txid, CCoins &coins) const { + CCoinsMap::const_iterator it = FetchCoins(txid); + if (it != cacheCoins.end()) { + coins = it->second.coins; + return true; + } + return false; +} + CCoinsModifier CCoinsViewCache::ModifyCoins(const uint256 &txid) { assert(!hasModifier); hasModifier = true; - std::pair<CCoinsMap::iterator, bool> ret = cacheCoins.insert(std::make_pair(txid, CCoins())); + std::pair<CCoinsMap::iterator, bool> ret = cacheCoins.insert(std::make_pair(txid, CCoinsCacheEntry())); if (ret.second) { - if (!base->GetCoins(txid, ret.first->second)) - ret.first->second.Clear(); + if (!base->GetCoins(txid, ret.first->second.coins)) { + // The parent view does not have this entry; mark it as fresh. + ret.first->second.coins.Clear(); + ret.first->second.flags = CCoinsCacheEntry::FRESH; + } else if (ret.first->second.coins.IsPruned()) { + // The parent view only has a pruned entry for this; mark it as fresh. + ret.first->second.flags = CCoinsCacheEntry::FRESH; + } } + // Assume that whenever ModifyCoins is called, the entry will be modified. + ret.first->second.flags |= CCoinsCacheEntry::DIRTY; return CCoinsModifier(*this, ret.first); } @@ -116,7 +126,7 @@ const CCoins* CCoinsViewCache::AccessCoins(const uint256 &txid) const { if (it == cacheCoins.end()) { return NULL; } else { - return &it->second; + return &it->second.coins; } } @@ -126,7 +136,7 @@ bool CCoinsViewCache::HaveCoins(const uint256 &txid) const { // as we only care about the case where an transaction was replaced entirely // in a reorganization (which wipes vout entirely, as opposed to spending // which just cleans individual outputs). - return (it != cacheCoins.end() && !it->second.vout.empty()); + return (it != cacheCoins.end() && !it->second.coins.vout.empty()); } uint256 CCoinsViewCache::GetBestBlock() const { @@ -142,7 +152,32 @@ void CCoinsViewCache::SetBestBlock(const uint256 &hashBlockIn) { bool CCoinsViewCache::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlockIn) { assert(!hasModifier); for (CCoinsMap::iterator it = mapCoins.begin(); it != mapCoins.end();) { - cacheCoins[it->first].swap(it->second); + if (it->second.flags & CCoinsCacheEntry::DIRTY) { // Ignore non-dirty entries (optimization). + CCoinsMap::iterator itUs = cacheCoins.find(it->first); + if (itUs == cacheCoins.end()) { + if (!it->second.coins.IsPruned()) { + // The parent cache does not have an entry, while the child + // cache does have (a non-pruned) one. Move the data up, and + // mark it as fresh (if the grandparent did have it, we + // would have pulled it in at first GetCoins). + assert(it->second.flags & CCoinsCacheEntry::FRESH); + CCoinsCacheEntry& entry = cacheCoins[it->first]; + entry.coins.swap(it->second.coins); + entry.flags = CCoinsCacheEntry::DIRTY | CCoinsCacheEntry::FRESH; + } + } else { + if ((itUs->second.flags & CCoinsCacheEntry::FRESH) && it->second.coins.IsPruned()) { + // The grandparent does not have an entry, and the child is + // modified and being pruned. This means we can just delete + // it from the parent. + cacheCoins.erase(itUs); + } else { + // A normal modification. + itUs->second.coins.swap(it->second.coins); + itUs->second.flags |= CCoinsCacheEntry::DIRTY; + } + } + } CCoinsMap::iterator itOld = it++; mapCoins.erase(itOld); } @@ -212,8 +247,12 @@ double CCoinsViewCache::GetPriority(const CTransaction &tx, int nHeight) const CCoinsModifier::CCoinsModifier(CCoinsViewCache& cache_, CCoinsMap::iterator it_) : cache(cache_), it(it_) {} -CCoinsModifier::~CCoinsModifier() { +CCoinsModifier::~CCoinsModifier() +{ assert(cache.hasModifier); cache.hasModifier = false; - it->second.Cleanup(); + it->second.coins.Cleanup(); + if ((it->second.flags & CCoinsCacheEntry::FRESH) && it->second.coins.IsPruned()) { + cache.cacheCoins.erase(it); + } } |