Merge in proofs, and fix two delay related bugs
Bug #1: If a claim was inserted for name which had no previous claims, it would be treated as if it had been under continuous ownership since block 0 Bug #2: When disconnecting and connecting blocks, the official trie was checked to see if changes of ownership took place, which could result in the wrong answer if a single cache was used to disconnect/connect multiple blocks without flushing to the trie Merge branch 'master' of git.jimmykiselak.com:/usr/git/ncc into real Conflicts: src/ncctrie.cpp src/ncctrie.h src/rpcserver.cpp src/test/ncctrie_tests.cpp
This commit is contained in:
commit
7b866aa51e
8 changed files with 712 additions and 62 deletions
|
@ -21,7 +21,7 @@ std::vector<unsigned char> heightToVch(int n)
|
|||
return vchHeight;
|
||||
}
|
||||
|
||||
uint256 CClaimValue::GetHash() const
|
||||
uint256 getValueHash(COutPoint outPoint, int nHeightOfLastTakeover)
|
||||
{
|
||||
CHash256 txHasher;
|
||||
txHasher.Write(outPoint.hash.begin(), outPoint.hash.size());
|
||||
|
@ -36,14 +36,21 @@ uint256 CClaimValue::GetHash() const
|
|||
std::vector<unsigned char> vchnOutHash(nOutHasher.OUTPUT_SIZE);
|
||||
nOutHasher.Finalize(&(vchnOutHash[0]));
|
||||
|
||||
CHash256 takeoverHasher;
|
||||
std::vector<unsigned char> vchTakeoverHeightToHash = heightToVch(nHeightOfLastTakeover);
|
||||
takeoverHasher.Write(vchTakeoverHeightToHash.data(), vchTakeoverHeightToHash.size());
|
||||
std::vector<unsigned char> vchTakeoverHash(takeoverHasher.OUTPUT_SIZE);
|
||||
takeoverHasher.Finalize(&(vchTakeoverHash[0]));
|
||||
|
||||
CHash256 hasher;
|
||||
hasher.Write(vchtxHash.data(), vchtxHash.size());
|
||||
hasher.Write(vchnOutHash.data(), vchnOutHash.size());
|
||||
hasher.Write(vchTakeoverHash.data(), vchTakeoverHash.size());
|
||||
std::vector<unsigned char> vchHash(hasher.OUTPUT_SIZE);
|
||||
hasher.Finalize(&(vchHash[0]));
|
||||
|
||||
uint256 claimHash(vchHash);
|
||||
return claimHash;
|
||||
uint256 valueHash(vchHash);
|
||||
return valueHash;
|
||||
}
|
||||
|
||||
bool CClaimTrieNode::insertClaim(CClaimValue claim)
|
||||
|
@ -86,7 +93,9 @@ bool CClaimTrieNode::removeClaim(const COutPoint& outPoint, CClaimValue& claim)
|
|||
bool CClaimTrieNode::getBestClaim(CClaimValue& claim) const
|
||||
{
|
||||
if (claims.empty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
claim = claims.front();
|
||||
|
@ -432,14 +441,16 @@ bool CClaimTrie::getInfoForName(const std::string& name, CClaimValue& claim) con
|
|||
{
|
||||
const CClaimTrieNode* current = getNodeForName(name);
|
||||
if (current)
|
||||
{
|
||||
return current->getBestClaim(claim);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CClaimTrie::getLastTakeoverForName(const std::string& name, int& lastTakeoverHeight) const
|
||||
{
|
||||
const CClaimTrieNode* current = getNodeForName(name);
|
||||
if (current)
|
||||
if (current && !current->claims.empty())
|
||||
{
|
||||
lastTakeoverHeight = current->nHeightOfLastTakeover;
|
||||
return true;
|
||||
|
@ -474,10 +485,8 @@ bool CClaimTrie::recursiveCheckConsistency(const CClaimTrieNode* node) const
|
|||
|
||||
if (hasClaim)
|
||||
{
|
||||
uint256 claimHash = claim.GetHash();
|
||||
vchToHash.insert(vchToHash.end(), claimHash.begin(), claimHash.end());
|
||||
std::vector<unsigned char> heightToHash = heightToVch(node->nHeightOfLastTakeover);
|
||||
vchToHash.insert(vchToHash.end(), heightToHash.begin(), heightToHash.end());
|
||||
uint256 valueHash = getValueHash(claim.outPoint, node->nHeightOfLastTakeover);
|
||||
vchToHash.insert(vchToHash.end(), valueHash.begin(), valueHash.end());
|
||||
}
|
||||
|
||||
CHash256 hasher;
|
||||
|
@ -1064,12 +1073,10 @@ bool CClaimTrieCache::recursiveComputeMerkleHash(CClaimTrieNode* tnCurrent, std:
|
|||
|
||||
if (hasClaim)
|
||||
{
|
||||
uint256 claimHash = claim.GetHash();
|
||||
vchToHash.insert(vchToHash.end(), claimHash.begin(), claimHash.end());
|
||||
int nHeightOfLastTakeover;
|
||||
assert(getLastTakeoverForName(sPos, nHeightOfLastTakeover));
|
||||
std::vector<unsigned char> heightToHash = heightToVch(nHeightOfLastTakeover);
|
||||
vchToHash.insert(vchToHash.end(), heightToHash.begin(), heightToHash.end());
|
||||
uint256 valueHash = getValueHash(claim.outPoint, nHeightOfLastTakeover);
|
||||
vchToHash.insert(vchToHash.end(), valueHash.begin(), valueHash.end());
|
||||
}
|
||||
|
||||
CHash256 hasher;
|
||||
|
@ -1110,6 +1117,31 @@ bool CClaimTrieCache::empty() const
|
|||
return base->empty() && cache.empty();
|
||||
}
|
||||
|
||||
CClaimTrieNode* CClaimTrieCache::addNodeToCache(const std::string& position, CClaimTrieNode* original) const
|
||||
{
|
||||
if (!original)
|
||||
original = new CClaimTrieNode();
|
||||
CClaimTrieNode* cacheCopy = new CClaimTrieNode(*original);
|
||||
cache[position] = cacheCopy;
|
||||
nodeCacheType::const_iterator itOriginals = block_originals.find(position);
|
||||
if (block_originals.end() == itOriginals)
|
||||
{
|
||||
CClaimTrieNode* originalCopy = new CClaimTrieNode(*original);
|
||||
block_originals[position] = originalCopy;
|
||||
}
|
||||
return cacheCopy;
|
||||
}
|
||||
|
||||
bool CClaimTrieCache::getOriginalInfoForName(const std::string& name, CClaimValue& claim) const
|
||||
{
|
||||
nodeCacheType::const_iterator itOriginalCache = block_originals.find(name);
|
||||
if (itOriginalCache == block_originals.end())
|
||||
{
|
||||
return base->getInfoForName(name, claim);
|
||||
}
|
||||
return itOriginalCache->second->getBestClaim(claim);
|
||||
}
|
||||
|
||||
bool CClaimTrieCache::insertClaimIntoTrie(const std::string& name, CClaimValue claim, bool fCheckTakeover) const
|
||||
{
|
||||
assert(base);
|
||||
|
@ -1118,11 +1150,6 @@ bool CClaimTrieCache::insertClaimIntoTrie(const std::string& name, CClaimValue c
|
|||
cachedNode = cache.find("");
|
||||
if (cachedNode != cache.end())
|
||||
currentNode = cachedNode->second;
|
||||
if (currentNode == NULL)
|
||||
{
|
||||
currentNode = new CClaimTrieNode();
|
||||
cache[""] = currentNode;
|
||||
}
|
||||
for (std::string::const_iterator itCur = name.begin(); itCur != name.end(); ++itCur)
|
||||
{
|
||||
std::string sCurrentSubstring(name.begin(), itCur);
|
||||
|
@ -1157,12 +1184,10 @@ bool CClaimTrieCache::insertClaimIntoTrie(const std::string& name, CClaimValue c
|
|||
}
|
||||
else
|
||||
{
|
||||
currentNode = new CClaimTrieNode(*currentNode);
|
||||
cache[sCurrentSubstring] = currentNode;
|
||||
currentNode = addNodeToCache(sCurrentSubstring, currentNode);
|
||||
}
|
||||
CClaimTrieNode* newNode = new CClaimTrieNode();
|
||||
CClaimTrieNode* newNode = addNodeToCache(sNextSubstring, NULL);
|
||||
currentNode->children[*itCur] = newNode;
|
||||
cache[sNextSubstring] = newNode;
|
||||
currentNode = newNode;
|
||||
}
|
||||
|
||||
|
@ -1173,8 +1198,7 @@ bool CClaimTrieCache::insertClaimIntoTrie(const std::string& name, CClaimValue c
|
|||
}
|
||||
else
|
||||
{
|
||||
currentNode = new CClaimTrieNode(*currentNode);
|
||||
cache[name] = currentNode;
|
||||
currentNode = addNodeToCache(name, currentNode);
|
||||
}
|
||||
bool fChanged = false;
|
||||
if (currentNode->claims.empty())
|
||||
|
@ -1241,8 +1265,7 @@ bool CClaimTrieCache::removeClaimFromTrie(const std::string& name, const COutPoi
|
|||
assert(cachedNode->second == currentNode);
|
||||
else
|
||||
{
|
||||
currentNode = new CClaimTrieNode(*currentNode);
|
||||
cache[name] = currentNode;
|
||||
currentNode = addNodeToCache(name, currentNode);
|
||||
}
|
||||
bool fChanged = false;
|
||||
assert(currentNode != NULL);
|
||||
|
@ -1335,8 +1358,7 @@ bool CClaimTrieCache::recursivePruneName(CClaimTrieNode* tnCurrent, unsigned int
|
|||
{
|
||||
// it isn't, so make a copy, stick it in the cache,
|
||||
// and make it the new current node
|
||||
tnCurrent = new CClaimTrieNode(*tnCurrent);
|
||||
cache[sCurrentSubstring] = tnCurrent;
|
||||
tnCurrent = addNodeToCache(sCurrentSubstring, tnCurrent);
|
||||
}
|
||||
// erase the character from the current node, which is
|
||||
// now guaranteed to be in the cache
|
||||
|
@ -1412,7 +1434,7 @@ bool CClaimTrieCache::addClaim(const std::string& name, const COutPoint& outPoin
|
|||
assert(nHeight == nCurrentHeight);
|
||||
CClaimValue currentClaim;
|
||||
int delayForClaim;
|
||||
if (base->getInfoForName(name, currentClaim) && currentClaim.claimId == claimId)
|
||||
if (getOriginalInfoForName(name, currentClaim) && currentClaim.claimId == claimId)
|
||||
{
|
||||
LogPrintf("%s: This is an update to a best claim.\n", __func__);
|
||||
delayForClaim = 0;
|
||||
|
@ -1810,7 +1832,7 @@ bool CClaimTrieCache::addSupport(const std::string& name, const COutPoint& outPo
|
|||
assert(nHeight == nCurrentHeight);
|
||||
CClaimValue claim;
|
||||
int delayForSupport;
|
||||
if (base->getInfoForName(name, claim) && claim.claimId == supportedClaimId)
|
||||
if (getOriginalInfoForName(name, claim) && claim.claimId == supportedClaimId)
|
||||
{
|
||||
LogPrintf("%s: This is a support to a best claim.\n", __func__);
|
||||
delayForSupport = 0;
|
||||
|
@ -2037,7 +2059,7 @@ bool CClaimTrieCache::incrementBlock(insertUndoType& insertUndo, claimQueueRowTy
|
|||
// claims in the queue for that name and the takeover height should be the current height
|
||||
// if the node is not in the cache, or getbestclaim fails, that means all of its claims were
|
||||
// deleted
|
||||
// if base->getInfoForName returns false, that means it's new and shouldn't go into the undo
|
||||
// if getOriginalInfoForName returns false, that means it's new and shouldn't go into the undo
|
||||
// if both exist, and the current best claim is not the same as or the parent to the new best
|
||||
// claim, then ownership has changed and the current height of last takeover should go into
|
||||
// the queue
|
||||
|
@ -2053,7 +2075,7 @@ bool CClaimTrieCache::incrementBlock(insertUndoType& insertUndo, claimQueueRowTy
|
|||
{
|
||||
haveClaimInCache = itCachedNode->second->getBestClaim(claimInCache);
|
||||
}
|
||||
haveClaimInTrie = base->getInfoForName(*itNamesToCheck, claimInTrie);
|
||||
haveClaimInTrie = getOriginalInfoForName(*itNamesToCheck, claimInTrie);
|
||||
bool takeoverHappened = false;
|
||||
if (!haveClaimInTrie)
|
||||
{
|
||||
|
@ -2169,6 +2191,15 @@ bool CClaimTrieCache::incrementBlock(insertUndoType& insertUndo, claimQueueRowTy
|
|||
}
|
||||
}
|
||||
}
|
||||
for (nodeCacheType::const_iterator itOriginals = block_originals.begin(); itOriginals != block_originals.end(); ++itOriginals)
|
||||
{
|
||||
delete itOriginals->second;
|
||||
}
|
||||
block_originals.clear();
|
||||
for (nodeCacheType::const_iterator itCache = cache.begin(); itCache != cache.end(); ++itCache)
|
||||
{
|
||||
block_originals[itCache->first] = new CClaimTrieNode(*(itCache->second));
|
||||
}
|
||||
namesToCheckForTakeover.clear();
|
||||
nCurrentHeight++;
|
||||
return true;
|
||||
|
@ -2226,43 +2257,65 @@ bool CClaimTrieCache::decrementBlock(insertUndoType& insertUndo, claimQueueRowTy
|
|||
return true;
|
||||
}
|
||||
|
||||
bool CClaimTrieCache::getLastTakeoverForName(const std::string& name, int& lastTakeoverHeight) const
|
||||
bool CClaimTrieCache::finalizeDecrement() const
|
||||
{
|
||||
for (nodeCacheType::iterator itOriginals = block_originals.begin(); itOriginals != block_originals.end(); ++itOriginals)
|
||||
{
|
||||
delete itOriginals->second;
|
||||
}
|
||||
block_originals.clear();
|
||||
for (nodeCacheType::const_iterator itCache = cache.begin(); itCache != cache.end(); ++itCache)
|
||||
{
|
||||
block_originals[itCache->first] = new CClaimTrieNode(*(itCache->second));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CClaimTrieCache::getLastTakeoverForName(const std::string& name, int& nLastTakeoverForName) const
|
||||
{
|
||||
if (!fRequireTakeoverHeights)
|
||||
{
|
||||
nLastTakeoverForName = 0;
|
||||
return true;
|
||||
}
|
||||
std::map<std::string, int>::iterator itHeights = cacheTakeoverHeights.find(name);
|
||||
if (itHeights == cacheTakeoverHeights.end())
|
||||
{
|
||||
if (base->getLastTakeoverForName(name, lastTakeoverHeight))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (fRequireTakeoverHeights)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
lastTakeoverHeight = 0;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return base->getLastTakeoverForName(name, nLastTakeoverForName);
|
||||
}
|
||||
lastTakeoverHeight = itHeights->second;
|
||||
nLastTakeoverForName = itHeights->second;
|
||||
return true;
|
||||
}
|
||||
|
||||
int CClaimTrieCache::getNumBlocksOfContinuousOwnership(const std::string& name) const
|
||||
{
|
||||
const CClaimTrieNode* node = NULL;
|
||||
nodeCacheType::const_iterator itCache = cache.find(name);
|
||||
if (itCache != cache.end())
|
||||
{
|
||||
node = itCache->second;
|
||||
}
|
||||
if (!node)
|
||||
{
|
||||
node = base->getNodeForName(name);
|
||||
}
|
||||
if (!node || node->claims.empty())
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
int nLastTakeoverHeight;
|
||||
assert(getLastTakeoverForName(name, nLastTakeoverHeight));
|
||||
return nCurrentHeight - nLastTakeoverHeight;
|
||||
}
|
||||
|
||||
int CClaimTrieCache::getDelayForName(const std::string& name) const
|
||||
{
|
||||
int nHeightOfLastTakeover;
|
||||
if (getLastTakeoverForName(name, nHeightOfLastTakeover))
|
||||
{
|
||||
return std::min((nCurrentHeight - nHeightOfLastTakeover) / base->nProportionalDelayFactor, 4032);
|
||||
}
|
||||
else
|
||||
if (!fRequireTakeoverHeights)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
int nBlocksOfContinuousOwnership = getNumBlocksOfContinuousOwnership(name);
|
||||
return std::min(nBlocksOfContinuousOwnership / base->nProportionalDelayFactor, 4032);
|
||||
}
|
||||
|
||||
uint256 CClaimTrieCache::getBestBlock()
|
||||
|
@ -2285,6 +2338,11 @@ bool CClaimTrieCache::clear() const
|
|||
delete itcache->second;
|
||||
}
|
||||
cache.clear();
|
||||
for (nodeCacheType::iterator itOriginals = block_originals.begin(); itOriginals != block_originals.end(); ++itOriginals)
|
||||
{
|
||||
delete itOriginals->second;
|
||||
}
|
||||
block_originals.clear();
|
||||
dirtyHashes.clear();
|
||||
cacheHashes.clear();
|
||||
claimQueueCache.clear();
|
||||
|
@ -2309,3 +2367,77 @@ bool CClaimTrieCache::flush()
|
|||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
uint256 CClaimTrieCache::getLeafHashForProof(const std::string& currentPosition, unsigned char nodeChar, const CClaimTrieNode* currentNode) const
|
||||
{
|
||||
std::stringstream leafPosition;
|
||||
leafPosition << currentPosition << nodeChar;
|
||||
hashMapType::iterator cachedHash = cacheHashes.find(leafPosition.str());
|
||||
if (cachedHash != cacheHashes.end())
|
||||
{
|
||||
return cachedHash->second;
|
||||
}
|
||||
else
|
||||
{
|
||||
return currentNode->hash;
|
||||
}
|
||||
}
|
||||
|
||||
CClaimTrieProof CClaimTrieCache::getProofForName(const std::string& name) const
|
||||
{
|
||||
if (dirty())
|
||||
getMerkleHash();
|
||||
std::vector<CClaimTrieProofNode> nodes;
|
||||
CClaimTrieNode* current = &(base->root);
|
||||
nodeCacheType::const_iterator cachedNode;
|
||||
bool fNameHasValue = false;
|
||||
COutPoint outPoint;
|
||||
int nHeightOfLastTakeover = 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;
|
||||
CClaimValue claim;
|
||||
bool fNodeHasValue = current->getBestClaim(claim);
|
||||
uint256 valueHash;
|
||||
if (fNodeHasValue)
|
||||
{
|
||||
int nHeightOfLastTakeover;
|
||||
assert(getLastTakeoverForName(currentPosition, nHeightOfLastTakeover));
|
||||
valueHash = getValueHash(claim.outPoint, nHeightOfLastTakeover);
|
||||
}
|
||||
std::vector<std::pair<unsigned char, uint256> > children;
|
||||
CClaimTrieNode* nextCurrent = NULL;
|
||||
for (nodeMapType::const_iterator itChildren = current->children.begin(); itChildren != current->children.end(); ++itChildren)
|
||||
{
|
||||
if (itName == name.end() || itChildren->first != *itName) // Leaf node
|
||||
{
|
||||
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));
|
||||
}
|
||||
}
|
||||
if (currentPosition == name)
|
||||
{
|
||||
fNameHasValue = fNodeHasValue;
|
||||
if (fNameHasValue)
|
||||
{
|
||||
outPoint = claim.outPoint;
|
||||
assert(getLastTakeoverForName(name, nHeightOfLastTakeover));
|
||||
}
|
||||
valueHash.SetNull();
|
||||
}
|
||||
CClaimTrieProofNode node(children, fNodeHasValue, valueHash);
|
||||
nodes.push_back(node);
|
||||
current = nextCurrent;
|
||||
}
|
||||
return CClaimTrieProof(nodes, fNameHasValue, outPoint,
|
||||
nHeightOfLastTakeover);
|
||||
}
|
||||
|
|
|
@ -23,6 +23,8 @@
|
|||
#define SUPPORT_QUEUE_NAME_ROW 'p'
|
||||
#define SUPPORT_EXP_QUEUE_ROW 'x'
|
||||
|
||||
uint256 getValueHash(COutPoint outPoint, int nHeightOfLastTakeover);
|
||||
|
||||
class CClaimValue
|
||||
{
|
||||
public:
|
||||
|
@ -41,8 +43,6 @@ public:
|
|||
, nAmount(nAmount), nEffectiveAmount(nAmount)
|
||||
, nHeight(nHeight), nValidAtHeight(nValidAtHeight)
|
||||
{}
|
||||
|
||||
uint256 GetHash() const;
|
||||
|
||||
ADD_SERIALIZE_METHODS;
|
||||
|
||||
|
@ -394,6 +394,30 @@ private:
|
|||
supportMapType dirtySupportNodes;
|
||||
};
|
||||
|
||||
class CClaimTrieProofNode
|
||||
{
|
||||
public:
|
||||
CClaimTrieProofNode() {};
|
||||
CClaimTrieProofNode(std::vector<std::pair<unsigned char, uint256> > children,
|
||||
bool hasValue, uint256 valHash)
|
||||
: children(children), hasValue(hasValue), valHash(valHash)
|
||||
{};
|
||||
std::vector<std::pair<unsigned char, uint256> > children;
|
||||
bool hasValue;
|
||||
uint256 valHash;
|
||||
};
|
||||
|
||||
class CClaimTrieProof
|
||||
{
|
||||
public:
|
||||
CClaimTrieProof() {};
|
||||
CClaimTrieProof(std::vector<CClaimTrieProofNode> nodes, bool hasValue, COutPoint outPoint, int nHeightOfLastTakeover) : nodes(nodes), hasValue(hasValue), outPoint(outPoint), nHeightOfLastTakeover(nHeightOfLastTakeover) {}
|
||||
std::vector<CClaimTrieProofNode> nodes;
|
||||
bool hasValue;
|
||||
COutPoint outPoint;
|
||||
int nHeightOfLastTakeover;
|
||||
};
|
||||
|
||||
class CClaimTrieCache
|
||||
{
|
||||
public:
|
||||
|
@ -453,12 +477,16 @@ public:
|
|||
bool removeClaimFromTrie(const std::string& name, const COutPoint& outPoint,
|
||||
CClaimValue& claim,
|
||||
bool fCheckTakeover = false) const;
|
||||
CClaimTrieProof getProofForName(const std::string& name) const;
|
||||
|
||||
bool finalizeDecrement() const;
|
||||
private:
|
||||
CClaimTrie* base;
|
||||
|
||||
bool fRequireTakeoverHeights;
|
||||
|
||||
mutable nodeCacheType cache;
|
||||
mutable nodeCacheType block_originals;
|
||||
mutable std::set<std::string> dirtyHashes;
|
||||
mutable hashMapType cacheHashes;
|
||||
mutable claimQueueType claimQueueCache;
|
||||
|
@ -537,6 +565,15 @@ private:
|
|||
bool getLastTakeoverForName(const std::string& name, int& lastTakeoverHeight) const;
|
||||
|
||||
int getDelayForName(const std::string& name) const;
|
||||
|
||||
uint256 getLeafHashForProof(const std::string& currentPosition, unsigned char nodeChar,
|
||||
const CClaimTrieNode* currentNode) const;
|
||||
|
||||
CClaimTrieNode* addNodeToCache(const std::string& position, CClaimTrieNode* original) const;
|
||||
|
||||
bool getOriginalInfoForName(const std::string& name, CClaimValue& claim) const;
|
||||
|
||||
int getNumBlocksOfContinuousOwnership(const std::string& name) const;
|
||||
};
|
||||
|
||||
#endif // BITCOIN_CLAIMTRIE_H
|
||||
|
|
37
src/main.cpp
37
src/main.cpp
|
@ -1694,6 +1694,7 @@ bool DisconnectBlock(const CBlock& block, CValidationState& state, const CBlockI
|
|||
|
||||
// move best block pointer to prevout block
|
||||
view.SetBestBlock(pindex->pprev->GetBlockHash());
|
||||
assert(trieCache.finalizeDecrement());
|
||||
trieCache.setBestBlock(pindex->pprev->GetBlockHash());
|
||||
assert(trieCache.getMerkleHash() == pindex->pprev->hashClaimTrie);
|
||||
|
||||
|
@ -3547,6 +3548,42 @@ bool CVerifyDB::VerifyDB(CCoinsView *coinsview, int nCheckLevel, int nCheckDepth
|
|||
return true;
|
||||
}
|
||||
|
||||
bool GetProofForName(const CBlockIndex* pindexProof, const std::string& name, CClaimTrieProof& proof)
|
||||
{
|
||||
AssertLockHeld(cs_main);
|
||||
if (!chainActive.Contains(pindexProof))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
CCoinsViewCache coins(pcoinsTip);
|
||||
CClaimTrieCache trieCache(pclaimTrie);
|
||||
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);
|
||||
|
|
|
@ -160,6 +160,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, CClaimTrieProof& proof);
|
||||
/** Import blocks from an external file */
|
||||
bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp = NULL);
|
||||
/** Initialize a new block tree database + block data on disk */
|
||||
|
|
|
@ -319,3 +319,126 @@ UniValue getclaimsfortx(const UniValue& params, bool fHelp)
|
|||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
UniValue proofToJSON(const CClaimTrieProof& proof)
|
||||
{
|
||||
UniValue result(UniValue::VOBJ);
|
||||
UniValue nodes(UniValue::VARR);
|
||||
for (std::vector<CClaimTrieProofNode>::const_iterator itNode = proof.nodes.begin(); itNode != proof.nodes.end(); ++itNode)
|
||||
{
|
||||
UniValue node(UniValue::VOBJ);
|
||||
UniValue children(UniValue::VARR);
|
||||
for (std::vector<std::pair<unsigned char, uint256> >::const_iterator itChildren = itNode->children.begin(); itChildren != itNode->children.end(); ++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->hasValue && !itNode->valHash.IsNull())
|
||||
{
|
||||
node.push_back(Pair("valueHash", itNode->valHash.GetHex()));
|
||||
}
|
||||
nodes.push_back(node);
|
||||
}
|
||||
result.push_back(Pair("nodes", nodes));
|
||||
if (proof.hasValue)
|
||||
{
|
||||
result.push_back(Pair("txhash", proof.outPoint.hash.GetHex()));
|
||||
result.push_back(Pair("nOut", (int)proof.outPoint.n));
|
||||
result.push_back(Pair("last takeover height", (int)proof.nHeightOfLastTakeover));
|
||||
}
|
||||
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"
|
||||
" \"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. 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"
|
||||
" 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"
|
||||
" \"last takeover height\" (numeric) the most recent height at\n"
|
||||
" which the value of a name\n"
|
||||
" changed other than through\n"
|
||||
" an update to the winning\n"
|
||||
" bid\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 + 20))
|
||||
throw JSONRPCError(RPC_INTERNAL_ERROR, "Block too deep to generate proof");
|
||||
|
||||
CClaimTrieProof proof;
|
||||
if (!GetProofForName(pblockIndex, strName, proof))
|
||||
throw JSONRPCError(RPC_INTERNAL_ERROR, "Failed to generate proof");
|
||||
|
||||
return proofToJSON(proof);
|
||||
}
|
||||
|
|
|
@ -385,6 +385,7 @@ static const CRPCCommand vRPCCommands[] =
|
|||
{ "claimtrie", "gettotalclaims", &gettotalclaims, true },
|
||||
{ "claimtrie", "gettotalvalueofclaims", &gettotalvalueofclaims, true },
|
||||
{ "claimtrie", "getclaimsfortx", &getclaimsfortx, true },
|
||||
{ "claimtrie", "getnameproof", &getnameproof, true },
|
||||
};
|
||||
|
||||
CRPCTable::CRPCTable()
|
||||
|
|
|
@ -274,6 +274,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);
|
||||
|
||||
|
||||
bool StartRPC();
|
||||
|
|
|
@ -165,16 +165,16 @@ BOOST_AUTO_TEST_CASE(claimtrie_merkle_hash)
|
|||
COutPoint tx6OutPoint(tx6.GetHash(), 0);
|
||||
|
||||
uint256 hash1;
|
||||
hash1.SetHex("4bbf61ec5669c721bf007c71c59f85e6658f1de7b4562078e22f69f8f7ebcafd");
|
||||
hash1.SetHex("71c7b8d35b9a3d7ad9a1272b68972979bbd18589f1efe6f27b0bf260a6ba78fa");
|
||||
|
||||
uint256 hash2;
|
||||
hash2.SetHex("33d00d8f4707eb0abef7297a7ff4975e7354fe1ed81455f28301dbceb939494d");
|
||||
hash2.SetHex("c4fc0e2ad56562a636a0a237a96a5f250ef53495c2cb5edd531f087a8de83722");
|
||||
|
||||
uint256 hash3;
|
||||
hash3.SetHex("eb19bbaeecfd6dee77a8cb69286ac01226caee3c3883f55b86d0ca5a2f4252d7");
|
||||
hash3.SetHex("baf52472bd7da19fe1e35116cfb3bd180d8770ffbe3ae9243df1fb58a14b0975");
|
||||
|
||||
uint256 hash4;
|
||||
hash4.SetHex("a889778ba28603294c1e5c7cec469a9019332ec93838b2f1331ebba547c5fd22");
|
||||
hash4.SetHex("c73232a755bf015f22eaa611b283ff38100f2a23fb6222e86eca363452ba0c51");
|
||||
|
||||
BOOST_CHECK(pclaimTrie->empty());
|
||||
|
||||
|
@ -274,17 +274,21 @@ BOOST_AUTO_TEST_CASE(claimtrie_insert_update_claim)
|
|||
|
||||
std::string sName1("atest");
|
||||
std::string sName2("btest");
|
||||
std::string sName3("atest123");
|
||||
std::string sValue1("testa");
|
||||
std::string sValue2("testb");
|
||||
std::string sValue3("123testa");
|
||||
|
||||
std::vector<unsigned char> vchName1(sName1.begin(), sName1.end());
|
||||
std::vector<unsigned char> vchName2(sName2.begin(), sName2.end());
|
||||
std::vector<unsigned char> vchName3(sName3.begin(), sName3.end());
|
||||
std::vector<unsigned char> vchValue1(sValue1.begin(), sValue1.end());
|
||||
std::vector<unsigned char> vchValue2(sValue2.begin(), sValue2.end());
|
||||
std::vector<unsigned char> vchValue3(sValue3.begin(), sValue3.end());
|
||||
|
||||
std::vector<CTransaction> coinbases;
|
||||
|
||||
BOOST_CHECK(CreateCoinbases(5, coinbases));
|
||||
BOOST_CHECK(CreateCoinbases(6, coinbases));
|
||||
|
||||
uint256 hash0(uint256S("0000000000000000000000000000000000000000000000000000000000000001"));
|
||||
BOOST_CHECK(pclaimTrie->getMerkleHash() == hash0);
|
||||
|
@ -345,6 +349,10 @@ BOOST_AUTO_TEST_CASE(claimtrie_insert_update_claim)
|
|||
CMutableTransaction tx12 = BuildTransaction(tx10);
|
||||
tx12.vout[0].scriptPubKey = CScript() << OP_TRUE;
|
||||
COutPoint tx12OutPoint(tx12.GetHash(), 0);
|
||||
|
||||
CMutableTransaction tx13 = BuildTransaction(coinbases[5]);
|
||||
tx13.vout[0].scriptPubKey = CScript() << OP_CLAIM_NAME << vchName3 << vchValue3 << OP_2DROP << OP_DROP << OP_TRUE;
|
||||
COutPoint tx13OutPoint(tx13.GetHash(), 0);
|
||||
|
||||
CClaimValue val;
|
||||
|
||||
|
@ -396,6 +404,34 @@ BOOST_AUTO_TEST_CASE(claimtrie_insert_update_claim)
|
|||
BOOST_CHECK(pclaimTrie->getInfoForName(sName1, val));
|
||||
BOOST_CHECK(val.outPoint == tx3OutPoint);
|
||||
BOOST_CHECK(pclaimTrie->haveClaimInQueue(sName1, tx9OutPoint, nThrowaway));
|
||||
|
||||
// Disconnect all blocks until the first block, and then reattach them, in memory only
|
||||
|
||||
//FlushStateToDisk();
|
||||
|
||||
CCoinsViewCache coins(pcoinsTip);
|
||||
CClaimTrieCache trieCache(pclaimTrie);
|
||||
CBlockIndex* pindexState = chainActive.Tip();
|
||||
CValidationState state;
|
||||
CBlockIndex* pindex;
|
||||
for (pindex = chainActive.Tip(); pindex && pindex->pprev; pindex=pindex->pprev)
|
||||
{
|
||||
CBlock block;
|
||||
BOOST_CHECK(ReadBlockFromDisk(block, pindex));
|
||||
if (pindex == pindexState && (coins.DynamicMemoryUsage() + pcoinsTip->DynamicMemoryUsage()) <= nCoinCacheUsage)
|
||||
{
|
||||
bool fClean = true;
|
||||
BOOST_CHECK(DisconnectBlock(block, state, pindex, coins, trieCache, &fClean));
|
||||
pindexState = pindex->pprev;
|
||||
}
|
||||
}
|
||||
while (pindex != chainActive.Tip())
|
||||
{
|
||||
pindex = chainActive.Next(pindex);
|
||||
CBlock block;
|
||||
BOOST_CHECK(ReadBlockFromDisk(block, pindex));
|
||||
BOOST_CHECK(ConnectBlock(block, state, pindex, coins, trieCache));
|
||||
}
|
||||
|
||||
// Roll back the last block, make sure tx1 and tx7 are put back in the trie
|
||||
|
||||
|
@ -902,6 +938,30 @@ BOOST_AUTO_TEST_CASE(claimtrie_insert_update_claim)
|
|||
BOOST_CHECK(RemoveBlock(blocks_to_invalidate.back()));
|
||||
blocks_to_invalidate.pop_back();
|
||||
mempool.clear();
|
||||
|
||||
// make sure all claim for names which exist in the trie but have no
|
||||
// values get inserted immediately
|
||||
|
||||
blocks_to_invalidate.push_back(chainActive.Tip()->GetBlockHash());
|
||||
|
||||
AddToMempool(tx13);
|
||||
|
||||
BOOST_CHECK(CreateBlocks(1, 2));
|
||||
|
||||
BOOST_CHECK(!pclaimTrie->empty());
|
||||
BOOST_CHECK(pclaimTrie->queueEmpty());
|
||||
|
||||
AddToMempool(tx1);
|
||||
|
||||
BOOST_CHECK(CreateBlocks(1, 2));
|
||||
BOOST_CHECK(!pclaimTrie->empty());
|
||||
BOOST_CHECK(pclaimTrie->queueEmpty());
|
||||
|
||||
// roll back
|
||||
|
||||
BOOST_CHECK(RemoveBlock(blocks_to_invalidate.back()));
|
||||
blocks_to_invalidate.pop_back();
|
||||
mempool.clear();
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(claimtrie_claim_expiration)
|
||||
|
@ -2323,4 +2383,261 @@ BOOST_AUTO_TEST_CASE(claimtrienode_serialize_unserialize)
|
|||
BOOST_CHECK(n1 == n2);
|
||||
}
|
||||
|
||||
bool verify_proof(const CClaimTrieProof proof, uint256 rootHash, const std::string& name)
|
||||
{
|
||||
uint256 previousComputedHash;
|
||||
std::string computedReverseName;
|
||||
bool verifiedValue = false;
|
||||
|
||||
for (std::vector<CClaimTrieProofNode>::const_reverse_iterator itNodes = proof.nodes.rbegin(); itNodes != proof.nodes.rend(); ++itNodes)
|
||||
{
|
||||
bool foundChildInChain = false;
|
||||
std::vector<unsigned char> vchToHash;
|
||||
for (std::vector<std::pair<unsigned char, uint256> >::const_iterator itChildren = itNodes->children.begin(); itChildren != itNodes->children.end(); ++itChildren)
|
||||
{
|
||||
vchToHash.push_back(itChildren->first);
|
||||
uint256 childHash;
|
||||
if (itChildren->second.IsNull())
|
||||
{
|
||||
if (previousComputedHash.IsNull())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (foundChildInChain)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
foundChildInChain = true;
|
||||
computedReverseName += itChildren->first;
|
||||
childHash = previousComputedHash;
|
||||
}
|
||||
else
|
||||
{
|
||||
childHash = itChildren->second;
|
||||
}
|
||||
vchToHash.insert(vchToHash.end(), childHash.begin(), childHash.end());
|
||||
}
|
||||
if (itNodes != proof.nodes.rbegin() && !foundChildInChain)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (itNodes->hasValue)
|
||||
{
|
||||
uint256 valHash;
|
||||
if (itNodes->valHash.IsNull())
|
||||
{
|
||||
if (itNodes != proof.nodes.rbegin())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!proof.hasValue)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
valHash = getValueHash(proof.outPoint,
|
||||
proof.nHeightOfLastTakeover);
|
||||
|
||||
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<unsigned char> vchHash(hasher.OUTPUT_SIZE);
|
||||
hasher.Write(vchToHash.data(), vchToHash.size());
|
||||
hasher.Finalize(&(vchHash[0]));
|
||||
uint256 calculatedHash(vchHash);
|
||||
previousComputedHash = calculatedHash;
|
||||
}
|
||||
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(claimtrievalue_proof)
|
||||
{
|
||||
BOOST_CHECK(pclaimTrie->nCurrentHeight == chainActive.Height() + 1);
|
||||
|
||||
LOCK(cs_main);
|
||||
|
||||
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<unsigned char> vchName1(sName1.begin(), sName1.end());
|
||||
std::vector<unsigned char> vchValue1(sValue1.begin(), sValue1.end());
|
||||
|
||||
std::vector<unsigned char> vchName2(sName2.begin(), sName2.end());
|
||||
std::vector<unsigned char> vchValue2(sValue2.begin(), sValue2.end());
|
||||
|
||||
std::vector<unsigned char> vchName3(sName3.begin(), sName3.end());
|
||||
std::vector<unsigned char> vchValue3(sValue3.begin(), sValue3.end());
|
||||
|
||||
std::vector<unsigned char> vchName4(sName4.begin(), sName4.end());
|
||||
std::vector<unsigned char> vchValue4(sValue4.begin(), sValue4.end());
|
||||
|
||||
std::vector<unsigned char> vchName7(sName7.begin(), sName7.end());
|
||||
|
||||
std::vector<CTransaction> coinbases;
|
||||
|
||||
BOOST_CHECK(CreateCoinbases(5, coinbases));
|
||||
|
||||
CMutableTransaction tx1 = BuildTransaction(coinbases[0]);
|
||||
tx1.vout[0].scriptPubKey = CScript() << OP_CLAIM_NAME << vchName1 << vchValue1 << OP_2DROP << OP_DROP << OP_TRUE;
|
||||
COutPoint tx1OutPoint(tx1.GetHash(), 0);
|
||||
|
||||
CMutableTransaction tx2 = BuildTransaction(coinbases[1]);
|
||||
tx2.vout[0].scriptPubKey = CScript() << OP_CLAIM_NAME << vchName2 << vchValue2 << OP_2DROP << OP_DROP << OP_TRUE;
|
||||
COutPoint tx2OutPoint(tx2.GetHash(), 0);
|
||||
|
||||
CMutableTransaction tx3 = BuildTransaction(coinbases[2]);
|
||||
tx3.vout[0].scriptPubKey = CScript() << OP_CLAIM_NAME << vchName3 << vchValue3 << OP_2DROP << OP_DROP << OP_TRUE;
|
||||
COutPoint tx3OutPoint(tx3.GetHash(), 0);
|
||||
|
||||
CMutableTransaction tx4 = BuildTransaction(coinbases[3]);
|
||||
tx4.vout[0].scriptPubKey = CScript() << OP_CLAIM_NAME << vchName4 << vchValue4 << OP_2DROP << OP_DROP << OP_TRUE;
|
||||
COutPoint tx4OutPoint(tx4.GetHash(), 0);
|
||||
|
||||
CMutableTransaction tx5 = BuildTransaction(coinbases[4]);
|
||||
tx5.vout[0].scriptPubKey = CScript() << OP_CLAIM_NAME << vchName7 << vchValue4 << OP_2DROP << OP_DROP << OP_TRUE;
|
||||
COutPoint tx5OutPoint(tx5.GetHash(), 0);
|
||||
|
||||
CClaimValue val;
|
||||
|
||||
std::vector<uint256> blocks_to_invalidate;
|
||||
|
||||
// create a claim. verify the expiration event has been scheduled.
|
||||
|
||||
AddToMempool(tx1);
|
||||
AddToMempool(tx2);
|
||||
AddToMempool(tx3);
|
||||
AddToMempool(tx4);
|
||||
|
||||
BOOST_CHECK(CreateBlocks(1, 5));
|
||||
|
||||
blocks_to_invalidate.push_back(chainActive.Tip()->GetBlockHash());
|
||||
|
||||
BOOST_CHECK(!pclaimTrie->empty());
|
||||
BOOST_CHECK(pclaimTrie->queueEmpty());
|
||||
BOOST_CHECK(pclaimTrie->getInfoForName(sName1, val));
|
||||
BOOST_CHECK(val.outPoint == tx1OutPoint);
|
||||
BOOST_CHECK(pclaimTrie->getInfoForName(sName2, val));
|
||||
BOOST_CHECK(val.outPoint == tx2OutPoint);
|
||||
BOOST_CHECK(pclaimTrie->getInfoForName(sName3, val));
|
||||
BOOST_CHECK(val.outPoint == tx3OutPoint);
|
||||
BOOST_CHECK(pclaimTrie->getInfoForName(sName4, val));
|
||||
BOOST_CHECK(val.outPoint == tx4OutPoint);
|
||||
|
||||
CClaimTrieCache cache(pclaimTrie);
|
||||
|
||||
CClaimTrieProof proof;
|
||||
|
||||
proof = cache.getProofForName(sName1);
|
||||
BOOST_CHECK(verify_proof(proof, chainActive.Tip()->hashClaimTrie, sName1));
|
||||
BOOST_CHECK(proof.outPoint == tx1OutPoint);
|
||||
|
||||
proof = cache.getProofForName(sName2);
|
||||
BOOST_CHECK(verify_proof(proof, chainActive.Tip()->hashClaimTrie, sName2));
|
||||
BOOST_CHECK(proof.outPoint == tx2OutPoint);
|
||||
|
||||
proof = cache.getProofForName(sName3);
|
||||
BOOST_CHECK(verify_proof(proof, chainActive.Tip()->hashClaimTrie, sName3));
|
||||
BOOST_CHECK(proof.outPoint == tx3OutPoint);
|
||||
|
||||
proof = cache.getProofForName(sName4);
|
||||
BOOST_CHECK(verify_proof(proof, chainActive.Tip()->hashClaimTrie, sName4));
|
||||
BOOST_CHECK(proof.outPoint == tx4OutPoint);
|
||||
|
||||
proof = cache.getProofForName(sName5);
|
||||
BOOST_CHECK(verify_proof(proof, chainActive.Tip()->hashClaimTrie, sName5));
|
||||
BOOST_CHECK(proof.hasValue == false);
|
||||
|
||||
proof = cache.getProofForName(sName6);
|
||||
BOOST_CHECK(verify_proof(proof, chainActive.Tip()->hashClaimTrie, sName6));
|
||||
BOOST_CHECK(proof.hasValue == false);
|
||||
|
||||
proof = cache.getProofForName(sName7);
|
||||
BOOST_CHECK(verify_proof(proof, chainActive.Tip()->hashClaimTrie, sName7));
|
||||
BOOST_CHECK(proof.hasValue == false);
|
||||
|
||||
AddToMempool(tx5);
|
||||
|
||||
BOOST_CHECK(CreateBlocks(1, 2));
|
||||
|
||||
BOOST_CHECK(pclaimTrie->getInfoForName(sName7, val));
|
||||
BOOST_CHECK(val.outPoint == tx5OutPoint);
|
||||
|
||||
cache = CClaimTrieCache(pclaimTrie);
|
||||
|
||||
proof = cache.getProofForName(sName1);
|
||||
BOOST_CHECK(verify_proof(proof, chainActive.Tip()->hashClaimTrie, sName1));
|
||||
BOOST_CHECK(proof.outPoint == tx1OutPoint);
|
||||
|
||||
proof = cache.getProofForName(sName2);
|
||||
BOOST_CHECK(verify_proof(proof, chainActive.Tip()->hashClaimTrie, sName2));
|
||||
BOOST_CHECK(proof.outPoint == tx2OutPoint);
|
||||
|
||||
proof = cache.getProofForName(sName3);
|
||||
BOOST_CHECK(verify_proof(proof, chainActive.Tip()->hashClaimTrie, sName3));
|
||||
BOOST_CHECK(proof.outPoint == tx3OutPoint);
|
||||
|
||||
proof = cache.getProofForName(sName4);
|
||||
BOOST_CHECK(verify_proof(proof, chainActive.Tip()->hashClaimTrie, sName4));
|
||||
BOOST_CHECK(proof.outPoint == tx4OutPoint);
|
||||
|
||||
proof = cache.getProofForName(sName5);
|
||||
BOOST_CHECK(verify_proof(proof, chainActive.Tip()->hashClaimTrie, sName5));
|
||||
BOOST_CHECK(proof.hasValue == false);
|
||||
|
||||
proof = cache.getProofForName(sName6);
|
||||
BOOST_CHECK(verify_proof(proof, chainActive.Tip()->hashClaimTrie, sName6));
|
||||
BOOST_CHECK(proof.hasValue == false);
|
||||
|
||||
proof = cache.getProofForName(sName7);
|
||||
BOOST_CHECK(verify_proof(proof, chainActive.Tip()->hashClaimTrie, sName7));
|
||||
BOOST_CHECK(proof.outPoint == tx5OutPoint);
|
||||
|
||||
BOOST_CHECK(RemoveBlock(blocks_to_invalidate.back()));
|
||||
blocks_to_invalidate.pop_back();
|
||||
BOOST_CHECK(pclaimTrie->empty());
|
||||
BOOST_CHECK(pclaimTrie->queueEmpty());
|
||||
BOOST_CHECK(blocks_to_invalidate.empty());
|
||||
|
||||
}
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
|
Loading…
Reference in a new issue