Auto merge of #1928 - ebfull:fix-anchor-cache-bug, r=str4d
Fix anchor cache bug Fixes #1912. If an anchor is removed from the cache, but didn't exist in it beforehand, it will insert a blank tree. If it's reinserted in a child cache, when the child cache flushes it will mark the treestate as entered but won't bring the valid tree with it. Thankfully, we assert when connecting blocks so that this inconsistency won't cause us to build on a blank tree after a reorg.
This commit is contained in:
commit
cdbd851417
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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:
|
||||
|
|
Loading…
Reference in New Issue