Add mapAnchors infrastructure to CCoinsView.
This adds the TXDB/CCoinsViewCache primitives necessary for writing consensus rules for mapAnchors later.
This commit is contained in:
125
src/coins.cpp
125
src/coins.cpp
@@ -40,20 +40,30 @@ bool CCoins::Spend(uint32_t nPos)
|
||||
Cleanup();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CCoinsView::GetAnchorAt(const uint256 &rt, libzerocash::IncrementalMerkleTree &tree) const { return false; }
|
||||
bool CCoinsView::GetCoins(const uint256 &txid, CCoins &coins) const { return false; }
|
||||
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; }
|
||||
uint256 CCoinsView::GetBestAnchor() const { return uint256(); };
|
||||
bool CCoinsView::BatchWrite(CCoinsMap &mapCoins,
|
||||
const uint256 &hashBlock,
|
||||
const uint256 &hashAnchor,
|
||||
CAnchorsMap &mapAnchors) { return false; }
|
||||
bool CCoinsView::GetStats(CCoinsStats &stats) const { return false; }
|
||||
|
||||
|
||||
CCoinsViewBacked::CCoinsViewBacked(CCoinsView *viewIn) : base(viewIn) { }
|
||||
|
||||
bool CCoinsViewBacked::GetAnchorAt(const uint256 &rt, libzerocash::IncrementalMerkleTree &tree) const { return base->GetAnchorAt(rt, tree); }
|
||||
bool CCoinsViewBacked::GetCoins(const uint256 &txid, CCoins &coins) const { return base->GetCoins(txid, coins); }
|
||||
bool CCoinsViewBacked::HaveCoins(const uint256 &txid) const { return base->HaveCoins(txid); }
|
||||
uint256 CCoinsViewBacked::GetBestBlock() const { return base->GetBestBlock(); }
|
||||
uint256 CCoinsViewBacked::GetBestAnchor() const { return base->GetBestAnchor(); }
|
||||
void CCoinsViewBacked::SetBackend(CCoinsView &viewIn) { base = &viewIn; }
|
||||
bool CCoinsViewBacked::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) { return base->BatchWrite(mapCoins, hashBlock); }
|
||||
bool CCoinsViewBacked::BatchWrite(CCoinsMap &mapCoins,
|
||||
const uint256 &hashBlock,
|
||||
const uint256 &hashAnchor,
|
||||
CAnchorsMap &mapAnchors) { return base->BatchWrite(mapCoins, hashBlock, hashAnchor, mapAnchors); }
|
||||
bool CCoinsViewBacked::GetStats(CCoinsStats &stats) const { return base->GetStats(stats); }
|
||||
|
||||
CCoinsKeyHasher::CCoinsKeyHasher() : salt(GetRandHash()) {}
|
||||
@@ -87,6 +97,70 @@ CCoinsMap::const_iterator CCoinsViewCache::FetchCoins(const uint256 &txid) const
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
bool CCoinsViewCache::GetAnchorAt(const uint256 &rt, libzerocash::IncrementalMerkleTree &tree) const {
|
||||
CAnchorsMap::const_iterator it = cacheAnchors.find(rt);
|
||||
if (it != cacheAnchors.end()) {
|
||||
if (it->second.entered) {
|
||||
tree.setTo(it->second.tree);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
CAnchorsCacheEntry entry;
|
||||
if (!base->GetAnchorAt(rt, tree)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
entry.entered = true;
|
||||
entry.tree.setTo(tree);
|
||||
cacheAnchors.insert(std::make_pair(rt, entry));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void CCoinsViewCache::PushAnchor(const libzerocash::IncrementalMerkleTree &tree) {
|
||||
std::vector<unsigned char> newrt_v(32);
|
||||
tree.getRootValue(newrt_v);
|
||||
uint256 newrt(newrt_v);
|
||||
|
||||
auto currentRoot = GetBestAnchor();
|
||||
|
||||
// We don't want to overwrite an anchor we already have.
|
||||
// This occurs when a block doesn't modify mapAnchors at all,
|
||||
// because there are no pours. We could get around this a
|
||||
// different way (make all blocks modify mapAnchors somehow)
|
||||
// but this is simpler to reason about.
|
||||
if (currentRoot != newrt) {
|
||||
CAnchorsMap::iterator ret = cacheAnchors.insert(std::make_pair(newrt, CAnchorsCacheEntry())).first;
|
||||
|
||||
ret->second.entered = true;
|
||||
ret->second.tree.setTo(tree);
|
||||
ret->second.flags = CAnchorsCacheEntry::DIRTY;
|
||||
|
||||
hashAnchor = newrt;
|
||||
}
|
||||
}
|
||||
|
||||
void CCoinsViewCache::PopAnchor(const uint256 &newrt) {
|
||||
auto currentRoot = GetBestAnchor();
|
||||
|
||||
// Blocks might not change the commitment tree, in which
|
||||
// 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;
|
||||
|
||||
ret->second.entered = false;
|
||||
ret->second.flags = CAnchorsCacheEntry::DIRTY;
|
||||
|
||||
hashAnchor = newrt;
|
||||
}
|
||||
}
|
||||
|
||||
bool CCoinsViewCache::GetCoins(const uint256 &txid, CCoins &coins) const {
|
||||
CCoinsMap::const_iterator it = FetchCoins(txid);
|
||||
if (it != cacheCoins.end()) {
|
||||
@@ -141,11 +215,21 @@ uint256 CCoinsViewCache::GetBestBlock() const {
|
||||
return hashBlock;
|
||||
}
|
||||
|
||||
|
||||
uint256 CCoinsViewCache::GetBestAnchor() const {
|
||||
if (hashAnchor.IsNull())
|
||||
hashAnchor = base->GetBestAnchor();
|
||||
return hashAnchor;
|
||||
}
|
||||
|
||||
void CCoinsViewCache::SetBestBlock(const uint256 &hashBlockIn) {
|
||||
hashBlock = hashBlockIn;
|
||||
}
|
||||
|
||||
bool CCoinsViewCache::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlockIn) {
|
||||
bool CCoinsViewCache::BatchWrite(CCoinsMap &mapCoins,
|
||||
const uint256 &hashBlockIn,
|
||||
const uint256 &hashAnchorIn,
|
||||
CAnchorsMap &mapAnchors) {
|
||||
assert(!hasModifier);
|
||||
for (CCoinsMap::iterator it = mapCoins.begin(); it != mapCoins.end();) {
|
||||
if (it->second.flags & CCoinsCacheEntry::DIRTY) { // Ignore non-dirty entries (optimization).
|
||||
@@ -181,13 +265,44 @@ bool CCoinsViewCache::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlockIn
|
||||
CCoinsMap::iterator itOld = it++;
|
||||
mapCoins.erase(itOld);
|
||||
}
|
||||
|
||||
for (CAnchorsMap::iterator child_it = mapAnchors.begin(); child_it != mapAnchors.end();)
|
||||
{
|
||||
if (child_it->second.flags & CAnchorsCacheEntry::DIRTY) {
|
||||
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 = true;
|
||||
entry.tree.setTo(child_it->second.tree);
|
||||
entry.flags = CAnchorsCacheEntry::DIRTY;
|
||||
|
||||
// TODO: cache usage
|
||||
}
|
||||
} else {
|
||||
if (parent_it->second.entered != child_it->second.entered) {
|
||||
// The parent may have removed the entry.
|
||||
parent_it->second.entered = child_it->second.entered;
|
||||
parent_it->second.flags |= CAnchorsCacheEntry::DIRTY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CAnchorsMap::iterator itOld = child_it++;
|
||||
mapAnchors.erase(itOld);
|
||||
}
|
||||
hashAnchor = hashAnchorIn;
|
||||
hashBlock = hashBlockIn;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CCoinsViewCache::Flush() {
|
||||
bool fOk = base->BatchWrite(cacheCoins, hashBlock);
|
||||
bool fOk = base->BatchWrite(cacheCoins, hashBlock, hashAnchor, cacheAnchors);
|
||||
cacheCoins.clear();
|
||||
cacheAnchors.clear();
|
||||
cachedCoinsUsage = 0;
|
||||
return fOk;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user