aboutsummaryrefslogtreecommitdiff
path: root/src/coins.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/coins.cpp')
-rw-r--r--src/coins.cpp37
1 files changed, 31 insertions, 6 deletions
diff --git a/src/coins.cpp b/src/coins.cpp
index 8ff652b474..fe474fdda2 100644
--- a/src/coins.cpp
+++ b/src/coins.cpp
@@ -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);
}
@@ -200,6 +220,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.
}
}
}