From ab830e84a3eed8b99b5b04dff4e1043766a80cd0 Mon Sep 17 00:00:00 2001 From: Jimmy Kiselak Date: Thu, 4 Feb 2016 19:55:01 -0500 Subject: [PATCH] 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;