From 6a572c8307fade498787dc113e69a3537ca394ec Mon Sep 17 00:00:00 2001 From: Jimmy Kiselak Date: Tue, 2 Feb 2016 00:28:57 -0500 Subject: [PATCH 1/3] enable creating proofs for the claim trie --- src/ncctrie.cpp | 109 ++++++++++++++- src/ncctrie.h | 35 +++++ src/test/ncctrie_tests.cpp | 273 +++++++++++++++++++++++++++++++++++++ 3 files changed, 416 insertions(+), 1 deletion(-) diff --git a/src/ncctrie.cpp b/src/ncctrie.cpp index d49538a6e..d060e1985 100644 --- a/src/ncctrie.cpp +++ b/src/ncctrie.cpp @@ -3,7 +3,7 @@ #include -uint256 CNodeValue::GetHash() const +uint256 getOutPointHash(uint256 txhash, uint32_t nOut) { CHash256 valTxHasher; valTxHasher.Write(txhash.begin(), txhash.size()); @@ -28,6 +28,11 @@ uint256 CNodeValue::GetHash() const return valHash; } +uint256 CNodeValue::GetHash() const +{ + return getOutPointHash(txhash, nOut); +} + bool CNCCTrieNode::insertValue(CNodeValue val, bool * pfChanged) { LogPrintf("%s: Inserting %s:%d (amount: %d) into the ncc trie\n", __func__, val.txhash.ToString(), val.nOut, val.nAmount); @@ -1267,3 +1272,105 @@ bool CNCCTrieCache::flush() } return success; } + +std::pair CNCCTrieCache::getLeafNodeForProof(const std::string& currentPosition, unsigned char nodeChar, const CNCCTrieNode* currentNode) const +{ + std::stringstream leafPosition; + leafPosition << currentPosition << nodeChar; + hashMapType::iterator cachedHash = cacheHashes.find(leafPosition.str()); + if (cachedHash != cacheHashes.end()) + { + return std::make_pair(leafPosition.str(), CNCCTrieProofLeafNode(cachedHash->second)); + } + else + { + return std::make_pair(leafPosition.str(), CNCCTrieProofLeafNode(currentNode->hash)); + } +} + +CNCCTrieProof CNCCTrieCache::getProofForName(const std::string& name) const +{ + if (dirty()) + getMerkleHash(); + std::map nodes; + std::map leafNodes; + CNCCTrieNode* current = &(base->root); + nodeCacheType::const_iterator cachedNode; + CNodeValue nameVal; + bool fNameHasValue; + uint256 txhash; + uint32_t nOut; + for (std::string::const_iterator itName = name.begin(); itName != name.end(); ++itName) + { + std::string currentPosition(name.begin(), itName); + cachedNode = cache.find(currentPosition); + if (cachedNode != cache.end()) + current = cachedNode->second; + hashMapType::const_iterator cachedHash = cacheHashes.find(currentPosition); + uint256 nodeHash; + if (cachedHash != cacheHashes.end()) + nodeHash = cachedHash->second; + else + nodeHash = current->hash; + CNodeValue val; + bool fNodeHasValue = current->getBestValue(val); + uint256 valHash; + if (fNodeHasValue) + valHash = val.GetHash(); + std::vector child_chars; + CNCCTrieNode* nextCurrent = NULL; + for (nodeMapType::const_iterator itChildren = current->children.begin(); itChildren != current->children.end(); ++itChildren) + { + child_chars.push_back(itChildren->first); + if (itChildren->first != *itName) // Leaf node + { + std::pair leaf = getLeafNodeForProof(currentPosition, itChildren->first, itChildren->second); + leafNodes[leaf.first] = leaf.second; + } + else // Full node + { + nextCurrent = itChildren->second; + } + } + nodes[currentPosition] = CNCCTrieProofNode(child_chars, fNodeHasValue, valHash, nodeHash); + current = nextCurrent; + if (current == NULL) + break; + } + if (current != NULL) + { + cachedNode = cache.find(name); + if (cachedNode != cache.end()) + current = cachedNode->second; + hashMapType::const_iterator cachedHash = cacheHashes.find(name); + uint256 nodeHash; + if (cachedHash != cacheHashes.end()) + nodeHash = cachedHash->second; + else + nodeHash = current->hash; + fNameHasValue = current->getBestValue(nameVal); + uint256 valHash; + if (fNameHasValue) + { + txhash = nameVal.txhash; + nOut = nameVal.nOut; + valHash = nameVal.GetHash(); + } + else + nOut = 0; + std::vector child_chars; + for (nodeMapType::const_iterator itChildren = current->children.begin(); itChildren != current->children.end(); ++itChildren) + { + child_chars.push_back(itChildren->first); + std::pair leaf = getLeafNodeForProof(name, itChildren->first, itChildren->second); + leafNodes[leaf.first] = leaf.second; + } + nodes[name] = CNCCTrieProofNode(child_chars, fNameHasValue, valHash, nodeHash); + } + else + { + fNameHasValue = false; + nOut = 0; + } + return CNCCTrieProof(nodes, leafNodes, fNameHasValue, txhash, nOut); +} diff --git a/src/ncctrie.h b/src/ncctrie.h index b7f8ab468..47101effc 100644 --- a/src/ncctrie.h +++ b/src/ncctrie.h @@ -16,6 +16,8 @@ #define DEFAULT_DELAY 100 +uint256 getOutPointHash(uint256 txhash, uint32_t nOut); + class CNodeValue { public: @@ -199,6 +201,37 @@ private: void BatchWriteExpirationQueueRows(CLevelDBBatch& batch); }; +class CNCCTrieProofNode +{ +public: + CNCCTrieProofNode() {}; + CNCCTrieProofNode(std::vector children, bool hasValue, uint256 valHash, uint256 nodeHash) : children(children), hasValue(hasValue), valHash(valHash), nodeHash(nodeHash) {}; + std::vector children; + bool hasValue; + uint256 valHash; + uint256 nodeHash; +}; + +class CNCCTrieProofLeafNode +{ +public: + CNCCTrieProofLeafNode() {}; + CNCCTrieProofLeafNode(uint256 nodeHash) : nodeHash(nodeHash) {}; + uint256 nodeHash; +}; + +class CNCCTrieProof +{ +public: + CNCCTrieProof() {}; + CNCCTrieProof(std::map nodes, std::map leafNodes, bool hasValue, uint256 txhash, uint32_t nOut) : nodes(nodes), leafNodes(leafNodes), hasValue(hasValue), txhash(txhash), nOut(nOut) {} + std::map nodes; + std::map leafNodes; + bool hasValue; + uint256 txhash; + uint32_t nOut; +}; + class CNCCTrieCache { public: @@ -219,6 +252,7 @@ public: ~CNCCTrieCache() { clear(); } bool insertClaimIntoTrie(const std::string name, CNodeValue val) const; bool removeClaimFromTrie(const std::string name, uint256 txhash, uint32_t nOut, int& nValidAtHeight) const; + CNCCTrieProof getProofForName(const std::string& name) const; private: CNCCTrie* base; mutable nodeCacheType cache; @@ -239,6 +273,7 @@ private: void removeFromExpirationQueue(const std::string name, uint256 txhash, uint32_t nOut, int nHeight) const; valueQueueType::iterator getQueueCacheRow(int nHeight, bool createIfNotExists) const; valueQueueType::iterator getExpirationQueueCacheRow(int nHeight, bool createIfNotExists) const; + std::pair getLeafNodeForProof(const std::string& currentPosition, unsigned char nodeChar, const CNCCTrieNode* currentNode) const; uint256 hashBlock; }; diff --git a/src/test/ncctrie_tests.cpp b/src/test/ncctrie_tests.cpp index 847b69c46..015a130c7 100644 --- a/src/test/ncctrie_tests.cpp +++ b/src/test/ncctrie_tests.cpp @@ -163,6 +163,30 @@ const unsigned int expire_nonces[] = { 120057, 8823, 5430, 44579, 61385, 84720, 64857, 70478, 116285, 8904, 63423}; +const unsigned int proof_nonces[] = { + 54804, 229797, 119107, 77644, 9782, 104842, 72332, 47116, 363729, 170598, + 434, 48775, 15164, 51638, 63813, 21028, 31896, 117216, 26019, 7458, + 101082, 48571, 84174, 56983, 4607, 176131, 60681, 11348, 42867, 152045, + 162526, 48222, 9115, 162702, 7084, 49303, 34393, 67035, 72372, 81712, + 58846, 123668, 37484, 69474, 77333, 104992, 71743, 9675, 12363, 39457, + 29256, 190978, 55083, 171352, 82498, 94184, 110307, 25965, 53432, 193885, + 3633, 74876, 15957, 252915, 119231, 32149, 84494, 139388, 141968, 9092, + 216682, 4336, 62779, 24742, 46920, 249663, 223300, 64403, 128849, 56433, + 30665, 174236, 77179, 18440, 217216, 45037, 35402, 135703, 15338, 126459, + 14097, 21840, 1175, 37591, 82927, 37893, 121151, 54253, 15384, 2192, + 12708, 74849, 39787, 15685, 37774, 13139, 34776, 159887, 6814, 3021, + 53901, 24286, 110415, 72451, 130820, 111543, 1300, 98281, 48336, 220704, + 51457, 91510, 148670, 41904, 16121, 41743, 63992, 27520, 15825, 108299, + 107508, 49504, 31499, 132824, 96172, 71483, 26828, 2545, 29227, 7846, + 114426, 221520, 9779, 594, 284222, 40381, 19645, 38653, 41721, 55469, + 16191, 7360, 42980, 36694, 13660, 72041, 1305, 83146, 92665, 15203, + 25428, 45068, 281892, 168851, 27104, 42352, 4321, 65234, 32459, 4261, + 101678, 29995, 34940, 34278, 169292, 24555, 20685, 44997, 42753, 21555, + 5974, 30, 59247, 17323, 10938, 78807, 17244, 68852, 17954, 26818, + 131893, 87813, 31242, 86553, 119319, 20972, 142663, 73972, 35525, 4782, + 20213, 13208, 35272, 229212, 203438, +}; + BOOST_FIXTURE_TEST_SUITE(ncctrie_tests, TestingSetup) CMutableTransaction BuildTransaction(const uint256& prevhash) @@ -1394,4 +1418,253 @@ BOOST_AUTO_TEST_CASE(ncctrienode_serialize_unserialize) BOOST_CHECK(n1 == n2); } +bool verify_proof(CNCCTrieProof proof, uint256 rootHash, const std::string& name) +{ + for (std::string::const_iterator itName = name.begin(); itName != name.end(); ++itName) + { + std::string currentPosition(name.begin(), itName); + std::map::const_iterator itNode = proof.nodes.find(currentPosition); + if (itNode == proof.nodes.end()) + { + return currentPosition != "" && !proof.hasValue; + } + std::vector vchToHash; + for (std::vector::const_iterator itChildren = itNode->second.children.begin(); itChildren != itNode->second.children.end(); ++itChildren) + { + vchToHash.push_back(*itChildren); + std::stringstream nextPosition; + nextPosition << currentPosition << *itChildren; + if (*itChildren == *itName) + { + std::map::const_iterator itChildNode = proof.nodes.find(nextPosition.str()); + if (itChildNode == proof.nodes.end()) + { + return false; + } + vchToHash.insert(vchToHash.end(), itChildNode->second.nodeHash.begin(), itChildNode->second.nodeHash.end()); + } + else + { + std::map::const_iterator itChildNode = proof.leafNodes.find(nextPosition.str()); + if (itChildNode == proof.leafNodes.end()) + { + return false; + } + vchToHash.insert(vchToHash.end(), itChildNode->second.nodeHash.begin(), itChildNode->second.nodeHash.end()); + } + } + if (itNode->second.hasValue) + { + vchToHash.insert(vchToHash.end(), itNode->second.valHash.begin(), itNode->second.valHash.end()); + } + CHash256 hasher; + std::vector vchHash(hasher.OUTPUT_SIZE); + hasher.Write(vchToHash.data(), vchToHash.size()); + hasher.Finalize(&(vchHash[0])); + uint256 calculatedHash(vchHash); + if (calculatedHash != itNode->second.nodeHash) + { + return false; + } + if (currentPosition == "" && calculatedHash != rootHash) + { + return false; + } + } + std::map::const_iterator itNode = proof.nodes.find(name); + if (itNode == proof.nodes.end()) + { + return name != "" && !proof.hasValue; + } + std::vector vchToHash; + for (std::vector::const_iterator itChildren = itNode->second.children.begin(); itChildren != itNode->second.children.end(); ++itChildren) + { + vchToHash.push_back(*itChildren); + std::stringstream nextPosition; + nextPosition << name << *itChildren; + std::map::const_iterator itChildNode = proof.leafNodes.find(nextPosition.str()); + if (itChildNode == proof.leafNodes.end()) + { + return false; + } + vchToHash.insert(vchToHash.end(), itChildNode->second.nodeHash.begin(), itChildNode->second.nodeHash.end()); + } + if (itNode->second.hasValue != proof.hasValue) + { + return false; + } + if (itNode->second.hasValue) + { + uint256 valHash = getOutPointHash(proof.txhash, proof.nOut); + if (valHash != itNode->second.valHash) + { + return false; + } + vchToHash.insert(vchToHash.end(), valHash.begin(), valHash.end()); + } + CHash256 hasher; + std::vector vchHash(hasher.OUTPUT_SIZE); + hasher.Write(vchToHash.data(), vchToHash.size()); + hasher.Finalize(&(vchHash[0])); + uint256 calculatedHash(vchHash); + if (calculatedHash != itNode->second.nodeHash) + { + return false; + } + if (name == "" && calculatedHash != rootHash) + { + return false; + } + return true; +} + +BOOST_AUTO_TEST_CASE(ncctrievalue_proof) +{ + int block_counter = 0; + bool find_nonces = false; + BOOST_CHECK(pnccTrie->nCurrentHeight == chainActive.Height() + 1); + + CBlockTemplate *pblocktemplate; + LOCK(cs_main); + + CScript scriptPubKey = CScript() << OP_TRUE; + + std::string sName1("a"); + std::string sValue1("testa"); + + std::string sName2("abc"); + std::string sValue2("testabc"); + + std::string sName3("abd"); + std::string sValue3("testabd"); + + std::string sName4("zyx"); + std::string sValue4("testzyx"); + + std::string sName5("zyxa"); + std::string sName6("omg"); + std::string sName7(""); + + std::vector vchName1(sName1.begin(), sName1.end()); + std::vector vchValue1(sValue1.begin(), sValue1.end()); + + std::vector vchName2(sName2.begin(), sName2.end()); + std::vector vchValue2(sValue2.begin(), sValue2.end()); + + std::vector vchName3(sName3.begin(), sName3.end()); + std::vector vchValue3(sValue3.begin(), sValue3.end()); + + std::vector vchName4(sName4.begin(), sName4.end()); + std::vector vchValue4(sValue4.begin(), sValue4.end()); + + CNodeValue val; + + BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey)); + pblocktemplate->block.hashPrevBlock = chainActive.Tip()->GetBlockHash(); + std::vector coinbases; + for (unsigned int i = 0; i < 104; ++i) + { + BOOST_CHECK(CreateBlock(pblocktemplate, find_nonces, proof_nonces[block_counter++])); + if (coinbases.size() < 4) + coinbases.push_back(CTransaction(pblocktemplate->block.vtx[0])); + } + + delete pblocktemplate; + + CMutableTransaction tx1 = BuildTransaction(coinbases[0]); + tx1.vout[0].scriptPubKey = CScript() << OP_CLAIM_NAME << vchName1 << vchValue1 << OP_2DROP << OP_DROP << OP_TRUE; + CMutableTransaction tx2 = BuildTransaction(coinbases[1]); + tx2.vout[0].scriptPubKey = CScript() << OP_CLAIM_NAME << vchName2 << vchValue2 << OP_2DROP << OP_DROP << OP_TRUE; + CMutableTransaction tx3 = BuildTransaction(coinbases[2]); + tx3.vout[0].scriptPubKey = CScript() << OP_CLAIM_NAME << vchName3 << vchValue3 << OP_2DROP << OP_DROP << OP_TRUE; + CMutableTransaction tx4 = BuildTransaction(coinbases[3]); + tx4.vout[0].scriptPubKey = CScript() << OP_CLAIM_NAME << vchName4 << vchValue4 << OP_2DROP << OP_DROP << OP_TRUE; + + std::vector blocks_to_invalidate; + + // create a claim. verify the expiration event has been scheduled. + + AddToMempool(tx1); + AddToMempool(tx2); + AddToMempool(tx3); + AddToMempool(tx4); + + BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey)); + BOOST_CHECK(pblocktemplate->block.vtx.size() == 5); + pblocktemplate->block.hashPrevBlock = chainActive.Tip()->GetBlockHash(); + BOOST_CHECK(CreateBlock(pblocktemplate, find_nonces, proof_nonces[block_counter++])); + blocks_to_invalidate.push_back(pblocktemplate->block.hashPrevBlock); + delete pblocktemplate; + + BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey)); + pblocktemplate->block.hashPrevBlock = chainActive.Tip()->GetBlockHash(); + for (unsigned int i = 1; i < 100; ++i) + { + BOOST_CHECK(CreateBlock(pblocktemplate, find_nonces, proof_nonces[block_counter++])); + } + delete pblocktemplate; + + BOOST_CHECK(pnccTrie->empty()); + BOOST_CHECK(!pnccTrie->queueEmpty()); + + BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey)); + pblocktemplate->block.hashPrevBlock = chainActive.Tip()->GetBlockHash(); + BOOST_CHECK(CreateBlock(pblocktemplate, find_nonces, proof_nonces[block_counter++])); + delete pblocktemplate; + + BOOST_CHECK(!pnccTrie->empty()); + BOOST_CHECK(pnccTrie->queueEmpty()); + BOOST_CHECK(pnccTrie->getInfoForName(sName1, val)); + BOOST_CHECK(val.txhash == tx1.GetHash()); + BOOST_CHECK(pnccTrie->getInfoForName(sName2, val)); + BOOST_CHECK(val.txhash == tx2.GetHash()); + BOOST_CHECK(pnccTrie->getInfoForName(sName3, val)); + BOOST_CHECK(val.txhash == tx3.GetHash()); + BOOST_CHECK(pnccTrie->getInfoForName(sName4, val)); + BOOST_CHECK(val.txhash == tx4.GetHash()); + + CNCCTrieCache cache(pnccTrie); + + CNCCTrieProof proof; + + proof = cache.getProofForName(sName1); + BOOST_CHECK(verify_proof(proof, chainActive.Tip()->hashNCCTrie, sName1)); + BOOST_CHECK(proof.txhash == tx1.GetHash()); + BOOST_CHECK(proof.nOut == 0); + + proof = cache.getProofForName(sName2); + BOOST_CHECK(verify_proof(proof, chainActive.Tip()->hashNCCTrie, sName2)); + BOOST_CHECK(proof.txhash == tx2.GetHash()); + BOOST_CHECK(proof.nOut == 0); + + proof = cache.getProofForName(sName3); + BOOST_CHECK(verify_proof(proof, chainActive.Tip()->hashNCCTrie, sName3)); + BOOST_CHECK(proof.txhash == tx3.GetHash()); + BOOST_CHECK(proof.nOut == 0); + + proof = cache.getProofForName(sName4); + BOOST_CHECK(verify_proof(proof, chainActive.Tip()->hashNCCTrie, sName4)); + BOOST_CHECK(proof.txhash == tx4.GetHash()); + BOOST_CHECK(proof.nOut == 0); + + proof = cache.getProofForName(sName5); + BOOST_CHECK(verify_proof(proof, chainActive.Tip()->hashNCCTrie, sName5)); + BOOST_CHECK(proof.hasValue == false); + + proof = cache.getProofForName(sName6); + BOOST_CHECK(verify_proof(proof, chainActive.Tip()->hashNCCTrie, sName6)); + BOOST_CHECK(proof.hasValue == false); + + proof = cache.getProofForName(sName7); + BOOST_CHECK(verify_proof(proof, chainActive.Tip()->hashNCCTrie, sName7)); + BOOST_CHECK(proof.hasValue == false); + + BOOST_CHECK(RemoveBlock(blocks_to_invalidate.back())); + blocks_to_invalidate.pop_back(); + BOOST_CHECK(pnccTrie->empty()); + BOOST_CHECK(pnccTrie->queueEmpty()); + BOOST_CHECK(blocks_to_invalidate.empty()); + +} + BOOST_AUTO_TEST_SUITE_END() From ab830e84a3eed8b99b5b04dff4e1043766a80cd0 Mon Sep 17 00:00:00 2001 From: Jimmy Kiselak Date: Thu, 4 Feb 2016 19:55:01 -0500 Subject: [PATCH 2/3] make getting a proof an rpc call --- src/main.cpp | 36 +++++++++++ src/main.h | 2 + src/ncctrie.cpp | 14 +--- src/ncctrie.h | 3 +- src/rpcncctrie.cpp | 118 ++++++++++++++++++++++++++++++++++ src/rpcserver.cpp | 1 + src/rpcserver.h | 1 + src/test/ncctrie_tests.cpp | 128 ++++++++++++++++--------------------- 8 files changed, 215 insertions(+), 88 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index cf81a0731..7bb9f213d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3491,6 +3491,42 @@ bool CVerifyDB::VerifyDB(CCoinsView *coinsview, int nCheckLevel, int nCheckDepth return true; } +bool GetProofForName(const CBlockIndex* pindexProof, const std::string& name, CNCCTrieProof& proof) +{ + AssertLockHeld(cs_main); + if (!chainActive.Contains(pindexProof)) + { + return false; + } + CCoinsViewCache coins(pcoinsTip); + CNCCTrieCache trieCache(pnccTrie); + CBlockIndex* pindexState = chainActive.Tip(); + CValidationState state; + for (CBlockIndex *pindex = chainActive.Tip(); pindex && pindex->pprev && pindexState != pindexProof; pindex=pindex->pprev) + { + boost::this_thread::interruption_point(); + CBlock block; + if (!ReadBlockFromDisk(block, pindex)) + { + return false; + } + if (pindex == pindexState && (coins.DynamicMemoryUsage() + pcoinsTip->DynamicMemoryUsage()) <= nCoinCacheUsage) + { + bool fClean = true; + if (!DisconnectBlock(block, state, pindex, coins, trieCache, &fClean)) + { + return false; + } + pindexState = pindex->pprev; + } + if (ShutdownRequested()) + return false; + } + assert(pindexState == pindexProof); + proof = trieCache.getProofForName(name); + return true; +} + void UnloadBlockIndex() { LOCK(cs_main); diff --git a/src/main.h b/src/main.h index 09869deb3..a6ede18e3 100644 --- a/src/main.h +++ b/src/main.h @@ -167,6 +167,8 @@ FILE* OpenBlockFile(const CDiskBlockPos &pos, bool fReadOnly = false); FILE* OpenUndoFile(const CDiskBlockPos &pos, bool fReadOnly = false); /** Translation to a filesystem path */ boost::filesystem::path GetBlockPosFilename(const CDiskBlockPos &pos, const char *prefix); +/** Get a cryptographic proof that a name maps to a value **/ +bool GetProofForName(const CBlockIndex* pindexProof, const std::string& name, CNCCTrieProof& proof); /** Import blocks from an external file */ bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp = NULL); /** Initialize a new block tree database + block data on disk */ diff --git a/src/ncctrie.cpp b/src/ncctrie.cpp index d060e1985..b8756b74b 100644 --- a/src/ncctrie.cpp +++ b/src/ncctrie.cpp @@ -1307,11 +1307,6 @@ CNCCTrieProof CNCCTrieCache::getProofForName(const std::string& name) const if (cachedNode != cache.end()) current = cachedNode->second; hashMapType::const_iterator cachedHash = cacheHashes.find(currentPosition); - uint256 nodeHash; - if (cachedHash != cacheHashes.end()) - nodeHash = cachedHash->second; - else - nodeHash = current->hash; CNodeValue val; bool fNodeHasValue = current->getBestValue(val); uint256 valHash; @@ -1332,7 +1327,7 @@ CNCCTrieProof CNCCTrieCache::getProofForName(const std::string& name) const nextCurrent = itChildren->second; } } - nodes[currentPosition] = CNCCTrieProofNode(child_chars, fNodeHasValue, valHash, nodeHash); + nodes[currentPosition] = CNCCTrieProofNode(child_chars, fNodeHasValue, valHash); current = nextCurrent; if (current == NULL) break; @@ -1343,11 +1338,6 @@ CNCCTrieProof CNCCTrieCache::getProofForName(const std::string& name) const if (cachedNode != cache.end()) current = cachedNode->second; hashMapType::const_iterator cachedHash = cacheHashes.find(name); - uint256 nodeHash; - if (cachedHash != cacheHashes.end()) - nodeHash = cachedHash->second; - else - nodeHash = current->hash; fNameHasValue = current->getBestValue(nameVal); uint256 valHash; if (fNameHasValue) @@ -1365,7 +1355,7 @@ CNCCTrieProof CNCCTrieCache::getProofForName(const std::string& name) const std::pair leaf = getLeafNodeForProof(name, itChildren->first, itChildren->second); leafNodes[leaf.first] = leaf.second; } - nodes[name] = CNCCTrieProofNode(child_chars, fNameHasValue, valHash, nodeHash); + nodes[name] = CNCCTrieProofNode(child_chars, fNameHasValue, valHash); } else { diff --git a/src/ncctrie.h b/src/ncctrie.h index 47101effc..b441d7e12 100644 --- a/src/ncctrie.h +++ b/src/ncctrie.h @@ -205,11 +205,10 @@ class CNCCTrieProofNode { public: CNCCTrieProofNode() {}; - CNCCTrieProofNode(std::vector children, bool hasValue, uint256 valHash, uint256 nodeHash) : children(children), hasValue(hasValue), valHash(valHash), nodeHash(nodeHash) {}; + CNCCTrieProofNode(std::vector children, bool hasValue, uint256 valHash) : children(children), hasValue(hasValue), valHash(valHash) {}; std::vector children; bool hasValue; uint256 valHash; - uint256 nodeHash; }; class CNCCTrieProofLeafNode diff --git a/src/rpcncctrie.cpp b/src/rpcncctrie.cpp index 06bc40b3c..8b9c666ba 100644 --- a/src/rpcncctrie.cpp +++ b/src/rpcncctrie.cpp @@ -269,3 +269,121 @@ UniValue getclaimsfortx(const UniValue& params, bool fHelp) } return ret; } + +UniValue proofToJSON(const CNCCTrieProof& proof) +{ + UniValue result(UniValue::VOBJ); + UniValue nodes(UniValue::VARR); + for (std::map::const_iterator itNode = proof.nodes.begin(); itNode != proof.nodes.end(); ++itNode) + { + UniValue node(UniValue::VOBJ); + node.push_back(Pair("name", itNode->first)); + UniValue children(UniValue::VARR); + for (std::vector::const_iterator itChildren = itNode->second.children.begin(); itChildren != itNode->second.children.end(); ++itChildren) + { + children.push_back(*itChildren); + } + node.push_back(Pair("children", children)); + if (itNode->second.hasValue) + { + node.push_back(Pair("valueHash", itNode->second.valHash.GetHex())); + } + nodes.push_back(node); + } + result.push_back(Pair("nodes", nodes)); + UniValue leafNodes(UniValue::VARR); + for (std::map::const_iterator itLeafNode = proof.leafNodes.begin(); itLeafNode != proof.leafNodes.end(); ++itLeafNode) + { + UniValue leafNode(UniValue::VOBJ); + leafNode.push_back(Pair("name", itLeafNode->first)); + leafNode.push_back(Pair("nodeHash", itLeafNode->second.nodeHash.GetHex())); + leafNodes.push_back(leafNode); + } + result.push_back(Pair("leafNodes", leafNodes)); + if (proof.hasValue) + { + result.push_back(Pair("txhash", proof.txhash.GetHex())); + result.push_back(Pair("nOut", (int)proof.nOut)); + } + return result; +} + +UniValue getnameproof(const UniValue& params, bool fHelp) +{ + if (fHelp || (params.size() != 1 && params.size() != 2)) + throw std::runtime_error( + "getnameproof\n" + "Return the cryptographic proof that a name maps to a value\n" + "or doesn't.\n" + "Arguments:\n" + "1. \"name\" (string) the name to get a proof for\n" + "2. \"blockhash\" (string, optional) the hash of the block\n" + " which is the basis\n" + " of the proof. If\n" + " none is given, \n" + " the latest block\n" + " will be used.\n" + "Result: \n" + "{\n" + " \"nodes\" : [ (array of object) full nodes (i.e.\n" + " those which lead to\n" + " the requested name)\n" + " \"name\" : \"node name\" (string) The name of the node\n" + " \"children\" : [ (array of numeric) the characters\n" + " corresponding to\n" + " the children of\n" + " this node\n" + " \"child\" (numeric) a 0-255 number referring to\n" + " a child node of this node\n" + " ]\n" + " \"valueHash\" (string, if exists) the hash of this\n" + " node's value, if\n" + " it has one\n" + " ]\n" + " \"leaf_nodes\" : [ (array of object) leaf nodes (i.e.\n" + " those which do not\n" + " lead to the requested\n" + " name but nonetheless\n" + " contribute to the proof\n" + " \"name\" : \"node name\" (string) the name of the node\n" + " \"nodeHash\" : \"hash\" (string) the hash of the node\n" + " ]\n" + " \"txhash\" : \"hash\" (string, if exists) the txid of the\n" + " claim which controls\n" + " this name, if there\n" + " is one.\n" + " \"nOut\" : n, (numeric) the nOut of the claim which\n" + " controls this name, if there\n" + " is one.\n" + " }\n" + "}\n"); + + LOCK(cs_main); + std::string strName = params[0].get_str(); + uint256 blockHash; + if (params.size() == 2) + { + std::string strBlockHash = params[1].get_str(); + blockHash = uint256S(strBlockHash); + } + else + { + blockHash = chainActive.Tip()->GetBlockHash(); + } + + if (mapBlockIndex.count(blockHash) == 0) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); + + CBlockIndex* pblockIndex = mapBlockIndex[blockHash]; + if (!chainActive.Contains(pblockIndex)) + throw JSONRPCError(RPC_INTERNAL_ERROR, "Block not in main chain"); + + if (chainActive.Tip()->nHeight > (pblockIndex->nHeight + 10)) + throw JSONRPCError(RPC_INTERNAL_ERROR, "Block too deep to generate proof"); + + CNCCTrieProof proof; + if (!GetProofForName(pblockIndex, strName, proof)) + throw JSONRPCError(RPC_INTERNAL_ERROR, "Failed to generate proof"); + + return proofToJSON(proof); +} diff --git a/src/rpcserver.cpp b/src/rpcserver.cpp index 1d68c2898..1a59d1579 100644 --- a/src/rpcserver.cpp +++ b/src/rpcserver.cpp @@ -386,6 +386,7 @@ static const CRPCCommand vRPCCommands[] = { "nametrie", "gettotalclaims", &gettotalclaims, true }, { "nametrie", "gettotalvalueofclaims", &gettotalvalueofclaims, true }, { "nametrie", "getclaimsfortx", &getclaimsfortx, true }, + { "nametrie", "getnameproof", &getnameproof, true }, }; CRPCTable::CRPCTable() diff --git a/src/rpcserver.h b/src/rpcserver.h index bfd366f55..b5ee55413 100644 --- a/src/rpcserver.h +++ b/src/rpcserver.h @@ -247,6 +247,7 @@ extern UniValue gettotalclaimednames(const UniValue& params, bool fHelp); extern UniValue gettotalclaims(const UniValue& params, bool fHelp); extern UniValue gettotalvalueofclaims(const UniValue& params, bool fHelp); extern UniValue getclaimsfortx(const UniValue& params, bool fHelp); +extern UniValue getnameproof(const UniValue& params, bool fHelp); // in rest.cpp extern bool HTTPReq_REST(AcceptedConnection *conn, diff --git a/src/test/ncctrie_tests.cpp b/src/test/ncctrie_tests.cpp index 015a130c7..d3795b068 100644 --- a/src/test/ncctrie_tests.cpp +++ b/src/test/ncctrie_tests.cpp @@ -1418,106 +1418,86 @@ BOOST_AUTO_TEST_CASE(ncctrienode_serialize_unserialize) BOOST_CHECK(n1 == n2); } -bool verify_proof(CNCCTrieProof proof, uint256 rootHash, const std::string& name) +bool get_node_hash(const std::string& name, const std::string& currentPosition, const std::string::const_iterator itName, CNCCTrieProof& proof, uint256& nodeHash) { - for (std::string::const_iterator itName = name.begin(); itName != name.end(); ++itName) + std::map::const_iterator itNode = proof.nodes.find(currentPosition); + if (itNode == proof.nodes.end()) { - std::string currentPosition(name.begin(), itName); - std::map::const_iterator itNode = proof.nodes.find(currentPosition); - if (itNode == proof.nodes.end()) + return false; + } + if (name == currentPosition && itName != name.end()) + { + return false; + } + std::vector vchToHash; + bool haveNextNode = false; + for (std::vector::const_iterator itChildren = itNode->second.children.begin(); itChildren != itNode->second.children.end(); ++itChildren) + { + vchToHash.push_back(*itChildren); + std::stringstream nextPosition; + nextPosition << currentPosition << *itChildren; + if (name != currentPosition && *itChildren == *itName) { - return currentPosition != "" && !proof.hasValue; + uint256 childNodeHash; + if (!get_node_hash(name, nextPosition.str(), itName + 1, proof, childNodeHash)) + { + return false; + } + haveNextNode = true; + vchToHash.insert(vchToHash.end(), childNodeHash.begin(), childNodeHash.end()); } - std::vector vchToHash; - for (std::vector::const_iterator itChildren = itNode->second.children.begin(); itChildren != itNode->second.children.end(); ++itChildren) + else { - vchToHash.push_back(*itChildren); - std::stringstream nextPosition; - nextPosition << currentPosition << *itChildren; - if (*itChildren == *itName) + std::map::const_iterator itChildNode = proof.leafNodes.find(nextPosition.str()); + if (itChildNode == proof.leafNodes.end()) { - std::map::const_iterator itChildNode = proof.nodes.find(nextPosition.str()); - if (itChildNode == proof.nodes.end()) - { - return false; - } - vchToHash.insert(vchToHash.end(), itChildNode->second.nodeHash.begin(), itChildNode->second.nodeHash.end()); - } - else - { - std::map::const_iterator itChildNode = proof.leafNodes.find(nextPosition.str()); - if (itChildNode == proof.leafNodes.end()) - { - return false; - } - vchToHash.insert(vchToHash.end(), itChildNode->second.nodeHash.begin(), itChildNode->second.nodeHash.end()); + return false; } + vchToHash.insert(vchToHash.end(), itChildNode->second.nodeHash.begin(), itChildNode->second.nodeHash.end()); + } + } + if (name != currentPosition) + { + if (!haveNextNode && proof.hasValue) + { + return false; } if (itNode->second.hasValue) { vchToHash.insert(vchToHash.end(), itNode->second.valHash.begin(), itNode->second.valHash.end()); } - CHash256 hasher; - std::vector vchHash(hasher.OUTPUT_SIZE); - hasher.Write(vchToHash.data(), vchToHash.size()); - hasher.Finalize(&(vchHash[0])); - uint256 calculatedHash(vchHash); - if (calculatedHash != itNode->second.nodeHash) + } + else + { + if (proof.hasValue != itNode->second.hasValue) { return false; } - if (currentPosition == "" && calculatedHash != rootHash) + if (proof.hasValue) { - return false; + uint256 valHash = getOutPointHash(proof.txhash, proof.nOut); + if (valHash != itNode->second.valHash) + { + return false; + } + vchToHash.insert(vchToHash.end(), valHash.begin(), valHash.end()); } } - std::map::const_iterator itNode = proof.nodes.find(name); - if (itNode == proof.nodes.end()) - { - return name != "" && !proof.hasValue; - } - std::vector vchToHash; - for (std::vector::const_iterator itChildren = itNode->second.children.begin(); itChildren != itNode->second.children.end(); ++itChildren) - { - vchToHash.push_back(*itChildren); - std::stringstream nextPosition; - nextPosition << name << *itChildren; - std::map::const_iterator itChildNode = proof.leafNodes.find(nextPosition.str()); - if (itChildNode == proof.leafNodes.end()) - { - return false; - } - vchToHash.insert(vchToHash.end(), itChildNode->second.nodeHash.begin(), itChildNode->second.nodeHash.end()); - } - if (itNode->second.hasValue != proof.hasValue) - { - return false; - } - if (itNode->second.hasValue) - { - uint256 valHash = getOutPointHash(proof.txhash, proof.nOut); - if (valHash != itNode->second.valHash) - { - return false; - } - vchToHash.insert(vchToHash.end(), valHash.begin(), valHash.end()); - } CHash256 hasher; std::vector vchHash(hasher.OUTPUT_SIZE); hasher.Write(vchToHash.data(), vchToHash.size()); hasher.Finalize(&(vchHash[0])); uint256 calculatedHash(vchHash); - if (calculatedHash != itNode->second.nodeHash) - { - return false; - } - if (name == "" && calculatedHash != rootHash) - { - return false; - } + nodeHash = calculatedHash; return true; } +bool verify_proof(CNCCTrieProof proof, uint256 rootHash, const std::string& name) +{ + uint256 computedRootHash; + return get_node_hash(name, "", name.begin(), proof, computedRootHash) && rootHash == computedRootHash; +} + BOOST_AUTO_TEST_CASE(ncctrievalue_proof) { int block_counter = 0; From 7d14e09e5d1c3bbeff89f59edb234d551b070c2b Mon Sep 17 00:00:00 2001 From: Jimmy Kiselak Date: Sat, 6 Feb 2016 00:17:57 -0500 Subject: [PATCH 3/3] remove all recursion from generating/verifying name proofs --- src/ncctrie.cpp | 75 ++++++++------------- src/ncctrie.h | 19 ++---- src/rpcncctrie.cpp | 71 ++++++++++---------- src/test/ncctrie_tests.cpp | 133 ++++++++++++++++++++----------------- 4 files changed, 138 insertions(+), 160 deletions(-) diff --git a/src/ncctrie.cpp b/src/ncctrie.cpp index b8756b74b..87af740c6 100644 --- a/src/ncctrie.cpp +++ b/src/ncctrie.cpp @@ -1273,18 +1273,18 @@ bool CNCCTrieCache::flush() return success; } -std::pair CNCCTrieCache::getLeafNodeForProof(const std::string& currentPosition, unsigned char nodeChar, const CNCCTrieNode* currentNode) const +uint256 CNCCTrieCache::getLeafHashForProof(const std::string& currentPosition, unsigned char nodeChar, const CNCCTrieNode* currentNode) const { std::stringstream leafPosition; leafPosition << currentPosition << nodeChar; hashMapType::iterator cachedHash = cacheHashes.find(leafPosition.str()); if (cachedHash != cacheHashes.end()) { - return std::make_pair(leafPosition.str(), CNCCTrieProofLeafNode(cachedHash->second)); + return cachedHash->second; } else { - return std::make_pair(leafPosition.str(), CNCCTrieProofLeafNode(currentNode->hash)); + return currentNode->hash; } } @@ -1292,75 +1292,52 @@ CNCCTrieProof CNCCTrieCache::getProofForName(const std::string& name) const { if (dirty()) getMerkleHash(); - std::map nodes; - std::map leafNodes; + std::vector nodes; CNCCTrieNode* current = &(base->root); nodeCacheType::const_iterator cachedNode; - CNodeValue nameVal; - bool fNameHasValue; + bool fNameHasValue = false; uint256 txhash; - uint32_t nOut; - for (std::string::const_iterator itName = name.begin(); itName != name.end(); ++itName) + uint32_t nOut = 0; + for (std::string::const_iterator itName = name.begin(); current; ++itName) { std::string currentPosition(name.begin(), itName); cachedNode = cache.find(currentPosition); if (cachedNode != cache.end()) current = cachedNode->second; - hashMapType::const_iterator cachedHash = cacheHashes.find(currentPosition); CNodeValue val; bool fNodeHasValue = current->getBestValue(val); uint256 valHash; if (fNodeHasValue) valHash = val.GetHash(); - std::vector child_chars; + std::vector > children; CNCCTrieNode* nextCurrent = NULL; for (nodeMapType::const_iterator itChildren = current->children.begin(); itChildren != current->children.end(); ++itChildren) { - child_chars.push_back(itChildren->first); - if (itChildren->first != *itName) // Leaf node + if (itName == name.end() || itChildren->first != *itName) // Leaf node { - std::pair leaf = getLeafNodeForProof(currentPosition, itChildren->first, itChildren->second); - leafNodes[leaf.first] = leaf.second; + uint256 childHash = getLeafHashForProof(currentPosition, itChildren->first, itChildren->second); + children.push_back(std::make_pair(itChildren->first, childHash)); } else // Full node { nextCurrent = itChildren->second; + uint256 childHash; + children.push_back(std::make_pair(itChildren->first, childHash)); } } - nodes[currentPosition] = CNCCTrieProofNode(child_chars, fNodeHasValue, valHash); + if (currentPosition == name) + { + fNameHasValue = fNodeHasValue; + if (fNameHasValue) + { + txhash = val.txhash; + nOut = val.nOut; + } + valHash.SetNull(); + } + CNCCTrieProofNode node(children, fNodeHasValue, valHash); + nodes.push_back(node); current = nextCurrent; - if (current == NULL) - break; } - if (current != NULL) - { - cachedNode = cache.find(name); - if (cachedNode != cache.end()) - current = cachedNode->second; - hashMapType::const_iterator cachedHash = cacheHashes.find(name); - fNameHasValue = current->getBestValue(nameVal); - uint256 valHash; - if (fNameHasValue) - { - txhash = nameVal.txhash; - nOut = nameVal.nOut; - valHash = nameVal.GetHash(); - } - else - nOut = 0; - std::vector child_chars; - for (nodeMapType::const_iterator itChildren = current->children.begin(); itChildren != current->children.end(); ++itChildren) - { - child_chars.push_back(itChildren->first); - std::pair leaf = getLeafNodeForProof(name, itChildren->first, itChildren->second); - leafNodes[leaf.first] = leaf.second; - } - nodes[name] = CNCCTrieProofNode(child_chars, fNameHasValue, valHash); - } - else - { - fNameHasValue = false; - nOut = 0; - } - return CNCCTrieProof(nodes, leafNodes, fNameHasValue, txhash, nOut); + return CNCCTrieProof(nodes, fNameHasValue, txhash, nOut); } diff --git a/src/ncctrie.h b/src/ncctrie.h index b441d7e12..cc1089eee 100644 --- a/src/ncctrie.h +++ b/src/ncctrie.h @@ -205,27 +205,18 @@ class CNCCTrieProofNode { public: CNCCTrieProofNode() {}; - CNCCTrieProofNode(std::vector children, bool hasValue, uint256 valHash) : children(children), hasValue(hasValue), valHash(valHash) {}; - std::vector children; + CNCCTrieProofNode(std::vector > children, bool hasValue, uint256 valHash) : children(children), hasValue(hasValue), valHash(valHash) {}; + std::vector > children; bool hasValue; uint256 valHash; }; -class CNCCTrieProofLeafNode -{ -public: - CNCCTrieProofLeafNode() {}; - CNCCTrieProofLeafNode(uint256 nodeHash) : nodeHash(nodeHash) {}; - uint256 nodeHash; -}; - class CNCCTrieProof { public: CNCCTrieProof() {}; - CNCCTrieProof(std::map nodes, std::map leafNodes, bool hasValue, uint256 txhash, uint32_t nOut) : nodes(nodes), leafNodes(leafNodes), hasValue(hasValue), txhash(txhash), nOut(nOut) {} - std::map nodes; - std::map leafNodes; + CNCCTrieProof(std::vector nodes, bool hasValue, uint256 txhash, uint32_t nOut) : nodes(nodes), hasValue(hasValue), txhash(txhash), nOut(nOut) {} + std::vector nodes; bool hasValue; uint256 txhash; uint32_t nOut; @@ -272,7 +263,7 @@ private: void removeFromExpirationQueue(const std::string name, uint256 txhash, uint32_t nOut, int nHeight) const; valueQueueType::iterator getQueueCacheRow(int nHeight, bool createIfNotExists) const; valueQueueType::iterator getExpirationQueueCacheRow(int nHeight, bool createIfNotExists) const; - std::pair getLeafNodeForProof(const std::string& currentPosition, unsigned char nodeChar, const CNCCTrieNode* currentNode) const; + uint256 getLeafHashForProof(const std::string& currentPosition, unsigned char nodeChar, const CNCCTrieNode* currentNode) const; uint256 hashBlock; }; diff --git a/src/rpcncctrie.cpp b/src/rpcncctrie.cpp index 8b9c666ba..eb6a54c7b 100644 --- a/src/rpcncctrie.cpp +++ b/src/rpcncctrie.cpp @@ -274,32 +274,28 @@ UniValue proofToJSON(const CNCCTrieProof& proof) { UniValue result(UniValue::VOBJ); UniValue nodes(UniValue::VARR); - for (std::map::const_iterator itNode = proof.nodes.begin(); itNode != proof.nodes.end(); ++itNode) + for (std::vector::const_iterator itNode = proof.nodes.begin(); itNode != proof.nodes.end(); ++itNode) { UniValue node(UniValue::VOBJ); - node.push_back(Pair("name", itNode->first)); UniValue children(UniValue::VARR); - for (std::vector::const_iterator itChildren = itNode->second.children.begin(); itChildren != itNode->second.children.end(); ++itChildren) + for (std::vector >::const_iterator itChildren = itNode->children.begin(); itChildren != itNode->children.end(); ++itChildren) { - children.push_back(*itChildren); + UniValue child(UniValue::VOBJ); + child.push_back(Pair("character", itChildren->first)); + if (!itChildren->second.IsNull()) + { + child.push_back(Pair("nodeHash", itChildren->second.GetHex())); + } + children.push_back(child); } node.push_back(Pair("children", children)); - if (itNode->second.hasValue) + if (itNode->hasValue && !itNode->valHash.IsNull()) { - node.push_back(Pair("valueHash", itNode->second.valHash.GetHex())); + node.push_back(Pair("valueHash", itNode->valHash.GetHex())); } nodes.push_back(node); } result.push_back(Pair("nodes", nodes)); - UniValue leafNodes(UniValue::VARR); - for (std::map::const_iterator itLeafNode = proof.leafNodes.begin(); itLeafNode != proof.leafNodes.end(); ++itLeafNode) - { - UniValue leafNode(UniValue::VOBJ); - leafNode.push_back(Pair("name", itLeafNode->first)); - leafNode.push_back(Pair("nodeHash", itLeafNode->second.nodeHash.GetHex())); - leafNodes.push_back(leafNode); - } - result.push_back(Pair("leafNodes", leafNodes)); if (proof.hasValue) { result.push_back(Pair("txhash", proof.txhash.GetHex())); @@ -316,8 +312,8 @@ UniValue getnameproof(const UniValue& params, bool fHelp) "Return the cryptographic proof that a name maps to a value\n" "or doesn't.\n" "Arguments:\n" - "1. \"name\" (string) the name to get a proof for\n" - "2. \"blockhash\" (string, optional) the hash of the block\n" + "1. \"name\" (string) the name to get a proof for\n" + "2. \"blockhash\" (string, optional) the hash of the block\n" " which is the basis\n" " of the proof. If\n" " none is given, \n" @@ -328,25 +324,28 @@ UniValue getnameproof(const UniValue& params, bool fHelp) " \"nodes\" : [ (array of object) full nodes (i.e.\n" " those which lead to\n" " the requested name)\n" - " \"name\" : \"node name\" (string) The name of the node\n" - " \"children\" : [ (array of numeric) the characters\n" - " corresponding to\n" - " the children of\n" - " this node\n" - " \"child\" (numeric) a 0-255 number referring to\n" - " a child node of this node\n" + " \"children\" : [ (array of object) the children of\n" + " this node\n" + " \"child\" : { (object) a child node, either leaf or\n" + " reference to a full node\n" + " \"character\" : \"char\" (string) the character which\n" + " leads from the parent\n" + " to this child node\n" + " \"nodeHash\" : \"hash\" (string, if exists) the hash of\n" + " the node if\n" + " this is a \n" + " leaf node\n" + " }\n" " ]\n" - " \"valueHash\" (string, if exists) the hash of this\n" - " node's value, if\n" - " it has one\n" - " ]\n" - " \"leaf_nodes\" : [ (array of object) leaf nodes (i.e.\n" - " those which do not\n" - " lead to the requested\n" - " name but nonetheless\n" - " contribute to the proof\n" - " \"name\" : \"node name\" (string) the name of the node\n" - " \"nodeHash\" : \"hash\" (string) the hash of the node\n" + " \"valueHash\" (string, if exists) the hash of this\n" + " node's value, if\n" + " it has one. If \n" + " this is the\n" + " requested name\n" + " this will not\n" + " exist whether\n" + " the node has a\n" + " value or not\n" " ]\n" " \"txhash\" : \"hash\" (string, if exists) the txid of the\n" " claim which controls\n" @@ -378,7 +377,7 @@ UniValue getnameproof(const UniValue& params, bool fHelp) if (!chainActive.Contains(pblockIndex)) throw JSONRPCError(RPC_INTERNAL_ERROR, "Block not in main chain"); - if (chainActive.Tip()->nHeight > (pblockIndex->nHeight + 10)) + if (chainActive.Tip()->nHeight > (pblockIndex->nHeight + 20)) throw JSONRPCError(RPC_INTERNAL_ERROR, "Block too deep to generate proof"); CNCCTrieProof proof; diff --git a/src/test/ncctrie_tests.cpp b/src/test/ncctrie_tests.cpp index d3795b068..cb2a21280 100644 --- a/src/test/ncctrie_tests.cpp +++ b/src/test/ncctrie_tests.cpp @@ -1418,84 +1418,95 @@ BOOST_AUTO_TEST_CASE(ncctrienode_serialize_unserialize) BOOST_CHECK(n1 == n2); } -bool get_node_hash(const std::string& name, const std::string& currentPosition, const std::string::const_iterator itName, CNCCTrieProof& proof, uint256& nodeHash) +bool verify_proof(const CNCCTrieProof proof, uint256 rootHash, const std::string& name) { - std::map::const_iterator itNode = proof.nodes.find(currentPosition); - if (itNode == proof.nodes.end()) + uint256 previousComputedHash; + std::string computedReverseName; + bool verifiedValue = false; + + for (std::vector::const_reverse_iterator itNodes = proof.nodes.rbegin(); itNodes != proof.nodes.rend(); ++itNodes) { - return false; - } - if (name == currentPosition && itName != name.end()) - { - return false; - } - std::vector vchToHash; - bool haveNextNode = false; - for (std::vector::const_iterator itChildren = itNode->second.children.begin(); itChildren != itNode->second.children.end(); ++itChildren) - { - vchToHash.push_back(*itChildren); - std::stringstream nextPosition; - nextPosition << currentPosition << *itChildren; - if (name != currentPosition && *itChildren == *itName) + bool foundChildInChain = false; + std::vector vchToHash; + for (std::vector >::const_iterator itChildren = itNodes->children.begin(); itChildren != itNodes->children.end(); ++itChildren) { - uint256 childNodeHash; - if (!get_node_hash(name, nextPosition.str(), itName + 1, proof, childNodeHash)) + vchToHash.push_back(itChildren->first); + uint256 childHash; + if (itChildren->second.IsNull()) { - return false; + if (previousComputedHash.IsNull()) + { + return false; + } + if (foundChildInChain) + { + return false; + } + foundChildInChain = true; + computedReverseName += itChildren->first; + childHash = previousComputedHash; } - haveNextNode = true; - vchToHash.insert(vchToHash.end(), childNodeHash.begin(), childNodeHash.end()); - } - else - { - std::map::const_iterator itChildNode = proof.leafNodes.find(nextPosition.str()); - if (itChildNode == proof.leafNodes.end()) + else { - return false; + childHash = itChildren->second; } - vchToHash.insert(vchToHash.end(), itChildNode->second.nodeHash.begin(), itChildNode->second.nodeHash.end()); + vchToHash.insert(vchToHash.end(), childHash.begin(), childHash.end()); } - } - if (name != currentPosition) - { - if (!haveNextNode && proof.hasValue) + if (itNodes != proof.nodes.rbegin() && !foundChildInChain) { return false; } - if (itNode->second.hasValue) + if (itNodes->hasValue) { - vchToHash.insert(vchToHash.end(), itNode->second.valHash.begin(), itNode->second.valHash.end()); - } - } - else - { - if (proof.hasValue != itNode->second.hasValue) - { - return false; - } - if (proof.hasValue) - { - uint256 valHash = getOutPointHash(proof.txhash, proof.nOut); - if (valHash != itNode->second.valHash) + uint256 valHash; + if (itNodes->valHash.IsNull()) { - return false; + if (itNodes != proof.nodes.rbegin()) + { + return false; + } + if (!proof.hasValue) + { + return false; + } + valHash = getOutPointHash(proof.txhash, proof.nOut); + verifiedValue = true; + } + else + { + valHash = itNodes->valHash; } vchToHash.insert(vchToHash.end(), valHash.begin(), valHash.end()); } + else if (proof.hasValue && itNodes == proof.nodes.rbegin()) + { + return false; + } + CHash256 hasher; + std::vector vchHash(hasher.OUTPUT_SIZE); + hasher.Write(vchToHash.data(), vchToHash.size()); + hasher.Finalize(&(vchHash[0])); + uint256 calculatedHash(vchHash); + previousComputedHash = calculatedHash; } - CHash256 hasher; - std::vector vchHash(hasher.OUTPUT_SIZE); - hasher.Write(vchToHash.data(), vchToHash.size()); - hasher.Finalize(&(vchHash[0])); - uint256 calculatedHash(vchHash); - nodeHash = calculatedHash; - return true; -} - -bool verify_proof(CNCCTrieProof proof, uint256 rootHash, const std::string& name) -{ - uint256 computedRootHash; - return get_node_hash(name, "", name.begin(), proof, computedRootHash) && rootHash == computedRootHash; + if (previousComputedHash != rootHash) + { + return false; + } + if (proof.hasValue && !verifiedValue) + { + return false; + } + std::string::reverse_iterator itComputedName = computedReverseName.rbegin(); + std::string::const_iterator itName = name.begin(); + for (; itName != name.end() && itComputedName != computedReverseName.rend(); ++itName, ++itComputedName) + { + if (*itName != *itComputedName) + { + return false; + } + } + return (!proof.hasValue || itName == name.end()); } BOOST_AUTO_TEST_CASE(ncctrievalue_proof)