diff --git a/src/claimtrie.cpp b/src/claimtrie.cpp index 334544859f..f4b1ccd986 100644 --- a/src/claimtrie.cpp +++ b/src/claimtrie.cpp @@ -403,28 +403,6 @@ CAmount CClaimTrie::getTotalValueOfClaimsRecursive(const CClaimTrieNode* current return value_in_subtrie; } -bool CClaimTrie::recursiveFlattenTrie(const std::string& name, const CClaimTrieNode* current, std::vector& nodes) const -{ - namedNodeType node(name, *current); - nodes.push_back(node); - for (nodeMapType::const_iterator it = current->children.begin(); it != current->children.end(); ++it) - { - std::stringstream ss; - ss << name << it->first; - if (!recursiveFlattenTrie(ss.str(), it->second, nodes)) - return false; - } - return true; -} - -std::vector CClaimTrie::flattenTrie() const -{ - std::vector nodes; - if (!recursiveFlattenTrie("", &root, nodes)) - LogPrintf("%s: Something went wrong flattening the trie", __func__); - return nodes; -} - const CClaimTrieNode* CClaimTrie::getNodeForName(const std::string& name) const { const CClaimTrieNode* current = &root; @@ -2548,25 +2526,32 @@ uint256 CClaimTrieCache::getLeafHashForProof(const std::string& currentPosition, } } -void CClaimTrieCache::recursiveFlattenTrie(const std::string& name, const CClaimTrieNode* current, std::vector& nodes) const +void CClaimTrieCache::recursiveIterateTrie(std::string& name, const CClaimTrieNode* current, CNodeCallback& callback) const { - nodes.push_back(std::make_pair(name, *current)); + callback.visit(name, current); + nodeCacheType::const_iterator cachedNode; for (nodeMapType::const_iterator it = current->children.begin(); it != current->children.end(); ++it) { - const std::string str = name + char(it->first); - cachedNode = cache.find(str); + name.push_back(it->first); + cachedNode = cache.find(name); if (cachedNode != cache.end()) - recursiveFlattenTrie(str, cachedNode->second, nodes); + recursiveIterateTrie(name, cachedNode->second, callback); else - recursiveFlattenTrie(str, it->second, nodes); + recursiveIterateTrie(name, it->second, callback); + name.erase(name.end() - 1); } } -std::vector CClaimTrieCache::flattenTrie() const +bool CClaimTrieCache::iterateTrie(CNodeCallback& callback) const { - std::vector nodes; - recursiveFlattenTrie("", getRoot(), nodes); - return nodes; + try { + std::string name; + recursiveIterateTrie(name, getRoot(), callback); + assert(name.empty()); + } catch (const CNodeCallback::CRecursionInterruptionException& ex) { + return ex.success; + } + return true; } claimsForNameType CClaimTrieCache::getClaimsForName(const std::string& name) const diff --git a/src/claimtrie.h b/src/claimtrie.h index 9d75a5d221..d192cf6f6f 100644 --- a/src/claimtrie.h +++ b/src/claimtrie.h @@ -132,8 +132,6 @@ typedef std::vector supportMapEntryType; typedef std::map nodeMapType; -typedef std::pair namedNodeType; - class CClaimTrieNode { public: @@ -317,7 +315,6 @@ class CClaimTrie bool WriteToDisk(); bool ReadFromDisk(bool check = false); - std::vector flattenTrie() const; bool getInfoForName(const std::string& name, CClaimValue& claim) const; bool getLastTakeoverForName(const std::string& name, int& lastTakeoverHeight) const; @@ -392,9 +389,6 @@ class CClaimTrie unsigned int getTotalClaimsRecursive(const CClaimTrieNode* current) const; CAmount getTotalValueOfClaimsRecursive(const CClaimTrieNode* current, bool fControllingOnly) const; - bool recursiveFlattenTrie(const std::string& name, - const CClaimTrieNode* current, - std::vector& nodes) const; void markNodeDirty(const std::string& name, CClaimTrieNode* node); void updateQueueRow(int nHeight, claimQueueRowType& row); @@ -459,6 +453,27 @@ class CClaimTrieProof int nHeightOfLastTakeover; }; +struct CNodeCallback { + struct CRecursionInterruptionException : public std::exception { + const bool success; + explicit CRecursionInterruptionException(bool success) : success(success) {} + }; + + virtual ~CNodeCallback() + { + } + + /** + * Callback to be called on every trie node + * @param[in] name full name of the node + * @param[in] node pointer to node itself + * + * To breakout early throw an exception. + * Throwing CRecursionInterruptionException will allow you to set the return value of iterateTrie. + */ + virtual void visit(const std::string& name, const CClaimTrieNode* node) = 0; +}; + class CClaimTrieCache { public: @@ -533,7 +548,7 @@ class CClaimTrieCache bool forkForExpirationChange(bool increment) const; - std::vector flattenTrie() const; + bool iterateTrie(CNodeCallback& callback) const; claimsForNameType getClaimsForName(const std::string& name) const; @@ -635,7 +650,7 @@ class CClaimTrieCache int getNumBlocksOfContinuousOwnership(const std::string& name) const; - void recursiveFlattenTrie(const std::string& name, const CClaimTrieNode* current, std::vector& nodes) const; + void recursiveIterateTrie(std::string& name, const CClaimTrieNode* current, CNodeCallback& callback) const; const CClaimTrieNode* getNodeForName(const std::string& name) const; }; diff --git a/src/rpc/claimtrie.cpp b/src/rpc/claimtrie.cpp index 8e391d3565..dc66cefa29 100644 --- a/src/rpc/claimtrie.cpp +++ b/src/rpc/claimtrie.cpp @@ -112,44 +112,65 @@ UniValue getclaimsintrie(const UniValue& params, bool fHelp) RollBackTo(blockIndex, coinsCache, trieCache); } - UniValue ret(UniValue::VARR); - std::vector nodes = trieCache.flattenTrie(); - for (std::vector::iterator it = nodes.begin(); it != nodes.end(); ++it) { - if (it->second.claims.empty()) continue; - - UniValue claims(UniValue::VARR); - for (std::vector::iterator itClaims = it->second.claims.begin(); itClaims != it->second.claims.end(); ++itClaims) { - UniValue claim(UniValue::VOBJ); - claim.push_back(Pair("claimId", itClaims->claimId.GetHex())); - claim.push_back(Pair("txid", itClaims->outPoint.hash.GetHex())); - claim.push_back(Pair("n", (int)itClaims->outPoint.n)); - claim.push_back(Pair("amount", ValueFromAmount(itClaims->nAmount))); - claim.push_back(Pair("height", itClaims->nHeight)); - const CCoins* coin = coinsCache.AccessCoins(itClaims->outPoint.hash); - if (!coin) { - LogPrintf("%s: %s does not exist in the coins view, despite being associated with a name\n", - __func__, itClaims->outPoint.hash.GetHex()); - claim.push_back(Pair("error", "No value found for claim")); - } else if (!coin->IsAvailable(itClaims->outPoint.n)) { - LogPrintf("%s: the specified txout of %s appears to have been spent\n", __func__, itClaims->outPoint.hash.GetHex()); - claim.push_back(Pair("error", "Txout spent")); - } else { - int op; - std::vector > vvchParams; - if (!DecodeClaimScript(coin->vout[itClaims->outPoint.n].scriptPubKey, op, vvchParams)) { - LogPrintf("%s: the specified txout of %s does not have an claim command\n", __func__, itClaims->outPoint.hash.GetHex()); + class CClaimsCallback : public CNodeCallback + { + public: + CClaimsCallback(UniValue& ret, const CCoinsViewCache& coinsCache) : nodes(ret), coinsCache(coinsCache) + { + } + + void visit(const std::string& name, const CClaimTrieNode* node) + { + if (ShutdownRequested()) + throw JSONRPCError(RPC_INTERNAL_ERROR, "Shutdown requested"); + + boost::this_thread::interruption_point(); + + if (node->claims.empty()) + return; + + UniValue claims(UniValue::VARR); + for (std::vector::const_iterator itClaims = node->claims.begin(); itClaims != node->claims.end(); ++itClaims) { + UniValue claim(UniValue::VOBJ); + claim.push_back(Pair("claimId", itClaims->claimId.GetHex())); + claim.push_back(Pair("txid", itClaims->outPoint.hash.GetHex())); + claim.push_back(Pair("n", (int)itClaims->outPoint.n)); + claim.push_back(Pair("amount", ::ValueFromAmount(itClaims->nAmount))); + claim.push_back(Pair("height", itClaims->nHeight)); + const CCoins* coin = coinsCache.AccessCoins(itClaims->outPoint.hash); + if (!coin) { + LogPrintf("%s: %s does not exist in the coins view, despite being associated with a name\n", + __func__, itClaims->outPoint.hash.GetHex()); + claim.push_back(Pair("error", "No value found for claim")); + } else if (!coin->IsAvailable(itClaims->outPoint.n)) { + LogPrintf("%s: the specified txout of %s appears to have been spent\n", __func__, itClaims->outPoint.hash.GetHex()); + claim.push_back(Pair("error", "Txout spent")); + } else { + int op; + std::vector > vvchParams; + if (!DecodeClaimScript(coin->vout[itClaims->outPoint.n].scriptPubKey, op, vvchParams)) { + LogPrintf("%s: the specified txout of %s does not have an claim command\n", __func__, itClaims->outPoint.hash.GetHex()); + } + std::string sValue(vvchParams[1].begin(), vvchParams[1].end()); + claim.push_back(Pair("value", sValue)); } - std::string sValue(vvchParams[1].begin(), vvchParams[1].end()); - claim.push_back(Pair("value", sValue)); + claims.push_back(claim); } - claims.push_back(claim); + + UniValue nodeObj(UniValue::VOBJ); + nodeObj.push_back(Pair("name", name)); + nodeObj.push_back(Pair("claims", claims)); + nodes.push_back(nodeObj); } - UniValue node(UniValue::VOBJ); - node.push_back(Pair("name", it->first)); - node.push_back(Pair("claims", claims)); - ret.push_back(node); - } + private: + UniValue& nodes; + const CCoinsViewCache& coinsCache; + }; + + UniValue ret(UniValue::VARR); + CClaimsCallback claimsCallback(ret, coinsCache); + trieCache.iterateTrie(claimsCallback); return ret; } @@ -188,23 +209,40 @@ UniValue getclaimtrie(const UniValue& params, bool fHelp) RollBackTo(blockIndex, coinsCache, trieCache); } - UniValue ret(UniValue::VARR); - std::vector nodes = trieCache.flattenTrie(); - for (std::vector::iterator it = nodes.begin(); it != nodes.end(); ++it) + class CClaimCallback : public CNodeCallback { - UniValue node(UniValue::VOBJ); - node.push_back(Pair("name", it->first)); - node.push_back(Pair("hash", it->second.hash.GetHex())); - CClaimValue claim; - if (it->second.getBestClaim(claim)) + public: + CClaimCallback(UniValue& ret) : nodes(ret) { - node.push_back(Pair("txid", claim.outPoint.hash.GetHex())); - node.push_back(Pair("n", (int)claim.outPoint.n)); - node.push_back(Pair("value", ValueFromAmount(claim.nAmount))); - node.push_back(Pair("height", claim.nHeight)); } - ret.push_back(node); - } + + void visit(const std::string& name, const CClaimTrieNode* node) + { + if (ShutdownRequested()) + throw JSONRPCError(RPC_INTERNAL_ERROR, "Shutdown requested"); + + boost::this_thread::interruption_point(); + + UniValue nodeObj(UniValue::VOBJ); + nodeObj.push_back(Pair("name", name)); + nodeObj.push_back(Pair("hash", node->hash.GetHex())); + CClaimValue claim; + if (node->getBestClaim(claim)) { + nodeObj.push_back(Pair("txid", claim.outPoint.hash.GetHex())); + nodeObj.push_back(Pair("n", (int)claim.outPoint.n)); + nodeObj.push_back(Pair("value", ::ValueFromAmount(claim.nAmount))); + nodeObj.push_back(Pair("height", claim.nHeight)); + } + nodes.push_back(nodeObj); + } + + private: + UniValue& nodes; + }; + + UniValue ret(UniValue::VARR); + CClaimCallback claimCallback(ret); + trieCache.iterateTrie(claimCallback); return ret; } diff --git a/src/test/claimtriecache_tests.cpp b/src/test/claimtriecache_tests.cpp index 52d0e8fc67..aa06fd19e8 100644 --- a/src/test/claimtriecache_tests.cpp +++ b/src/test/claimtriecache_tests.cpp @@ -274,5 +274,58 @@ BOOST_AUTO_TEST_CASE(recursive_prune_test) BOOST_CHECK_EQUAL(0, it->second->children.size()); } +BOOST_AUTO_TEST_CASE(iteratetrie_test) +{ + BOOST_CHECK(pclaimTrie->empty()); + CClaimTrieCacheTest ctc(pclaimTrie); + + uint256 hash0(uint256S("0000000000000000000000000000000000000000000000000000000000000001")); + CMutableTransaction tx1 = BuildTransaction(hash0); + + const uint256 txhash = tx1.GetHash(); + CClaimValue claimVal(COutPoint(txhash, 0), ClaimIdHash(txhash, 0), CAmount(10), 0, 0); + ctc.insertClaimIntoTrie("test", claimVal); + + + int count = 0; + + struct TestCallBack : public CNodeCallback { + TestCallBack(int& count) : count(count) + { + } + + void visit(const std::string& name, const CClaimTrieNode* node) + { + count++; + if (name == "test") { + BOOST_CHECK(node->claims.size() == 1); + } + } + + int& count; + } testCallback(count); + + BOOST_CHECK(ctc.iterateTrie(testCallback)); + BOOST_CHECK(count == 5); + + count = 3; + + struct TestCallBack2 : public CNodeCallback { + TestCallBack2(int& count) : count(count) + { + } + + void visit(const std::string& name, const CClaimTrieNode* node) + { + if (--count <= 0) + throw CRecursionInterruptionException(false); + } + + int& count; + } testCallback2(count); + + BOOST_CHECK(!ctc.iterateTrie(testCallback2)); + BOOST_CHECK(count == 0); +} BOOST_AUTO_TEST_SUITE_END()