diff --git a/src/coins.cpp b/src/coins.cpp index c861bb81e..65c25f131 100644 --- a/src/coins.cpp +++ b/src/coins.cpp @@ -176,11 +176,20 @@ void CCoinsViewCache::PopAnchor(const uint256 &newrt) { // case restoring the "old" anchor during a reorg must // have no effect. if (currentRoot != newrt) { - CAnchorsMap::iterator ret = cacheAnchors.insert(std::make_pair(currentRoot, CAnchorsCacheEntry())).first; + // Bring the current best anchor into our local cache + // so that its tree exists in memory. + { + ZCIncrementalMerkleTree tree; + assert(GetAnchorAt(currentRoot, tree)); + } - ret->second.entered = false; - ret->second.flags = CAnchorsCacheEntry::DIRTY; + // Mark the anchor as unentered, removing it from view + cacheAnchors[currentRoot].entered = false; + // Mark the cache entry as dirty so it's propagated + cacheAnchors[currentRoot].flags = CAnchorsCacheEntry::DIRTY; + + // Mark the new root as the best anchor hashAnchor = newrt; } } diff --git a/src/test/coins_tests.cpp b/src/test/coins_tests.cpp index b638bccfc..ea7e81851 100644 --- a/src/test/coins_tests.cpp +++ b/src/test/coins_tests.cpp @@ -249,6 +249,81 @@ BOOST_AUTO_TEST_CASE(nullifier_regression_test) } } +BOOST_AUTO_TEST_CASE(anchor_pop_regression_test) +{ + // Correct behavior: + { + CCoinsViewTest base; + CCoinsViewCacheTest cache1(&base); + + // Create dummy anchor/commitment + ZCIncrementalMerkleTree tree; + uint256 cm = GetRandHash(); + tree.append(cm); + + // Add the anchor + cache1.PushAnchor(tree); + cache1.Flush(); + + // Remove the anchor + cache1.PopAnchor(ZCIncrementalMerkleTree::empty_root()); + cache1.Flush(); + + // Add the anchor back + cache1.PushAnchor(tree); + cache1.Flush(); + + // The base contains the anchor, of course! + { + ZCIncrementalMerkleTree checktree; + BOOST_CHECK(cache1.GetAnchorAt(tree.root(), checktree)); + BOOST_CHECK(checktree.root() == tree.root()); + } + } + + // Previously incorrect behavior + { + CCoinsViewTest base; + CCoinsViewCacheTest cache1(&base); + + // Create dummy anchor/commitment + ZCIncrementalMerkleTree tree; + uint256 cm = GetRandHash(); + tree.append(cm); + + // Add the anchor and flush to disk + cache1.PushAnchor(tree); + cache1.Flush(); + + // Remove the anchor, but don't flush yet! + cache1.PopAnchor(ZCIncrementalMerkleTree::empty_root()); + + { + CCoinsViewCacheTest cache2(&cache1); // Build cache on top + cache2.PushAnchor(tree); // Put the same anchor back! + cache2.Flush(); // Flush to cache1 + } + + // cache2's flush kinda worked, i.e. cache1 thinks the + // tree is there, but it didn't bring down the correct + // treestate... + { + ZCIncrementalMerkleTree checktree; + BOOST_CHECK(cache1.GetAnchorAt(tree.root(), checktree)); + BOOST_CHECK(checktree.root() == tree.root()); // Oh, shucks. + } + + // Flushing cache won't help either, just makes the inconsistency + // permanent. + cache1.Flush(); + { + ZCIncrementalMerkleTree checktree; + BOOST_CHECK(cache1.GetAnchorAt(tree.root(), checktree)); + BOOST_CHECK(checktree.root() == tree.root()); // Oh, shucks. + } + } +} + BOOST_AUTO_TEST_CASE(anchor_regression_test) { // Correct behavior: