Cleanup();
return true;
}
-
+bool CCoinsView::GetAnchorAt(const uint256 &rt, ZCIncrementalMerkleTree &tree) const { return false; }
+bool CCoinsView::GetNullifier(const uint256 &nullifier) 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,
+ CNullifiersMap &mapNullifiers) { return false; }
bool CCoinsView::GetStats(CCoinsStats &stats) const { return false; }
CCoinsViewBacked::CCoinsViewBacked(CCoinsView *viewIn) : base(viewIn) { }
+
+bool CCoinsViewBacked::GetAnchorAt(const uint256 &rt, ZCIncrementalMerkleTree &tree) const { return base->GetAnchorAt(rt, tree); }
+bool CCoinsViewBacked::GetNullifier(const uint256 &nullifier) const { return base->GetNullifier(nullifier); }
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,
+ CNullifiersMap &mapNullifiers) { return base->BatchWrite(mapCoins, hashBlock, hashAnchor, mapAnchors, mapNullifiers); }
bool CCoinsViewBacked::GetStats(CCoinsStats &stats) const { return base->GetStats(stats); }
CCoinsKeyHasher::CCoinsKeyHasher() : salt(GetRandHash()) {}
}
size_t CCoinsViewCache::DynamicMemoryUsage() const {
- return memusage::DynamicUsage(cacheCoins) + cachedCoinsUsage;
+ return memusage::DynamicUsage(cacheCoins) +
+ memusage::DynamicUsage(cacheAnchors) +
+ memusage::DynamicUsage(cacheNullifiers) +
+ cachedCoinsUsage;
}
CCoinsMap::const_iterator CCoinsViewCache::FetchCoins(const uint256 &txid) const {
return ret;
}
+
+bool CCoinsViewCache::GetAnchorAt(const uint256 &rt, ZCIncrementalMerkleTree &tree) const {
+ CAnchorsMap::const_iterator it = cacheAnchors.find(rt);
+ if (it != cacheAnchors.end()) {
+ if (it->second.entered) {
+ tree = it->second.tree;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ if (!base->GetAnchorAt(rt, tree)) {
+ return false;
+ }
+
+ CAnchorsMap::iterator ret = cacheAnchors.insert(std::make_pair(rt, CAnchorsCacheEntry())).first;
+ ret->second.entered = true;
+ ret->second.tree = tree;
+ cachedCoinsUsage += memusage::DynamicUsage(ret->second.tree);
+
+ return true;
+}
+
+bool CCoinsViewCache::GetNullifier(const uint256 &nullifier) const {
+ CNullifiersMap::iterator it = cacheNullifiers.find(nullifier);
+ if (it != cacheNullifiers.end())
+ return it->second.entered;
+
+ CNullifiersCacheEntry entry;
+ bool tmp = base->GetNullifier(nullifier);
+ entry.entered = tmp;
+
+ cacheNullifiers.insert(std::make_pair(nullifier, entry));
+
+ return tmp;
+}
+
+void CCoinsViewCache::PushAnchor(const ZCIncrementalMerkleTree &tree) {
+ uint256 newrt = tree.root();
+
+ 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 joinsplits. We could get around this a
+ // different way (make all blocks modify mapAnchors somehow)
+ // but this is simpler to reason about.
+ if (currentRoot != newrt) {
+ auto insertRet = cacheAnchors.insert(std::make_pair(newrt, CAnchorsCacheEntry()));
+ CAnchorsMap::iterator ret = insertRet.first;
+
+ ret->second.entered = true;
+ ret->second.tree = tree;
+ ret->second.flags = CAnchorsCacheEntry::DIRTY;
+
+ if (insertRet.second) {
+ // An insert took place
+ cachedCoinsUsage += memusage::DynamicUsage(ret->second.tree);
+ }
+
+ 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;
+ }
+}
+
+void CCoinsViewCache::SetNullifier(const uint256 &nullifier, bool spent) {
+ std::pair<CNullifiersMap::iterator, bool> ret = cacheNullifiers.insert(std::make_pair(nullifier, CNullifiersCacheEntry()));
+ ret.first->second.entered = spent;
+ ret.first->second.flags |= CNullifiersCacheEntry::DIRTY;
+}
+
bool CCoinsViewCache::GetCoins(const uint256 &txid, CCoins &coins) const {
CCoinsMap::const_iterator it = FetchCoins(txid);
if (it != cacheCoins.end()) {
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,
+ CNullifiersMap &mapNullifiers) {
assert(!hasModifier);
for (CCoinsMap::iterator it = mapCoins.begin(); it != mapCoins.end();) {
if (it->second.flags & CCoinsCacheEntry::DIRTY) { // Ignore non-dirty entries (optimization).
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 = child_it->second.tree;
+ entry.flags = CAnchorsCacheEntry::DIRTY;
+
+ cachedCoinsUsage += memusage::DynamicUsage(entry.tree);
+ }
+ } 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);
+ }
+
+ for (CNullifiersMap::iterator child_it = mapNullifiers.begin(); child_it != mapNullifiers.end();)
+ {
+ if (child_it->second.flags & CNullifiersCacheEntry::DIRTY) { // Ignore non-dirty entries (optimization).
+ 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;
+ }
+ } else {
+ if (parent_it->second.entered != child_it->second.entered) {
+ parent_it->second.entered = child_it->second.entered;
+ parent_it->second.flags |= CNullifiersCacheEntry::DIRTY;
+ }
+ }
+ }
+ CNullifiersMap::iterator itOld = child_it++;
+ mapNullifiers.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, cacheNullifiers);
cacheCoins.clear();
+ cacheAnchors.clear();
+ cacheNullifiers.clear();
cachedCoinsUsage = 0;
return fOk;
}
return coins->vout[input.prevout.n];
}
-CAmount CCoinsViewCache::GetValueIn(const CTransaction& tx) const
+const CScript &CCoinsViewCache::GetSpendFor(const CTxIn& input) const
{
- if (tx.IsCoinBase())
- return 0;
+ const CCoins* coins = AccessCoins(input.prevout.hash);
+ assert(coins);
+ return coins->vout[input.prevout.n].scriptPubKey;
+}
+
+uint64_t komodo_interest(int32_t txheight,uint64_t nValue,uint32_t nLockTime,uint32_t tiptime);
+extern char ASSETCHAINS_SYMBOL[16];
- CAmount nResult = 0;
+CAmount CCoinsViewCache::GetValueIn(int32_t nHeight,int64_t *interestp,const CTransaction& tx,uint32_t tiptime) const
+{
+ uint32_t timestamp,minutes; int64_t interest;
+ *interestp = 0;
+ if ( tx.IsCoinBase() != 0 )
+ return 0;
+ CAmount value,nResult = 0;
for (unsigned int i = 0; i < tx.vin.size(); i++)
- nResult += GetOutputFor(tx.vin[i]).nValue;
+ {
+ value = GetOutputFor(tx.vin[i]).nValue;
+ nResult += value;
+ interest = komodo_interest(nHeight,value,tx.nLockTime,tiptime);
+#ifdef KOMODO_ENABLE_INTEREST
+ if ( ASSETCHAINS_SYMBOL[0] == 0 && nHeight >= 60000 )
+ {
+ printf("nResult %.8f += interest %.8f ht.%d lock.%u tip.%u\n",(double)nResult/COIN,(double)interest/COIN,nHeight,tx.nLockTime,tiptime);
+ nResult += interest;
+ }
+#endif
+ (*interestp) += interest;
+ }
+ nResult += tx.GetJoinSplitValueIn();
return nResult;
}
+bool CCoinsViewCache::HaveJoinSplitRequirements(const CTransaction& tx) const
+{
+ boost::unordered_map<uint256, ZCIncrementalMerkleTree, CCoinsKeyHasher> intermediates;
+
+ BOOST_FOREACH(const JSDescription &joinsplit, tx.vjoinsplit)
+ {
+ BOOST_FOREACH(const uint256& nullifier, joinsplit.nullifiers)
+ {
+ if (GetNullifier(nullifier)) {
+ // If the nullifier is set, this transaction
+ // double-spends!
+ return false;
+ }
+ }
+
+ ZCIncrementalMerkleTree tree;
+ auto it = intermediates.find(joinsplit.anchor);
+ if (it != intermediates.end()) {
+ tree = it->second;
+ } else if (!GetAnchorAt(joinsplit.anchor, tree)) {
+ return false;
+ }
+
+ BOOST_FOREACH(const uint256& commitment, joinsplit.commitments)
+ {
+ tree.append(commitment);
+ }
+
+ intermediates.insert(std::make_pair(tree.root(), tree));
+ }
+
+ return true;
+}
+
bool CCoinsViewCache::HaveInputs(const CTransaction& tx) const
{
if (!tx.IsCoinBase()) {