remove all recursion from generating/verifying name proofs

This commit is contained in:
Jimmy Kiselak 2016-02-06 00:17:57 -05:00
parent ab830e84a3
commit 7d14e09e5d
4 changed files with 138 additions and 160 deletions

View file

@ -1273,18 +1273,18 @@ bool CNCCTrieCache::flush()
return success; return success;
} }
std::pair<std::string, CNCCTrieProofLeafNode> 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; std::stringstream leafPosition;
leafPosition << currentPosition << nodeChar; leafPosition << currentPosition << nodeChar;
hashMapType::iterator cachedHash = cacheHashes.find(leafPosition.str()); hashMapType::iterator cachedHash = cacheHashes.find(leafPosition.str());
if (cachedHash != cacheHashes.end()) if (cachedHash != cacheHashes.end())
{ {
return std::make_pair(leafPosition.str(), CNCCTrieProofLeafNode(cachedHash->second)); return cachedHash->second;
} }
else 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()) if (dirty())
getMerkleHash(); getMerkleHash();
std::map<std::string, CNCCTrieProofNode> nodes; std::vector<CNCCTrieProofNode> nodes;
std::map<std::string, CNCCTrieProofLeafNode> leafNodes;
CNCCTrieNode* current = &(base->root); CNCCTrieNode* current = &(base->root);
nodeCacheType::const_iterator cachedNode; nodeCacheType::const_iterator cachedNode;
CNodeValue nameVal; bool fNameHasValue = false;
bool fNameHasValue;
uint256 txhash; uint256 txhash;
uint32_t nOut; uint32_t nOut = 0;
for (std::string::const_iterator itName = name.begin(); itName != name.end(); ++itName) for (std::string::const_iterator itName = name.begin(); current; ++itName)
{ {
std::string currentPosition(name.begin(), itName); std::string currentPosition(name.begin(), itName);
cachedNode = cache.find(currentPosition); cachedNode = cache.find(currentPosition);
if (cachedNode != cache.end()) if (cachedNode != cache.end())
current = cachedNode->second; current = cachedNode->second;
hashMapType::const_iterator cachedHash = cacheHashes.find(currentPosition);
CNodeValue val; CNodeValue val;
bool fNodeHasValue = current->getBestValue(val); bool fNodeHasValue = current->getBestValue(val);
uint256 valHash; uint256 valHash;
if (fNodeHasValue) if (fNodeHasValue)
valHash = val.GetHash(); valHash = val.GetHash();
std::vector<unsigned char> child_chars; std::vector<std::pair<unsigned char, uint256> > children;
CNCCTrieNode* nextCurrent = NULL; CNCCTrieNode* nextCurrent = NULL;
for (nodeMapType::const_iterator itChildren = current->children.begin(); itChildren != current->children.end(); ++itChildren) for (nodeMapType::const_iterator itChildren = current->children.begin(); itChildren != current->children.end(); ++itChildren)
{ {
child_chars.push_back(itChildren->first); if (itName == name.end() || itChildren->first != *itName) // Leaf node
if (itChildren->first != *itName) // Leaf node
{ {
std::pair<std::string, CNCCTrieProofLeafNode> leaf = getLeafNodeForProof(currentPosition, itChildren->first, itChildren->second); uint256 childHash = getLeafHashForProof(currentPosition, itChildren->first, itChildren->second);
leafNodes[leaf.first] = leaf.second; children.push_back(std::make_pair(itChildren->first, childHash));
} }
else // Full node else // Full node
{ {
nextCurrent = itChildren->second; 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; current = nextCurrent;
if (current == NULL)
break;
} }
if (current != NULL) return CNCCTrieProof(nodes, fNameHasValue, txhash, nOut);
{
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<unsigned char> child_chars;
for (nodeMapType::const_iterator itChildren = current->children.begin(); itChildren != current->children.end(); ++itChildren)
{
child_chars.push_back(itChildren->first);
std::pair<std::string, CNCCTrieProofLeafNode> 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);
} }

View file

@ -205,27 +205,18 @@ class CNCCTrieProofNode
{ {
public: public:
CNCCTrieProofNode() {}; CNCCTrieProofNode() {};
CNCCTrieProofNode(std::vector<unsigned char> children, bool hasValue, uint256 valHash) : children(children), hasValue(hasValue), valHash(valHash) {}; CNCCTrieProofNode(std::vector<std::pair<unsigned char, uint256> > children, bool hasValue, uint256 valHash) : children(children), hasValue(hasValue), valHash(valHash) {};
std::vector<unsigned char> children; std::vector<std::pair<unsigned char, uint256> > children;
bool hasValue; bool hasValue;
uint256 valHash; uint256 valHash;
}; };
class CNCCTrieProofLeafNode
{
public:
CNCCTrieProofLeafNode() {};
CNCCTrieProofLeafNode(uint256 nodeHash) : nodeHash(nodeHash) {};
uint256 nodeHash;
};
class CNCCTrieProof class CNCCTrieProof
{ {
public: public:
CNCCTrieProof() {}; CNCCTrieProof() {};
CNCCTrieProof(std::map<std::string, CNCCTrieProofNode> nodes, std::map<std::string, CNCCTrieProofLeafNode> leafNodes, bool hasValue, uint256 txhash, uint32_t nOut) : nodes(nodes), leafNodes(leafNodes), hasValue(hasValue), txhash(txhash), nOut(nOut) {} CNCCTrieProof(std::vector<CNCCTrieProofNode> nodes, bool hasValue, uint256 txhash, uint32_t nOut) : nodes(nodes), hasValue(hasValue), txhash(txhash), nOut(nOut) {}
std::map<std::string, CNCCTrieProofNode> nodes; std::vector<CNCCTrieProofNode> nodes;
std::map<std::string, CNCCTrieProofLeafNode> leafNodes;
bool hasValue; bool hasValue;
uint256 txhash; uint256 txhash;
uint32_t nOut; uint32_t nOut;
@ -272,7 +263,7 @@ private:
void removeFromExpirationQueue(const std::string name, uint256 txhash, uint32_t nOut, int nHeight) const; void removeFromExpirationQueue(const std::string name, uint256 txhash, uint32_t nOut, int nHeight) const;
valueQueueType::iterator getQueueCacheRow(int nHeight, bool createIfNotExists) const; valueQueueType::iterator getQueueCacheRow(int nHeight, bool createIfNotExists) const;
valueQueueType::iterator getExpirationQueueCacheRow(int nHeight, bool createIfNotExists) const; valueQueueType::iterator getExpirationQueueCacheRow(int nHeight, bool createIfNotExists) const;
std::pair<std::string, CNCCTrieProofLeafNode> 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; uint256 hashBlock;
}; };

View file

@ -274,32 +274,28 @@ UniValue proofToJSON(const CNCCTrieProof& proof)
{ {
UniValue result(UniValue::VOBJ); UniValue result(UniValue::VOBJ);
UniValue nodes(UniValue::VARR); UniValue nodes(UniValue::VARR);
for (std::map<std::string, CNCCTrieProofNode>::const_iterator itNode = proof.nodes.begin(); itNode != proof.nodes.end(); ++itNode) for (std::vector<CNCCTrieProofNode>::const_iterator itNode = proof.nodes.begin(); itNode != proof.nodes.end(); ++itNode)
{ {
UniValue node(UniValue::VOBJ); UniValue node(UniValue::VOBJ);
node.push_back(Pair("name", itNode->first));
UniValue children(UniValue::VARR); UniValue children(UniValue::VARR);
for (std::vector<unsigned char>::const_iterator itChildren = itNode->second.children.begin(); itChildren != itNode->second.children.end(); ++itChildren) for (std::vector<std::pair<unsigned char, uint256> >::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)); 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); nodes.push_back(node);
} }
result.push_back(Pair("nodes", nodes)); result.push_back(Pair("nodes", nodes));
UniValue leafNodes(UniValue::VARR);
for (std::map<std::string, CNCCTrieProofLeafNode>::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) if (proof.hasValue)
{ {
result.push_back(Pair("txhash", proof.txhash.GetHex())); 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" "Return the cryptographic proof that a name maps to a value\n"
"or doesn't.\n" "or doesn't.\n"
"Arguments:\n" "Arguments:\n"
"1. \"name\" (string) the name to get a proof for\n" "1. \"name\" (string) the name to get a proof for\n"
"2. \"blockhash\" (string, optional) the hash of the block\n" "2. \"blockhash\" (string, optional) the hash of the block\n"
" which is the basis\n" " which is the basis\n"
" of the proof. If\n" " of the proof. If\n"
" none is given, \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" " \"nodes\" : [ (array of object) full nodes (i.e.\n"
" those which lead to\n" " those which lead to\n"
" the requested name)\n" " the requested name)\n"
" \"name\" : \"node name\" (string) The name of the node\n" " \"children\" : [ (array of object) the children of\n"
" \"children\" : [ (array of numeric) the characters\n" " this node\n"
" corresponding to\n" " \"child\" : { (object) a child node, either leaf or\n"
" the children of\n" " reference to a full node\n"
" this node\n" " \"character\" : \"char\" (string) the character which\n"
" \"child\" (numeric) a 0-255 number referring to\n" " leads from the parent\n"
" a child node of this node\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" " ]\n"
" \"valueHash\" (string, if exists) the hash of this\n" " \"valueHash\" (string, if exists) the hash of this\n"
" node's value, if\n" " node's value, if\n"
" it has one\n" " it has one. If \n"
" ]\n" " this is the\n"
" \"leaf_nodes\" : [ (array of object) leaf nodes (i.e.\n" " requested name\n"
" those which do not\n" " this will not\n"
" lead to the requested\n" " exist whether\n"
" name but nonetheless\n" " the node has a\n"
" contribute to the proof\n" " value or not\n"
" \"name\" : \"node name\" (string) the name of the node\n"
" \"nodeHash\" : \"hash\" (string) the hash of the node\n"
" ]\n" " ]\n"
" \"txhash\" : \"hash\" (string, if exists) the txid of the\n" " \"txhash\" : \"hash\" (string, if exists) the txid of the\n"
" claim which controls\n" " claim which controls\n"
@ -378,7 +377,7 @@ UniValue getnameproof(const UniValue& params, bool fHelp)
if (!chainActive.Contains(pblockIndex)) if (!chainActive.Contains(pblockIndex))
throw JSONRPCError(RPC_INTERNAL_ERROR, "Block not in main chain"); 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"); throw JSONRPCError(RPC_INTERNAL_ERROR, "Block too deep to generate proof");
CNCCTrieProof proof; CNCCTrieProof proof;

View file

@ -1418,84 +1418,95 @@ BOOST_AUTO_TEST_CASE(ncctrienode_serialize_unserialize)
BOOST_CHECK(n1 == n2); 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<std::string, CNCCTrieProofNode>::const_iterator itNode = proof.nodes.find(currentPosition); uint256 previousComputedHash;
if (itNode == proof.nodes.end()) std::string computedReverseName;
bool verifiedValue = false;
for (std::vector<CNCCTrieProofNode>::const_reverse_iterator itNodes = proof.nodes.rbegin(); itNodes != proof.nodes.rend(); ++itNodes)
{ {
return false; bool foundChildInChain = false;
} std::vector<unsigned char> vchToHash;
if (name == currentPosition && itName != name.end()) for (std::vector<std::pair<unsigned char, uint256> >::const_iterator itChildren = itNodes->children.begin(); itChildren != itNodes->children.end(); ++itChildren)
{
return false;
}
std::vector<unsigned char> vchToHash;
bool haveNextNode = false;
for (std::vector<unsigned char>::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)
{ {
uint256 childNodeHash; vchToHash.push_back(itChildren->first);
if (!get_node_hash(name, nextPosition.str(), itName + 1, proof, childNodeHash)) 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; else
vchToHash.insert(vchToHash.end(), childNodeHash.begin(), childNodeHash.end());
}
else
{
std::map<std::string, CNCCTrieProofLeafNode>::const_iterator itChildNode = proof.leafNodes.find(nextPosition.str());
if (itChildNode == proof.leafNodes.end())
{ {
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 (itNodes != proof.nodes.rbegin() && !foundChildInChain)
if (name != currentPosition)
{
if (!haveNextNode && proof.hasValue)
{ {
return false; return false;
} }
if (itNode->second.hasValue) if (itNodes->hasValue)
{ {
vchToHash.insert(vchToHash.end(), itNode->second.valHash.begin(), itNode->second.valHash.end()); uint256 valHash;
} if (itNodes->valHash.IsNull())
}
else
{
if (proof.hasValue != itNode->second.hasValue)
{
return false;
}
if (proof.hasValue)
{
uint256 valHash = getOutPointHash(proof.txhash, proof.nOut);
if (valHash != itNode->second.valHash)
{ {
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()); vchToHash.insert(vchToHash.end(), valHash.begin(), valHash.end());
} }
else if (proof.hasValue && itNodes == proof.nodes.rbegin())
{
return false;
}
CHash256 hasher;
std::vector<unsigned char> vchHash(hasher.OUTPUT_SIZE);
hasher.Write(vchToHash.data(), vchToHash.size());
hasher.Finalize(&(vchHash[0]));
uint256 calculatedHash(vchHash);
previousComputedHash = calculatedHash;
} }
CHash256 hasher; if (previousComputedHash != rootHash)
std::vector<unsigned char> vchHash(hasher.OUTPUT_SIZE); {
hasher.Write(vchToHash.data(), vchToHash.size()); return false;
hasher.Finalize(&(vchHash[0])); }
uint256 calculatedHash(vchHash); if (proof.hasValue && !verifiedValue)
nodeHash = calculatedHash; {
return true; return false;
} }
std::string::reverse_iterator itComputedName = computedReverseName.rbegin();
bool verify_proof(CNCCTrieProof proof, uint256 rootHash, const std::string& name) std::string::const_iterator itName = name.begin();
{ for (; itName != name.end() && itComputedName != computedReverseName.rend(); ++itName, ++itComputedName)
uint256 computedRootHash; {
return get_node_hash(name, "", name.begin(), proof, computedRootHash) && rootHash == computedRootHash; if (*itName != *itComputedName)
{
return false;
}
}
return (!proof.hasValue || itName == name.end());
} }
BOOST_AUTO_TEST_CASE(ncctrievalue_proof) BOOST_AUTO_TEST_CASE(ncctrievalue_proof)