From 9e38d0f7451092f6a16853a2b0a37b68a5b3c3fb Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Fri, 17 Jul 2015 13:46:18 -0400 Subject: Separate core memory usage computation in core_memusage.h --- src/coins.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'src/coins.cpp') diff --git a/src/coins.cpp b/src/coins.cpp index a41d5a310..f02949de5 100644 --- a/src/coins.cpp +++ b/src/coins.cpp @@ -83,7 +83,7 @@ CCoinsMap::const_iterator CCoinsViewCache::FetchCoins(const uint256 &txid) const // version as fresh. ret->second.flags = CCoinsCacheEntry::FRESH; } - cachedCoinsUsage += memusage::DynamicUsage(ret->second.coins); + cachedCoinsUsage += ret->second.coins.DynamicMemoryUsage(); return ret; } @@ -110,7 +110,7 @@ CCoinsModifier CCoinsViewCache::ModifyCoins(const uint256 &txid) { ret.first->second.flags = CCoinsCacheEntry::FRESH; } } else { - cachedCoinUsage = memusage::DynamicUsage(ret.first->second.coins); + cachedCoinUsage = ret.first->second.coins.DynamicMemoryUsage(); } // Assume that whenever ModifyCoins is called, the entry will be modified. ret.first->second.flags |= CCoinsCacheEntry::DIRTY; @@ -159,7 +159,7 @@ bool CCoinsViewCache::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlockIn assert(it->second.flags & CCoinsCacheEntry::FRESH); CCoinsCacheEntry& entry = cacheCoins[it->first]; entry.coins.swap(it->second.coins); - cachedCoinsUsage += memusage::DynamicUsage(entry.coins); + cachedCoinsUsage += entry.coins.DynamicMemoryUsage(); entry.flags = CCoinsCacheEntry::DIRTY | CCoinsCacheEntry::FRESH; } } else { @@ -167,13 +167,13 @@ bool CCoinsViewCache::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlockIn // 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 -= memusage::DynamicUsage(itUs->second.coins); + cachedCoinsUsage -= itUs->second.coins.DynamicMemoryUsage(); cacheCoins.erase(itUs); } else { // A normal modification. - cachedCoinsUsage -= memusage::DynamicUsage(itUs->second.coins); + cachedCoinsUsage -= itUs->second.coins.DynamicMemoryUsage(); itUs->second.coins.swap(it->second.coins); - cachedCoinsUsage += memusage::DynamicUsage(itUs->second.coins); + cachedCoinsUsage += itUs->second.coins.DynamicMemoryUsage(); itUs->second.flags |= CCoinsCacheEntry::DIRTY; } } @@ -261,6 +261,6 @@ CCoinsModifier::~CCoinsModifier() cache.cacheCoins.erase(it); } else { // If the coin still exists after the modification, add the new usage - cache.cachedCoinsUsage += memusage::DynamicUsage(it->second.coins); + cache.cachedCoinsUsage += it->second.coins.DynamicMemoryUsage(); } } -- cgit v1.2.3 From 14470f9aa6baf02ca7162564f397153a2da0c592 Mon Sep 17 00:00:00 2001 From: Alex Morcos Date: Mon, 2 Nov 2015 21:27:15 -0500 Subject: ModifyNewCoins saves database lookups When processing a new transaction, in addition to spending the Coins of its txin's it creates a new Coins for its outputs. The existing ModifyCoins function will first make sure this Coins does not already exist. It can not exist due to BIP 30, but because of that the lookup can't be cached and always has to go to the database. Since we are creating the coins to match the new tx anyway, there is no point in checking if they exist first anyway. However this should not be used for coinbase tx's in order to preserve the historical behavior of overwriting the two existing duplicate tx pairs. --- src/coins.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'src/coins.cpp') diff --git a/src/coins.cpp b/src/coins.cpp index f02949de5..96b336ce7 100644 --- a/src/coins.cpp +++ b/src/coins.cpp @@ -117,6 +117,15 @@ CCoinsModifier CCoinsViewCache::ModifyCoins(const uint256 &txid) { return CCoinsModifier(*this, ret.first, cachedCoinUsage); } +CCoinsModifier CCoinsViewCache::ModifyNewCoins(const uint256 &txid) { + assert(!hasModifier); + std::pair 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); +} + const CCoins* CCoinsViewCache::AccessCoins(const uint256 &txid) const { CCoinsMap::const_iterator it = FetchCoins(txid); if (it == cacheCoins.end()) { -- cgit v1.2.3 From 072e2f864445bc6ef3b390255f08c9e8bec2ea94 Mon Sep 17 00:00:00 2001 From: Alex Morcos Date: Thu, 26 Mar 2015 13:52:10 -0400 Subject: Alter assumptions in CCoinsViewCache::BatchWrite Previously it would break if you flushed a parent cache while there was a child cache referring to it. This change will allow the flushing of parent caches. --- src/coins.cpp | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) (limited to 'src/coins.cpp') diff --git a/src/coins.cpp b/src/coins.cpp index 96b336ce7..f0ea5c045 100644 --- a/src/coins.cpp +++ b/src/coins.cpp @@ -160,18 +160,23 @@ bool CCoinsViewCache::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlockIn 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); + // 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 | CCoinsCacheEntry::FRESH; + 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 -- cgit v1.2.3 From 8504867b146014c99c6acb180020a1369069c761 Mon Sep 17 00:00:00 2001 From: Alex Morcos Date: Thu, 12 Nov 2015 16:57:03 -0500 Subject: Save the last unnecessary database read It's possible coins with the same hash exist when you create a duplicate coinbase, so previously we were reading from the database to make sure we had the old coins cached so if we were to spend the new ones, the old ones would also be spent. This pull instead just marks the new coins as not fresh if they are from a coinbase, so if they are spent they will be written all the way down to the database anyway overwriting any duplicates. --- src/coins.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'src/coins.cpp') diff --git a/src/coins.cpp b/src/coins.cpp index f0ea5c045..660181b0c 100644 --- a/src/coins.cpp +++ b/src/coins.cpp @@ -117,11 +117,17 @@ CCoinsModifier CCoinsViewCache::ModifyCoins(const uint256 &txid) { return CCoinsModifier(*this, ret.first, cachedCoinUsage); } -CCoinsModifier CCoinsViewCache::ModifyNewCoins(const uint256 &txid) { +// 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. +CCoinsModifier CCoinsViewCache::ModifyNewCoins(const uint256 &txid, bool coinbase) { assert(!hasModifier); std::pair ret = cacheCoins.insert(std::make_pair(txid, CCoinsCacheEntry())); ret.first->second.coins.Clear(); - ret.first->second.flags = CCoinsCacheEntry::FRESH; + if (!coinbase) { + ret.first->second.flags = CCoinsCacheEntry::FRESH; + } ret.first->second.flags |= CCoinsCacheEntry::DIRTY; return CCoinsModifier(*this, ret.first, 0); } -- cgit v1.2.3 From c0353064ddf71ad103bd19f6e7c10ff8e240ac46 Mon Sep 17 00:00:00 2001 From: Alex Morcos Date: Fri, 13 Nov 2015 10:05:21 -0500 Subject: Change GetPriority calculation. Compute the value of inputs that already are in the chain at time of mempool entry and only increase priority due to aging for those inputs. This effectively changes the CTxMemPoolEntry's GetPriority calculation from an upper bound to a lower bound. --- src/coins.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'src/coins.cpp') diff --git a/src/coins.cpp b/src/coins.cpp index f0ea5c045..723e11470 100644 --- a/src/coins.cpp +++ b/src/coins.cpp @@ -243,8 +243,9 @@ bool CCoinsViewCache::HaveInputs(const CTransaction& tx) const 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; @@ -253,8 +254,9 @@ double CCoinsViewCache::GetPriority(const CTransaction &tx, int nHeight) const const CCoins* coins = AccessCoins(txin.prevout.hash); assert(coins); if (!coins->IsAvailable(txin.prevout.n)) continue; - if (coins->nHeight < nHeight) { + 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); -- cgit v1.2.3 From 74d0f902628472cd0cee66121ef0311eec201c40 Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Wed, 21 Oct 2015 17:41:40 -0700 Subject: Add method to remove a tx from CCoinsViewCache if it is unchanged --- src/coins.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'src/coins.cpp') diff --git a/src/coins.cpp b/src/coins.cpp index 723e11470..060d6b7c5 100644 --- a/src/coins.cpp +++ b/src/coins.cpp @@ -206,6 +206,15 @@ bool CCoinsViewCache::Flush() { 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(); } -- cgit v1.2.3 From 97bf377bd1f27ad841e1414e74361923a9f794f5 Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Thu, 22 Oct 2015 15:49:53 -0700 Subject: Add CCoinsViewCache::HaveCoinsInCache to check if a tx is cached --- src/coins.cpp | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'src/coins.cpp') diff --git a/src/coins.cpp b/src/coins.cpp index 060d6b7c5..122bf4e48 100644 --- a/src/coins.cpp +++ b/src/coins.cpp @@ -144,6 +144,11 @@ bool CCoinsViewCache::HaveCoins(const uint256 &txid) const { 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.IsNull()) hashBlock = base->GetBestBlock(); -- cgit v1.2.3 From fa24439ff3d8ab5b9efaf66ef4dae6713b88cb35 Mon Sep 17 00:00:00 2001 From: MarcoFalke Date: Sun, 13 Dec 2015 17:58:29 +0100 Subject: Bump copyright headers to 2015 --- src/coins.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/coins.cpp') diff --git a/src/coins.cpp b/src/coins.cpp index 122bf4e48..4d1dbdea4 100644 --- a/src/coins.cpp +++ b/src/coins.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014 The Bitcoin Core developers +// Copyright (c) 2012-2015 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -- cgit v1.2.3 From 509cb006d514cece5ab7680094f033c8dc8a2318 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Mon, 28 Mar 2016 18:18:30 +0200 Subject: txdb: Add Cursor() method to CCoinsView to iterate over UTXO set Add a method Cursor() to CCoinsView that returns a cursor which can be used to iterate over the whole UTXO set. - rpc: Change gettxoutsetinfo to use new Cursor method - txdb: Remove GetStats method - Now that GetStats is implemented in terms of Cursor, remove it. --- src/coins.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'src/coins.cpp') diff --git a/src/coins.cpp b/src/coins.cpp index 877fb8b26..1c329740b 100644 --- a/src/coins.cpp +++ b/src/coins.cpp @@ -45,7 +45,7 @@ bool CCoinsView::GetCoins(const uint256 &txid, CCoins &coins) const { return fal bool CCoinsView::HaveCoins(const uint256 &txid) const { 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; } +CCoinsViewCursor *CCoinsView::Cursor() const { return 0; } CCoinsViewBacked::CCoinsViewBacked(CCoinsView *viewIn) : base(viewIn) { } @@ -54,7 +54,7 @@ bool CCoinsViewBacked::HaveCoins(const uint256 &txid) const { return base->HaveC uint256 CCoinsViewBacked::GetBestBlock() const { return base->GetBestBlock(); } 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); } +CCoinsViewCursor *CCoinsViewBacked::Cursor() const { return base->Cursor(); } CCoinsKeyHasher::CCoinsKeyHasher() : salt(GetRandHash()) {} @@ -300,3 +300,7 @@ CCoinsModifier::~CCoinsModifier() cache.cachedCoinsUsage += it->second.coins.DynamicMemoryUsage(); } } + +CCoinsViewCursor::~CCoinsViewCursor() +{ +} -- cgit v1.2.3 From 382c871d28b95cc52309a128edd8dc23822bcd60 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Fri, 6 May 2016 20:47:12 +0200 Subject: Use SipHash-2-4 for CCoinsCache index This is ~1.7x slower than the Lookup3-of-Xor-with-salt construct we were using before, but it is a primitive designed for exactly this. --- src/coins.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'src/coins.cpp') diff --git a/src/coins.cpp b/src/coins.cpp index 1c329740b..b7dd293d6 100644 --- a/src/coins.cpp +++ b/src/coins.cpp @@ -56,7 +56,11 @@ void CCoinsViewBacked::SetBackend(CCoinsView &viewIn) { base = &viewIn; } bool CCoinsViewBacked::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) { return base->BatchWrite(mapCoins, hashBlock); } CCoinsViewCursor *CCoinsViewBacked::Cursor() const { return base->Cursor(); } -CCoinsKeyHasher::CCoinsKeyHasher() : salt(GetRandHash()) {} +SaltedTxidHasher::SaltedTxidHasher() +{ + GetRandBytes((unsigned char*)&k0, sizeof(k0)); + GetRandBytes((unsigned char*)&k1, sizeof(k1)); +} CCoinsViewCache::CCoinsViewCache(CCoinsView *baseIn) : CCoinsViewBacked(baseIn), hasModifier(false), cachedCoinsUsage(0) { } -- cgit v1.2.3 From 888483098e60f2a944f1d246bbfec4d14a2975f8 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Tue, 7 Jun 2016 16:29:03 +0200 Subject: Use C++11 thread-safe static initializers --- src/coins.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'src/coins.cpp') diff --git a/src/coins.cpp b/src/coins.cpp index b7dd293d6..39db7dedf 100644 --- a/src/coins.cpp +++ b/src/coins.cpp @@ -56,11 +56,7 @@ void CCoinsViewBacked::SetBackend(CCoinsView &viewIn) { base = &viewIn; } bool CCoinsViewBacked::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) { return base->BatchWrite(mapCoins, hashBlock); } CCoinsViewCursor *CCoinsViewBacked::Cursor() const { return base->Cursor(); } -SaltedTxidHasher::SaltedTxidHasher() -{ - GetRandBytes((unsigned char*)&k0, sizeof(k0)); - GetRandBytes((unsigned char*)&k1, sizeof(k1)); -} +SaltedTxidHasher::SaltedTxidHasher() : k0(GetRand(std::numeric_limits::max())), k1(GetRand(std::numeric_limits::max())) {} CCoinsViewCache::CCoinsViewCache(CCoinsView *baseIn) : CCoinsViewBacked(baseIn), hasModifier(false), cachedCoinsUsage(0) { } -- cgit v1.2.3 From 94a34a5d951cee59ef9c9274c5ad49ac2a91ab8a Mon Sep 17 00:00:00 2001 From: maiiz Date: Mon, 18 Jul 2016 15:01:34 +0800 Subject: Fix relaypriority calculation error --- src/coins.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/coins.cpp') diff --git a/src/coins.cpp b/src/coins.cpp index 39db7dedf..8ff652b47 100644 --- a/src/coins.cpp +++ b/src/coins.cpp @@ -275,7 +275,7 @@ double CCoinsViewCache::GetPriority(const CTransaction &tx, int nHeight, CAmount assert(coins); if (!coins->IsAvailable(txin.prevout.n)) continue; if (coins->nHeight <= nHeight) { - dResult += coins->vout[txin.prevout.n].nValue * (nHeight-coins->nHeight); + dResult += (double)(coins->vout[txin.prevout.n].nValue) * (nHeight-coins->nHeight); inChainInputValue += coins->vout[txin.prevout.n].nValue; } } -- cgit v1.2.3 From 27765b6403cece54320374b37afb01a0cfe571c3 Mon Sep 17 00:00:00 2001 From: isle2983 Date: Sat, 31 Dec 2016 11:01:21 -0700 Subject: Increment MIT Licence copyright header year on files modified in 2016 Edited via: $ contrib/devtools/copyright_header.py update . --- src/coins.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/coins.cpp') diff --git a/src/coins.cpp b/src/coins.cpp index 8ff652b47..04b3a6f23 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. -- cgit v1.2.3 From b50cd7a67e71051db59199a4185e7c82b669c659 Mon Sep 17 00:00:00 2001 From: Alex Morcos Date: Mon, 7 Nov 2016 15:30:41 -0500 Subject: Fix dangerous condition in ModifyNewCoins. We were marking coins FRESH before being sure they were not overwriting dirty undo data. This condition was never reached in existing code because undo data was always flushed before UpdateCoins was called with new transactions, but could have been exposed in an otherwise safe refactor. Clarify in the comments the assumptions made in ModifyNewCoins. Add ability to undo transactions to UpdateCoins unit test. Thanks to Russ Yanofsky for suggestion on how to make logic clearer and fixing up the ccoins_modify_new test cases. --- src/coins.cpp | 37 +++++++++++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 6 deletions(-) (limited to 'src/coins.cpp') diff --git a/src/coins.cpp b/src/coins.cpp index 8ff652b47..fe474fdda 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 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. } } } -- cgit v1.2.3 From dd44ea39bb6e1e2a244630909647ea7fbb052941 Mon Sep 17 00:00:00 2001 From: Russell Yanofsky Date: Fri, 9 Dec 2016 12:28:22 -0500 Subject: Check FRESH validity in CCoinsViewCache::BatchWrite --- src/coins.cpp | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'src/coins.cpp') diff --git a/src/coins.cpp b/src/coins.cpp index 68f32a9da..4d0e4bc0a 100644 --- a/src/coins.cpp +++ b/src/coins.cpp @@ -207,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 -- cgit v1.2.3