diff options
Diffstat (limited to 'src/coins.cpp')
| -rw-r--r-- | src/coins.cpp | 245 |
1 files changed, 162 insertions, 83 deletions
diff --git a/src/coins.cpp b/src/coins.cpp index 7bfb84ef3..122bf4e48 100644 --- a/src/coins.cpp +++ b/src/coins.cpp @@ -1,16 +1,19 @@ -// Copyright (c) 2012-2013 The Bitcoin developers -// Distributed under the MIT/X11 software license, see the accompanying +// Copyright (c) 2012-2014 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "coins.h" +#include "memusage.h" #include "random.h" #include <assert.h> -// calculate number of bytes for the bitmask, and its number of non-zero bytes -// each bit in the bitmask represents the availability of one output, but the -// availabilities of the first two outputs are encoded separately +/** + * calculate number of bytes for the bitmask, and its number of non-zero bytes + * each bit in the bitmask represents the availability of one output, but the + * availabilities of the first two outputs are encoded separately + */ void CCoins::CalcMaskSize(unsigned int &nBytes, unsigned int &nNonzeroBytes) const { unsigned int nLastUsedByte = 0; for (unsigned int b = 0; 2+b*8 < vout.size(); b++) { @@ -29,120 +32,171 @@ void CCoins::CalcMaskSize(unsigned int &nBytes, unsigned int &nNonzeroBytes) con nBytes += nLastUsedByte; } -bool CCoins::Spend(const COutPoint &out, CTxInUndo &undo) { - if (out.n >= vout.size()) - return false; - if (vout[out.n].IsNull()) +bool CCoins::Spend(uint32_t nPos) +{ + if (nPos >= vout.size() || vout[nPos].IsNull()) return false; - undo = CTxInUndo(vout[out.n]); - vout[out.n].SetNull(); + vout[nPos].SetNull(); Cleanup(); - if (vout.size() == 0) { - undo.nHeight = nHeight; - undo.fCoinBase = fCoinBase; - undo.nVersion = this->nVersion; - } return true; } -bool CCoins::Spend(int nPos) { - CTxInUndo undo; - COutPoint out(0, nPos); - return Spend(out, undo); -} - - bool CCoinsView::GetCoins(const uint256 &txid, CCoins &coins) const { return false; } -bool CCoinsView::SetCoins(const uint256 &txid, const CCoins &coins) { return false; } bool CCoinsView::HaveCoins(const uint256 &txid) const { return false; } -uint256 CCoinsView::GetBestBlock() const { return uint256(0); } -bool CCoinsView::SetBestBlock(const uint256 &hashBlock) { return false; } +uint256 CCoinsView::GetBestBlock() const { return uint256(); } bool CCoinsView::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) { return false; } bool CCoinsView::GetStats(CCoinsStats &stats) const { return false; } -CCoinsViewBacked::CCoinsViewBacked(CCoinsView &viewIn) : base(&viewIn) { } +CCoinsViewBacked::CCoinsViewBacked(CCoinsView *viewIn) : base(viewIn) { } bool CCoinsViewBacked::GetCoins(const uint256 &txid, CCoins &coins) const { return base->GetCoins(txid, coins); } -bool CCoinsViewBacked::SetCoins(const uint256 &txid, const CCoins &coins) { return base->SetCoins(txid, coins); } bool CCoinsViewBacked::HaveCoins(const uint256 &txid) const { return base->HaveCoins(txid); } uint256 CCoinsViewBacked::GetBestBlock() const { return base->GetBestBlock(); } -bool CCoinsViewBacked::SetBestBlock(const uint256 &hashBlock) { return base->SetBestBlock(hashBlock); } void CCoinsViewBacked::SetBackend(CCoinsView &viewIn) { base = &viewIn; } bool CCoinsViewBacked::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) { return base->BatchWrite(mapCoins, hashBlock); } bool CCoinsViewBacked::GetStats(CCoinsStats &stats) const { return base->GetStats(stats); } CCoinsKeyHasher::CCoinsKeyHasher() : salt(GetRandHash()) {} -CCoinsViewCache::CCoinsViewCache(CCoinsView &baseIn, bool fDummy) : CCoinsViewBacked(baseIn), hashBlock(0) { } +CCoinsViewCache::CCoinsViewCache(CCoinsView *baseIn) : CCoinsViewBacked(baseIn), hasModifier(false), cachedCoinsUsage(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); } -CCoinsMap::iterator CCoinsViewCache::FetchCoins(const uint256 &txid) { +size_t CCoinsViewCache::DynamicMemoryUsage() const { + return memusage::DynamicUsage(cacheCoins) + cachedCoinsUsage; +} + +CCoinsMap::const_iterator CCoinsViewCache::FetchCoins(const uint256 &txid) const { CCoinsMap::iterator it = cacheCoins.find(txid); 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; + } + cachedCoinsUsage += ret->second.coins.DynamicMemoryUsage(); return ret; } -CCoinsMap::const_iterator CCoinsViewCache::FetchCoins(const uint256 &txid) const { - /* Avoid redundant implementation with the const-cast. */ - return const_cast<CCoinsViewCache*>(this)->FetchCoins(txid); +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; } -CCoins &CCoinsViewCache::GetCoins(const uint256 &txid) { - CCoinsMap::iterator it = FetchCoins(txid); - assert(it != cacheCoins.end()); - return it->second; +CCoinsModifier CCoinsViewCache::ModifyCoins(const uint256 &txid) { + assert(!hasModifier); + std::pair<CCoinsMap::iterator, bool> ret = cacheCoins.insert(std::make_pair(txid, CCoinsCacheEntry())); + size_t cachedCoinUsage = 0; + if (ret.second) { + 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; + } + } else { + cachedCoinUsage = ret.first->second.coins.DynamicMemoryUsage(); + } + // Assume that whenever ModifyCoins is called, the entry will be modified. + ret.first->second.flags |= CCoinsCacheEntry::DIRTY; + return CCoinsModifier(*this, ret.first, cachedCoinUsage); } -const CCoins &CCoinsViewCache::GetCoins(const uint256 &txid) const { - /* Avoid redundant implementation with the const-cast. */ - return const_cast<CCoinsViewCache*>(this)->GetCoins(txid); +CCoinsModifier CCoinsViewCache::ModifyNewCoins(const uint256 &txid) { + assert(!hasModifier); + std::pair<CCoinsMap::iterator, bool> ret = cacheCoins.insert(std::make_pair(txid, CCoinsCacheEntry())); + ret.first->second.coins.Clear(); + ret.first->second.flags = CCoinsCacheEntry::FRESH; + ret.first->second.flags |= CCoinsCacheEntry::DIRTY; + return CCoinsModifier(*this, ret.first, 0); } -bool CCoinsViewCache::SetCoins(const uint256 &txid, const CCoins &coins) { - cacheCoins[txid] = coins; - return true; +const CCoins* CCoinsViewCache::AccessCoins(const uint256 &txid) const { + CCoinsMap::const_iterator it = FetchCoins(txid); + if (it == cacheCoins.end()) { + return NULL; + } else { + return &it->second.coins; + } } bool CCoinsViewCache::HaveCoins(const uint256 &txid) const { CCoinsMap::const_iterator it = FetchCoins(txid); // We're using vtx.empty() instead of IsPruned here for performance reasons, - // as we only care about the case where an transaction was replaced entirely + // as we only care about the case where a 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()); +} + +bool CCoinsViewCache::HaveCoinsInCache(const uint256 &txid) const { + CCoinsMap::const_iterator it = cacheCoins.find(txid); + return it != cacheCoins.end(); } uint256 CCoinsViewCache::GetBestBlock() const { - if (hashBlock == uint256(0)) + if (hashBlock.IsNull()) hashBlock = base->GetBestBlock(); return hashBlock; } -bool CCoinsViewCache::SetBestBlock(const uint256 &hashBlockIn) { +void CCoinsViewCache::SetBestBlock(const uint256 &hashBlockIn) { hashBlock = hashBlockIn; - return true; } 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()) { + // The parent cache does not have an entry, while the child does + // We can ignore it if it's both FRESH and pruned in the child + if (!(it->second.flags & CCoinsCacheEntry::FRESH && it->second.coins.IsPruned())) { + // Otherwise we will need to create it in the parent + // and move the data up and mark it as dirty + CCoinsCacheEntry& entry = cacheCoins[it->first]; + entry.coins.swap(it->second.coins); + cachedCoinsUsage += entry.coins.DynamicMemoryUsage(); + entry.flags = CCoinsCacheEntry::DIRTY; + // We can mark it FRESH in the parent if it was FRESH in the child + // Otherwise it might have just been flushed from the parent's cache + // and already exist in the grandparent + if (it->second.flags & CCoinsCacheEntry::FRESH) + entry.flags |= CCoinsCacheEntry::FRESH; + } + } else { + // Found the entry in the parent cache + 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. + cachedCoinsUsage -= itUs->second.coins.DynamicMemoryUsage(); + cacheCoins.erase(itUs); + } else { + // A normal modification. + cachedCoinsUsage -= itUs->second.coins.DynamicMemoryUsage(); + itUs->second.coins.swap(it->second.coins); + cachedCoinsUsage += itUs->second.coins.DynamicMemoryUsage(); + itUs->second.flags |= CCoinsCacheEntry::DIRTY; + } + } + } CCoinsMap::iterator itOld = it++; mapCoins.erase(itOld); } @@ -153,26 +207,36 @@ bool CCoinsViewCache::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlockIn bool CCoinsViewCache::Flush() { bool fOk = base->BatchWrite(cacheCoins, hashBlock); cacheCoins.clear(); + cachedCoinsUsage = 0; return fOk; } +void CCoinsViewCache::Uncache(const uint256& hash) +{ + CCoinsMap::iterator it = cacheCoins.find(hash); + if (it != cacheCoins.end() && it->second.flags == 0) { + cachedCoinsUsage -= it->second.coins.DynamicMemoryUsage(); + cacheCoins.erase(it); + } +} + unsigned int CCoinsViewCache::GetCacheSize() const { return cacheCoins.size(); } const CTxOut &CCoinsViewCache::GetOutputFor(const CTxIn& input) const { - const CCoins &coins = GetCoins(input.prevout.hash); - assert(coins.IsAvailable(input.prevout.n)); - return coins.vout[input.prevout.n]; + const CCoins* coins = AccessCoins(input.prevout.hash); + assert(coins && coins->IsAvailable(input.prevout.n)); + return coins->vout[input.prevout.n]; } -int64_t CCoinsViewCache::GetValueIn(const CTransaction& tx) const +CAmount CCoinsViewCache::GetValueIn(const CTransaction& tx) const { if (tx.IsCoinBase()) return 0; - int64_t nResult = 0; + CAmount nResult = 0; for (unsigned int i = 0; i < tx.vin.size(); i++) nResult += GetOutputFor(tx.vin[i]).nValue; @@ -182,36 +246,51 @@ int64_t CCoinsViewCache::GetValueIn(const CTransaction& tx) const bool CCoinsViewCache::HaveInputs(const CTransaction& tx) const { if (!tx.IsCoinBase()) { - // first check whether information about the prevout hash is available for (unsigned int i = 0; i < tx.vin.size(); i++) { const COutPoint &prevout = tx.vin[i].prevout; - if (!HaveCoins(prevout.hash)) - return false; - } - - // then check whether the actual outputs are available - for (unsigned int i = 0; i < tx.vin.size(); i++) { - const COutPoint &prevout = tx.vin[i].prevout; - const CCoins &coins = GetCoins(prevout.hash); - if (!coins.IsAvailable(prevout.n)) + const CCoins* coins = AccessCoins(prevout.hash); + if (!coins || !coins->IsAvailable(prevout.n)) { return false; + } } } return true; } -double CCoinsViewCache::GetPriority(const CTransaction &tx, int nHeight) const +double CCoinsViewCache::GetPriority(const CTransaction &tx, int nHeight, CAmount &inChainInputValue) const { + inChainInputValue = 0; if (tx.IsCoinBase()) return 0.0; double dResult = 0.0; BOOST_FOREACH(const CTxIn& txin, tx.vin) { - const CCoins &coins = GetCoins(txin.prevout.hash); - if (!coins.IsAvailable(txin.prevout.n)) continue; - if (coins.nHeight < nHeight) { - dResult += coins.vout[txin.prevout.n].nValue * (nHeight-coins.nHeight); + const CCoins* coins = AccessCoins(txin.prevout.hash); + assert(coins); + if (!coins->IsAvailable(txin.prevout.n)) continue; + if (coins->nHeight <= nHeight) { + dResult += coins->vout[txin.prevout.n].nValue * (nHeight-coins->nHeight); + inChainInputValue += coins->vout[txin.prevout.n].nValue; } } return tx.ComputePriority(dResult); } + +CCoinsModifier::CCoinsModifier(CCoinsViewCache& cache_, CCoinsMap::iterator it_, size_t usage) : cache(cache_), it(it_), cachedCoinUsage(usage) { + assert(!cache.hasModifier); + cache.hasModifier = true; +} + +CCoinsModifier::~CCoinsModifier() +{ + assert(cache.hasModifier); + cache.hasModifier = false; + it->second.coins.Cleanup(); + cache.cachedCoinsUsage -= cachedCoinUsage; // Subtract the old usage + if ((it->second.flags & CCoinsCacheEntry::FRESH) && it->second.coins.IsPruned()) { + cache.cacheCoins.erase(it); + } else { + // If the coin still exists after the modification, add the new usage + cache.cachedCoinsUsage += it->second.coins.DynamicMemoryUsage(); + } +} |