aboutsummaryrefslogtreecommitdiff
path: root/src/coins.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/coins.cpp')
-rw-r--r--src/coins.cpp65
1 files changed, 39 insertions, 26 deletions
diff --git a/src/coins.cpp b/src/coins.cpp
index 39db7dedfb..b2e33abf33 100644
--- a/src/coins.cpp
+++ b/src/coins.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2012-2015 The Bitcoin Core developers
+// Copyright (c) 2012-2016 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -117,17 +117,37 @@ CCoinsModifier CCoinsViewCache::ModifyCoins(const uint256 &txid) {
return CCoinsModifier(*this, ret.first, cachedCoinUsage);
}
-// ModifyNewCoins has to know whether the new outputs its creating are for a
-// coinbase or not. If they are for a coinbase, it can not mark them as fresh.
-// This is to ensure that the historical duplicate coinbases before BIP30 was
-// in effect will still be properly overwritten when spent.
+/* ModifyNewCoins allows for faster coin modification when creating the new
+ * outputs from a transaction. It assumes that BIP 30 (no duplicate txids)
+ * applies and has already been tested for (or the test is not required due to
+ * BIP 34, height in coinbase). If we can assume BIP 30 then we know that any
+ * non-coinbase transaction we are adding to the UTXO must not already exist in
+ * the utxo unless it is fully spent. Thus we can check only if it exists DIRTY
+ * at the current level of the cache, in which case it is not safe to mark it
+ * FRESH (b/c then its spentness still needs to flushed). If it's not dirty and
+ * doesn't exist or is pruned in the current cache, we know it either doesn't
+ * exist or is pruned in parent caches, which is the definition of FRESH. The
+ * exception to this is the two historical violations of BIP 30 in the chain,
+ * both of which were coinbases. We do not mark these fresh so we we can ensure
+ * that they will still be properly overwritten when spent.
+ */
CCoinsModifier CCoinsViewCache::ModifyNewCoins(const uint256 &txid, bool coinbase) {
assert(!hasModifier);
std::pair<CCoinsMap::iterator, bool> ret = cacheCoins.insert(std::make_pair(txid, CCoinsCacheEntry()));
- ret.first->second.coins.Clear();
if (!coinbase) {
- ret.first->second.flags = CCoinsCacheEntry::FRESH;
+ // New coins must not already exist.
+ if (!ret.first->second.coins.IsPruned())
+ throw std::logic_error("ModifyNewCoins should not find pre-existing coins on a non-coinbase unless they are pruned!");
+
+ if (!(ret.first->second.flags & CCoinsCacheEntry::DIRTY)) {
+ // If the coin is known to be pruned (have no unspent outputs) in
+ // the current view and the cache entry is not dirty, we know the
+ // coin also must be pruned in the parent view as well, so it is safe
+ // to mark this fresh.
+ ret.first->second.flags |= CCoinsCacheEntry::FRESH;
+ }
}
+ ret.first->second.coins.Clear();
ret.first->second.flags |= CCoinsCacheEntry::DIRTY;
return CCoinsModifier(*this, ret.first, 0);
}
@@ -187,6 +207,13 @@ bool CCoinsViewCache::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlockIn
entry.flags |= CCoinsCacheEntry::FRESH;
}
} else {
+ // Assert that the child cache entry was not marked FRESH if the
+ // 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) && !itUs->second.coins.IsPruned())
+ throw std::logic_error("FRESH flag misapplied to cache entry for base transaction with spendable outputs");
+
// 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
@@ -200,6 +227,11 @@ bool CCoinsViewCache::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlockIn
itUs->second.coins.swap(it->second.coins);
cachedCoinsUsage += itUs->second.coins.DynamicMemoryUsage();
itUs->second.flags |= CCoinsCacheEntry::DIRTY;
+ // NOTE: It is possible the child has a FRESH flag here in
+ // the event the entry we found in the parent is pruned. But
+ // we must not copy that FRESH flag to the parent as that
+ // pruned state likely still needs to be communicated to the
+ // grandparent.
}
}
}
@@ -263,25 +295,6 @@ bool CCoinsViewCache::HaveInputs(const CTransaction& tx) const
return true;
}
-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 = 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;