diff --git a/src/coins.cpp b/src/coins.cpp index d60269962..c861bb81e 100644 --- a/src/coins.cpp +++ b/src/coins.cpp @@ -303,16 +303,12 @@ bool CCoinsViewCache::BatchWrite(CCoinsMap &mapCoins, CAnchorsMap::iterator parent_it = cacheAnchors.find(child_it->first); if (parent_it == cacheAnchors.end()) { - if (child_it->second.entered) { - // Parent doesn't have an entry, but child has a new commitment root. + CAnchorsCacheEntry& entry = cacheAnchors[child_it->first]; + entry.entered = child_it->second.entered; + entry.tree = child_it->second.tree; + entry.flags = CAnchorsCacheEntry::DIRTY; - CAnchorsCacheEntry& entry = cacheAnchors[child_it->first]; - entry.entered = true; - entry.tree = child_it->second.tree; - entry.flags = CAnchorsCacheEntry::DIRTY; - - cachedCoinsUsage += memusage::DynamicUsage(entry.tree); - } + cachedCoinsUsage += memusage::DynamicUsage(entry.tree); } else { if (parent_it->second.entered != child_it->second.entered) { // The parent may have removed the entry. @@ -332,14 +328,9 @@ bool CCoinsViewCache::BatchWrite(CCoinsMap &mapCoins, CNullifiersMap::iterator parent_it = cacheNullifiers.find(child_it->first); if (parent_it == cacheNullifiers.end()) { - if (child_it->second.entered) { - // Parent doesn't have an entry, but child has a SPENT nullifier. - // Move the spent nullifier up. - - CNullifiersCacheEntry& entry = cacheNullifiers[child_it->first]; - entry.entered = true; - entry.flags = CNullifiersCacheEntry::DIRTY; - } + CNullifiersCacheEntry& entry = cacheNullifiers[child_it->first]; + entry.entered = child_it->second.entered; + entry.flags = CNullifiersCacheEntry::DIRTY; } else { if (parent_it->second.entered != child_it->second.entered) { parent_it->second.entered = child_it->second.entered; diff --git a/src/test/coins_tests.cpp b/src/test/coins_tests.cpp index 8f626d9ab..b638bccfc 100644 --- a/src/test/coins_tests.cpp +++ b/src/test/coins_tests.cpp @@ -166,6 +166,174 @@ uint256 appendRandomCommitment(ZCIncrementalMerkleTree &tree) BOOST_FIXTURE_TEST_SUITE(coins_tests, BasicTestingSetup) +BOOST_AUTO_TEST_CASE(nullifier_regression_test) +{ + // Correct behavior: + { + CCoinsViewTest base; + CCoinsViewCacheTest cache1(&base); + + // Insert a nullifier into the base. + uint256 nf = GetRandHash(); + cache1.SetNullifier(nf, true); + cache1.Flush(); // Flush to base. + + // Remove the nullifier from cache + cache1.SetNullifier(nf, false); + + // The nullifier now should be `false`. + BOOST_CHECK(!cache1.GetNullifier(nf)); + } + + // Also correct behavior: + { + CCoinsViewTest base; + CCoinsViewCacheTest cache1(&base); + + // Insert a nullifier into the base. + uint256 nf = GetRandHash(); + cache1.SetNullifier(nf, true); + cache1.Flush(); // Flush to base. + + // Remove the nullifier from cache + cache1.SetNullifier(nf, false); + cache1.Flush(); // Flush to base. + + // The nullifier now should be `false`. + BOOST_CHECK(!cache1.GetNullifier(nf)); + } + + // Works because we bring it from the parent cache: + { + CCoinsViewTest base; + CCoinsViewCacheTest cache1(&base); + + // Insert a nullifier into the base. + uint256 nf = GetRandHash(); + cache1.SetNullifier(nf, true); + cache1.Flush(); // Empties cache. + + // Create cache on top. + { + // Remove the nullifier. + CCoinsViewCacheTest cache2(&cache1); + BOOST_CHECK(cache2.GetNullifier(nf)); + cache2.SetNullifier(nf, false); + cache2.Flush(); // Empties cache, flushes to cache1. + } + + // The nullifier now should be `false`. + BOOST_CHECK(!cache1.GetNullifier(nf)); + } + + // Was broken: + { + CCoinsViewTest base; + CCoinsViewCacheTest cache1(&base); + + // Insert a nullifier into the base. + uint256 nf = GetRandHash(); + cache1.SetNullifier(nf, true); + cache1.Flush(); // Empties cache. + + // Create cache on top. + { + // Remove the nullifier. + CCoinsViewCacheTest cache2(&cache1); + cache2.SetNullifier(nf, false); + cache2.Flush(); // Empties cache, flushes to cache1. + } + + // The nullifier now should be `false`. + BOOST_CHECK(!cache1.GetNullifier(nf)); + } +} + +BOOST_AUTO_TEST_CASE(anchor_regression_test) +{ + // Correct behavior: + { + CCoinsViewTest base; + CCoinsViewCacheTest cache1(&base); + + // Insert anchor into base. + ZCIncrementalMerkleTree tree; + uint256 cm = GetRandHash(); + tree.append(cm); + cache1.PushAnchor(tree); + cache1.Flush(); + + cache1.PopAnchor(ZCIncrementalMerkleTree::empty_root()); + BOOST_CHECK(cache1.GetBestAnchor() == ZCIncrementalMerkleTree::empty_root()); + BOOST_CHECK(!cache1.GetAnchorAt(tree.root(), tree)); + } + + // Also correct behavior: + { + CCoinsViewTest base; + CCoinsViewCacheTest cache1(&base); + + // Insert anchor into base. + ZCIncrementalMerkleTree tree; + uint256 cm = GetRandHash(); + tree.append(cm); + cache1.PushAnchor(tree); + cache1.Flush(); + + cache1.PopAnchor(ZCIncrementalMerkleTree::empty_root()); + cache1.Flush(); + BOOST_CHECK(cache1.GetBestAnchor() == ZCIncrementalMerkleTree::empty_root()); + BOOST_CHECK(!cache1.GetAnchorAt(tree.root(), tree)); + } + + // Works because we bring the anchor in from parent cache. + { + CCoinsViewTest base; + CCoinsViewCacheTest cache1(&base); + + // Insert anchor into base. + ZCIncrementalMerkleTree tree; + uint256 cm = GetRandHash(); + tree.append(cm); + cache1.PushAnchor(tree); + cache1.Flush(); + + { + // Pop anchor. + CCoinsViewCacheTest cache2(&cache1); + BOOST_CHECK(cache2.GetAnchorAt(tree.root(), tree)); + cache2.PopAnchor(ZCIncrementalMerkleTree::empty_root()); + cache2.Flush(); + } + + BOOST_CHECK(cache1.GetBestAnchor() == ZCIncrementalMerkleTree::empty_root()); + BOOST_CHECK(!cache1.GetAnchorAt(tree.root(), tree)); + } + + // Was broken: + { + CCoinsViewTest base; + CCoinsViewCacheTest cache1(&base); + + // Insert anchor into base. + ZCIncrementalMerkleTree tree; + uint256 cm = GetRandHash(); + tree.append(cm); + cache1.PushAnchor(tree); + cache1.Flush(); + + { + // Pop anchor. + CCoinsViewCacheTest cache2(&cache1); + cache2.PopAnchor(ZCIncrementalMerkleTree::empty_root()); + cache2.Flush(); + } + + BOOST_CHECK(cache1.GetBestAnchor() == ZCIncrementalMerkleTree::empty_root()); + BOOST_CHECK(!cache1.GetAnchorAt(tree.root(), tree)); + } +} + BOOST_AUTO_TEST_CASE(nullifiers_test) { CCoinsViewTest base;