|
|
@ -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; |
|
|
|
} |
|
|
|