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)