]> Git Repo - VerusCoin.git/blobdiff - src/coins.cpp
test
[VerusCoin.git] / src / coins.cpp
index a41d5a310d6b9cb9b9e0537c2b2c3a4f96fbd866..3388f7439a1586f87724c5b10078c3fdaeb2e332 100644 (file)
@@ -40,20 +40,34 @@ bool CCoins::Spend(uint32_t nPos)
     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()) {}
@@ -66,7 +80,10 @@ CCoinsViewCache::~CCoinsViewCache()
 }
 
 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 {
@@ -87,6 +104,93 @@ 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()) {
@@ -141,11 +245,22 @@ 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,
+                                 CNullifiersMap &mapNullifiers) {
     assert(!hasModifier);
     for (CCoinsMap::iterator it = mapCoins.begin(); it != mapCoins.end();) {
         if (it->second.flags & CCoinsCacheEntry::DIRTY) { // Ignore non-dirty entries (optimization).
@@ -181,13 +296,71 @@ 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 = 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;
 }
@@ -203,18 +376,76 @@ const CTxOut &CCoinsViewCache::GetOutputFor(const CTxIn& input) const
     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()) {
This page took 0.032484 seconds and 4 git commands to generate.