Implement a hard fork for extended/infinite claim expiration times #112

Closed
lbrynaut wants to merge 247 commits from claim-expiration into master
7 changed files with 771 additions and 70 deletions
Showing only changes of commit f2a7a5c401 - Show all commits

View file

@ -1630,7 +1630,9 @@ static bool ApplyTxInUndo(const CTxInUndo& undo, CCoinsViewCache& view, CNCCTrie
assert(vvchParams.size() == 2); assert(vvchParams.size() == 2);
std::string name(vvchParams[0].begin(), vvchParams[0].end()); std::string name(vvchParams[0].begin(), vvchParams[0].end());
LogPrintf("%s: Restoring %s to the NCC trie due to a block being disconnected\n", __func__, name.c_str()); LogPrintf("%s: Restoring %s to the NCC trie due to a block being disconnected\n", __func__, name.c_str());
if (!trieCache.insertName(name, out.hash, out.n, undo.txout.nValue, undo.nHeight)) int nValidHeight = undo.nNCCValidHeight;
assert(nValidHeight > 0 && nValidHeight >= coins->nHeight);
if (!trieCache.undoSpendClaim(name, out.hash, out.n, undo.txout.nValue, coins->nHeight, nValidHeight))
LogPrintf("%s: Something went wrong inserting the name\n", __func__); LogPrintf("%s: Something went wrong inserting the name\n", __func__);
} }
@ -1659,6 +1661,8 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex
if (blockUndo.vtxundo.size() + 1 != block.vtx.size()) if (blockUndo.vtxundo.size() + 1 != block.vtx.size())
return error("DisconnectBlock(): block and undo data inconsistent"); return error("DisconnectBlock(): block and undo data inconsistent");
assert(trieCache.decrementBlock(blockUndo.queueUndo));
// undo transactions in reverse order // undo transactions in reverse order
for (int i = block.vtx.size() - 1; i >= 0; i--) { for (int i = block.vtx.size() - 1; i >= 0; i--) {
const CTransaction &tx = block.vtx[i]; const CTransaction &tx = block.vtx[i];
@ -1691,7 +1695,7 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex
assert(vvchParams.size() == 2); assert(vvchParams.size() == 2);
std::string name(vvchParams[0].begin(), vvchParams[0].end()); std::string name(vvchParams[0].begin(), vvchParams[0].end());
LogPrintf("%s: Removing %s from the ncc trie due to its block being disconnected\n", __func__, name.c_str()); LogPrintf("%s: Removing %s from the ncc trie due to its block being disconnected\n", __func__, name.c_str());
if (!trieCache.removeName(name, hash, i)) if (!trieCache.undoAddClaim(name, hash, i, pindex->nHeight))
LogPrintf("%s: Something went wrong removing the name %s in hash %s\n", __func__, name.c_str(), hash.GetHex()); LogPrintf("%s: Something went wrong removing the name %s in hash %s\n", __func__, name.c_str(), hash.GetHex());
} }
} }
@ -1849,6 +1853,8 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
return state.DoS(100, error("ConnectBlock(): too many sigops"), return state.DoS(100, error("ConnectBlock(): too many sigops"),
REJECT_INVALID, "bad-blk-sigops"); REJECT_INVALID, "bad-blk-sigops");
std::map<unsigned int, unsigned int> mNCCUndoHeights;
if (!tx.IsCoinBase()) if (!tx.IsCoinBase())
{ {
if (!view.HaveInputs(tx)) if (!view.HaveInputs(tx))
@ -1884,8 +1890,9 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
{ {
assert(vvchParams.size() == 2); assert(vvchParams.size() == 2);
std::string name(vvchParams[0].begin(), vvchParams[0].end()); std::string name(vvchParams[0].begin(), vvchParams[0].end());
int nValidAtHeight;
LogPrintf("%s: Removing %s from the ncc trie. Tx: %s, nOut: %d\n", __func__, name, txin.prevout.hash.GetHex(), txin.prevout.n); LogPrintf("%s: Removing %s from the ncc trie. Tx: %s, nOut: %d\n", __func__, name, txin.prevout.hash.GetHex(), txin.prevout.n);
if (!trieCache.removeName(name, txin.prevout.hash, txin.prevout.n)) if (!trieCache.spendClaim(name, txin.prevout.hash, txin.prevout.n, coins->nHeight, nValidAtHeight))
LogPrintf("%s: Something went wrong removing the name\n", __func__); LogPrintf("%s: Something went wrong removing the name\n", __func__);
} }
} }
@ -1901,28 +1908,38 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
assert(vvchParams.size() == 2); assert(vvchParams.size() == 2);
std::string name(vvchParams[0].begin(), vvchParams[0].end()); std::string name(vvchParams[0].begin(), vvchParams[0].end());
LogPrintf("%s: Inserting %s into the ncc trie. Tx: %s, nOut: %d\n", __func__, name, tx.GetHash().GetHex(), i); LogPrintf("%s: Inserting %s into the ncc trie. Tx: %s, nOut: %d\n", __func__, name, tx.GetHash().GetHex(), i);
if (!trieCache.insertName(name, tx.GetHash(), i, txout.nValue, pindex->nHeight)) if (!trieCache.addClaim(name, tx.GetHash(), i, txout.nValue, pindex->nHeight))
LogPrintf("%s: Something went wrong inserting the name\n", __func__); LogPrintf("%s: Something went wrong inserting the name\n", __func__);
} }
} }
} }
CTxUndo undoDummy; CTxUndo undoDummy;
if (i > 0) { if (i > 0)
{
blockundo.vtxundo.push_back(CTxUndo()); blockundo.vtxundo.push_back(CTxUndo());
} }
UpdateCoins(tx, state, view, i == 0 ? undoDummy : blockundo.vtxundo.back(), pindex->nHeight); UpdateCoins(tx, state, view, i == 0 ? undoDummy : blockundo.vtxundo.back(), pindex->nHeight);
if (i > 0 && !mNCCUndoHeights.empty())
// The CUndo vector contains all of the {
// necessary information for putting NCC claims std::vector<CTxInUndo>& txinUndos = blockundo.vtxundo.back().vprevout;
// removed by this block back into the trie. for (std::map<unsigned int, unsigned int>::iterator itHeight = mNCCUndoHeights.begin(); itHeight != mNCCUndoHeights.end(); ++itHeight)
// (As long as the coinbase can't remove any {
// NCC claims from the trie.) txinUndos[itHeight->first].nNCCValidHeight = itHeight->second;
}
}
// The CTxUndo vector contains the heights at which NCC claims should be put into the trie.
// This is necessary because some NCC claims are inserted immediately into the trie, and
// others are inserted after a delay, depending on the state of the NCC trie at the time
// that the claim was originally inserted into the blockchain. That state will not be
// available when and if this block is disconnected.
vPos.push_back(std::make_pair(tx.GetHash(), pos)); vPos.push_back(std::make_pair(tx.GetHash(), pos));
pos.nTxOffset += ::GetSerializeSize(tx, SER_DISK, CLIENT_VERSION); pos.nTxOffset += ::GetSerializeSize(tx, SER_DISK, CLIENT_VERSION);
} }
assert(trieCache.incrementBlock(blockundo.queueUndo));
if (trieCache.getMerkleHash() != block.hashNCCTrie) if (trieCache.getMerkleHash() != block.hashNCCTrie)
return state.DoS(100, return state.DoS(100,
error("ConnectBlock() : the merkle root of the NCC trie does not match " error("ConnectBlock() : the merkle root of the NCC trie does not match "

View file

@ -300,7 +300,8 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn)
{ {
assert(vvchParams.size() == 2); assert(vvchParams.size() == 2);
std::string name(vvchParams[0].begin(), vvchParams[0].end()); std::string name(vvchParams[0].begin(), vvchParams[0].end());
if (!trieCache.removeName(name, txin.prevout.hash, txin.prevout.n)) int throwaway;
if (!trieCache.spendClaim(name, txin.prevout.hash, txin.prevout.n, coins->nHeight, throwaway))
LogPrintf("%s: Something went wrong removing the name\n", __func__); LogPrintf("%s: Something went wrong removing the name\n", __func__);
} }
} }
@ -317,7 +318,7 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn)
{ {
assert(vvchParams.size() == 2); assert(vvchParams.size() == 2);
std::string name(vvchParams[0].begin(), vvchParams[0].end()); std::string name(vvchParams[0].begin(), vvchParams[0].end());
if (!trieCache.insertName(name, tx.GetHash(), i, txout.nValue, nHeight)) if (!trieCache.addClaim(name, tx.GetHash(), i, txout.nValue, nHeight))
LogPrintf("%s: Something went wrong inserting the name\n", __func__); LogPrintf("%s: Something went wrong inserting the name\n", __func__);
} }
} }

View file

@ -33,14 +33,23 @@ bool CNCCTrieNode::insertValue(CNodeValue val, bool * pfChanged)
return true; return true;
} }
bool CNCCTrieNode::removeValue(CNodeValue val, bool * pfChanged) bool CNCCTrieNode::removeValue(uint256& txhash, uint32_t nOut, CNodeValue& val, bool * pfChanged)
{ {
LogPrintf("%s: Removing %s from the ncc trie\n", __func__, val.ToString()); LogPrintf("%s: Removing %s from the ncc trie\n", __func__, val.ToString());
bool fChanged = false; bool fChanged = false;
CNodeValue currentTop = values.front(); CNodeValue currentTop = values.front();
std::vector<CNodeValue>::iterator position = std::find(values.begin(), values.end(), val); //std::vector<CNodeValue>::iterator position = std::find(values.begin(), values.end(), val);
std::vector<CNodeValue>::iterator position;
for (position = values.begin(); position != values.end(); ++position)
{
if (position->txhash == txhash && position->nOut == nOut)
{
std::swap(val, *position);
break;
}
}
if (position != values.end()) if (position != values.end())
values.erase(position); values.erase(position);
else else
@ -67,7 +76,7 @@ bool CNCCTrieNode::removeValue(CNodeValue val, bool * pfChanged)
return true; return true;
} }
bool CNCCTrieNode::getValue(CNodeValue& value) const bool CNCCTrieNode::getBestValue(CNodeValue& value) const
{ {
if (values.empty()) if (values.empty())
return false; return false;
@ -88,6 +97,11 @@ bool CNCCTrie::empty() const
return root.empty(); return root.empty();
} }
bool CNCCTrie::queueEmpty() const
{
return valueQueue.empty();
}
bool CNCCTrie::recursiveDumpToJSON(const std::string& name, const CNCCTrieNode* current, json_spirit::Array& ret) const bool CNCCTrie::recursiveDumpToJSON(const std::string& name, const CNCCTrieNode* current, json_spirit::Array& ret) const
{ {
using namespace json_spirit; using namespace json_spirit;
@ -95,7 +109,7 @@ bool CNCCTrie::recursiveDumpToJSON(const std::string& name, const CNCCTrieNode*
objNode.push_back(Pair("name", name)); objNode.push_back(Pair("name", name));
objNode.push_back(Pair("hash", current->hash.GetHex())); objNode.push_back(Pair("hash", current->hash.GetHex()));
CNodeValue val; CNodeValue val;
if (current->getValue(val)) if (current->getBestValue(val))
{ {
objNode.push_back(Pair("txid", val.txhash.GetHex())); objNode.push_back(Pair("txid", val.txhash.GetHex()));
objNode.push_back(Pair("n", (int)val.nOut)); objNode.push_back(Pair("n", (int)val.nOut));
@ -131,7 +145,7 @@ bool CNCCTrie::getInfoForName(const std::string& name, CNodeValue& val) const
return false; return false;
current = itchildren->second; current = itchildren->second;
} }
return current->getValue(val); return current->getBestValue(val);
} }
bool CNCCTrie::checkConsistency() bool CNCCTrie::checkConsistency()
@ -159,7 +173,7 @@ bool CNCCTrie::recursiveCheckConsistency(CNCCTrieNode* node)
} }
CNodeValue val; CNodeValue val;
bool hasValue = node->getValue(val); bool hasValue = node->getBestValue(val);
if (hasValue) if (hasValue)
{ {
@ -179,7 +193,30 @@ bool CNCCTrie::recursiveCheckConsistency(CNCCTrieNode* node)
return calculatedHash == node->hash; return calculatedHash == node->hash;
} }
bool CNCCTrie::update(nodeCacheType& cache, hashMapType& hashes, const uint256& hashBlockIn) valueQueueType::iterator CNCCTrie::getQueueRow(int nHeight)
{
valueQueueType::iterator itQueueRow = valueQueue.find(nHeight);
if (itQueueRow == valueQueue.end())
{
std::vector<CValueQueueEntry> queueRow;
std::pair<valueQueueType::iterator, bool> ret;
ret = valueQueue.insert(std::pair<int, std::vector<CValueQueueEntry> >(nHeight, queueRow));
assert(ret.second);
itQueueRow = ret.first;
}
return itQueueRow;
}
void CNCCTrie::deleteQueueRow(int nHeight)
{
valueQueueType::iterator itQueueRow = valueQueue.find(nHeight);
if (itQueueRow != valueQueue.end())
{
valueQueue.erase(itQueueRow);
}
}
bool CNCCTrie::update(nodeCacheType& cache, hashMapType& hashes, const uint256& hashBlockIn, valueQueueType& queueCache, int nNewHeight)
{ {
// General strategy: the cache is ordered by length, ensuring child // General strategy: the cache is ordered by length, ensuring child
// nodes are always inserted after their parents. Insert each node // nodes are always inserted after their parents. Insert each node
@ -219,8 +256,25 @@ bool CNCCTrie::update(nodeCacheType& cache, hashMapType& hashes, const uint256&
return false; return false;
changedNodes[ithash->first] = pNode; changedNodes[ithash->first] = pNode;
} }
BatchWrite(changedNodes, deletedNames, hashBlockIn); std::vector<int> vChangedQueueRows;
std::vector<int> vDeletedQueueRows;
for (valueQueueType::iterator itQueueCacheRow = queueCache.begin(); itQueueCacheRow != queueCache.end(); ++itQueueCacheRow)
{
if (itQueueCacheRow->second.empty())
{
vDeletedQueueRows.push_back(itQueueCacheRow->first);
deleteQueueRow(itQueueCacheRow->first);
}
else
{
vChangedQueueRows.push_back(itQueueCacheRow->first);
valueQueueType::iterator itQueueRow = getQueueRow(itQueueCacheRow->first);
itQueueRow->second.swap(itQueueCacheRow->second);
}
}
BatchWrite(changedNodes, deletedNames, hashBlockIn, vChangedQueueRows, vDeletedQueueRows, nNewHeight);
hashBlock = hashBlockIn; hashBlock = hashBlockIn;
nCurrentHeight = nNewHeight;
return true; return true;
} }
@ -302,25 +356,41 @@ bool CNCCTrie::updateHash(const std::string& name, uint256& hash, CNCCTrieNode**
return true; return true;
} }
void BatchWriteNode(CLevelDBBatch& batch, const std::string& name, const CNCCTrieNode* pNode) void CNCCTrie::BatchWriteNode(CLevelDBBatch& batch, const std::string& name, const CNCCTrieNode* pNode) const
{ {
LogPrintf("%s: Writing %s to disk with %d values\n", __func__, name, pNode->values.size()); LogPrintf("%s: Writing %s to disk with %d values\n", __func__, name, pNode->values.size());
batch.Write(std::make_pair('n', name), *pNode); batch.Write(std::make_pair('n', name), *pNode);
} }
void BatchEraseNode(CLevelDBBatch& batch, const std::string& name) void CNCCTrie::BatchEraseNode(CLevelDBBatch& batch, const std::string& name) const
{ {
batch.Erase(std::make_pair('n', name)); batch.Erase(std::make_pair('n', name));
} }
bool CNCCTrie::BatchWrite(nodeCacheType& changedNodes, std::vector<std::string>& deletedNames, const uint256& hashBlockIn) void CNCCTrie::BatchWriteQueueRow(CLevelDBBatch& batch, int nRowNum)
{
valueQueueType::iterator itQueueRow = getQueueRow(nRowNum);
batch.Write(std::make_pair('r', nRowNum), itQueueRow->second);
}
void CNCCTrie::BatchEraseQueueRow(CLevelDBBatch& batch, int nRowNum)
{
batch.Erase(std::make_pair('r', nRowNum));
}
bool CNCCTrie::BatchWrite(nodeCacheType& changedNodes, std::vector<std::string>& deletedNames, const uint256& hashBlockIn, std::vector<int> vChangedQueueRows, std::vector<int> vDeletedQueueRows, int nNewHeight)
{ {
CLevelDBBatch batch; CLevelDBBatch batch;
for (nodeCacheType::iterator itcache = changedNodes.begin(); itcache != changedNodes.end(); ++itcache) for (nodeCacheType::iterator itcache = changedNodes.begin(); itcache != changedNodes.end(); ++itcache)
BatchWriteNode(batch, itcache->first, itcache->second); BatchWriteNode(batch, itcache->first, itcache->second);
for (std::vector<std::string>::iterator itname = deletedNames.begin(); itname != deletedNames.end(); ++itname) for (std::vector<std::string>::iterator itname = deletedNames.begin(); itname != deletedNames.end(); ++itname)
BatchEraseNode(batch, *itname); BatchEraseNode(batch, *itname);
for (std::vector<int>::iterator itRowNum = vChangedQueueRows.begin(); itRowNum != vChangedQueueRows.end(); ++itRowNum)
BatchWriteQueueRow(batch, *itRowNum);
for (std::vector<int>::iterator itRowNum = vDeletedQueueRows.begin(); itRowNum != vDeletedQueueRows.end(); ++itRowNum)
BatchEraseQueueRow(batch, *itRowNum);
batch.Write('h', hashBlockIn); batch.Write('h', hashBlockIn);
batch.Write('t', nNewHeight);
return db.WriteBatch(batch); return db.WriteBatch(batch);
} }
@ -370,6 +440,15 @@ bool CNCCTrie::ReadFromDisk(bool check)
if (!InsertFromDisk(name, node)) if (!InsertFromDisk(name, node))
return false; return false;
} }
else if (chType == 'r')
{
leveldb::Slice slValue = pcursor->value();
int nHeight;
ssKey >> nHeight;
CDataStream ssValue(slValue.data(), slValue.data()+slValue.size(), SER_DISK, CLIENT_VERSION);
valueQueueType::iterator itQueueRow = getQueueRow(nHeight);
ssValue >> itQueueRow->second;
}
pcursor->Next(); pcursor->Next();
} }
catch (const std::exception& e) catch (const std::exception& e)
@ -381,7 +460,7 @@ bool CNCCTrie::ReadFromDisk(bool check)
if (check) if (check)
{ {
LogPrintf("Checking NCC trie consistency..."); LogPrintf("Checking NCC trie consistency...");
if( checkConsistency()) if (checkConsistency())
{ {
LogPrintf("consistent\n"); LogPrintf("consistent\n");
return true; return true;
@ -427,7 +506,7 @@ bool CNCCTrieCache::recursiveComputeMerkleHash(CNCCTrieNode* tnCurrent, std::str
} }
CNodeValue val; CNodeValue val;
bool hasValue = tnCurrent->getValue(val); bool hasValue = tnCurrent->getBestValue(val);
if (hasValue) if (hasValue)
{ {
@ -477,7 +556,7 @@ bool CNCCTrieCache::empty() const
return base->empty() && cache.empty(); return base->empty() && cache.empty();
} }
bool CNCCTrieCache::insertName(const std::string name, uint256 txhash, int nOut, CAmount nAmount, int nHeight) const bool CNCCTrieCache::insertClaimIntoTrie(const std::string name, CNodeValue val) const
{ {
assert(base); assert(base);
CNCCTrieNode* currentNode = &(base->root); CNCCTrieNode* currentNode = &(base->root);
@ -544,7 +623,7 @@ bool CNCCTrieCache::insertName(const std::string name, uint256 txhash, int nOut,
cache[name] = currentNode; cache[name] = currentNode;
} }
bool fChanged = false; bool fChanged = false;
currentNode->insertValue(CNodeValue(txhash, nOut, nAmount, nHeight), &fChanged); currentNode->insertValue(val, &fChanged);
if (fChanged) if (fChanged)
{ {
for (std::string::const_iterator itCur = name.begin(); itCur != name.end(); ++itCur) for (std::string::const_iterator itCur = name.begin(); itCur != name.end(); ++itCur)
@ -557,7 +636,7 @@ bool CNCCTrieCache::insertName(const std::string name, uint256 txhash, int nOut,
return true; return true;
} }
bool CNCCTrieCache::removeName(const std::string name, uint256 txhash, int nOut) const bool CNCCTrieCache::removeClaimFromTrie(const std::string name, uint256 txhash, uint32_t nOut, int& nValidAtHeight) const
{ {
assert(base); assert(base);
CNCCTrieNode* currentNode = &(base->root); CNCCTrieNode* currentNode = &(base->root);
@ -583,7 +662,6 @@ bool CNCCTrieCache::removeName(const std::string name, uint256 txhash, int nOut)
currentNode = childNode->second; currentNode = childNode->second;
continue; continue;
} }
LogPrintf("%s: The name %s does not exist in the trie\n", __func__, name.c_str()); LogPrintf("%s: The name %s does not exist in the trie\n", __func__, name.c_str());
return false; return false;
} }
@ -598,11 +676,16 @@ bool CNCCTrieCache::removeName(const std::string name, uint256 txhash, int nOut)
} }
bool fChanged = false; bool fChanged = false;
assert(currentNode != NULL); assert(currentNode != NULL);
bool success = currentNode->removeValue(CNodeValue(txhash, nOut), &fChanged); CNodeValue val;
bool success = currentNode->removeValue(txhash, nOut, val, &fChanged);
if (!success) if (!success)
{ {
LogPrintf("%s: Removing a value was unsuccessful. name = %s, txhash = %s, nOut = %d", __func__, name.c_str(), txhash.GetHex(), nOut); LogPrintf("%s: Removing a value was unsuccessful. name = %s, txhash = %s, nOut = %d", __func__, name.c_str(), txhash.GetHex(), nOut);
} }
else
{
nValidAtHeight = val.nValidAtHeight;
}
assert(success); assert(success);
if (fChanged) if (fChanged)
{ {
@ -692,6 +775,167 @@ bool CNCCTrieCache::recursivePruneName(CNCCTrieNode* tnCurrent, unsigned int nPo
return true; return true;
} }
valueQueueType::iterator CNCCTrieCache::getQueueCacheRow(int nHeight, bool createIfNotExists) const
{
valueQueueType::iterator itQueueRow = valueQueueCache.find(nHeight);
if (itQueueRow == valueQueueCache.end())
{
// Have to make a new row it put in the cache, if createIfNotExists is true
std::vector<CValueQueueEntry> queueRow;
// If the row exists in the base, copy its values into the new row.
valueQueueType::iterator itBaseQueueRow = base->valueQueue.find(nHeight);
if (itBaseQueueRow == base->valueQueue.end())
{
if (!createIfNotExists)
return itQueueRow;
}
else
queueRow = itBaseQueueRow->second;
// Stick the new row in the cache
std::pair<valueQueueType::iterator, bool> ret;
ret = valueQueueCache.insert(std::pair<int, std::vector<CValueQueueEntry> >(nHeight, queueRow));
assert(ret.second);
itQueueRow = ret.first;
}
return itQueueRow;
}
bool CNCCTrieCache::getInfoForName(const std::string name, CNodeValue& val) const
{
nodeCacheType::iterator itcache = cache.find(name);
if (itcache != cache.end())
{
return itcache->second->getBestValue(val);
}
else
{
return base->getInfoForName(name, val);
}
}
bool CNCCTrieCache::addClaim(const std::string name, uint256 txhash, uint32_t nOut, CAmount nAmount, int nHeight) const
{
assert(nHeight == nCurrentHeight);
return addClaimToQueue(name, txhash, nOut, nAmount, nHeight, nHeight + DEFAULT_DELAY);
}
bool CNCCTrieCache::addClaim(const std::string name, uint256 txhash, uint32_t nOut, CAmount nAmount, int nHeight, uint256 prevTxhash, uint32_t nPrevOut) const
{
assert(nHeight == nCurrentHeight);
CNodeValue val;
if (getInfoForName(name, val))
{
if (val.txhash == prevTxhash && val.nOut == nPrevOut)
return addClaimToQueue(name, txhash, nOut, nAmount, nHeight, nHeight);
}
return addClaim(name, txhash, nOut, nAmount, nHeight);
}
bool CNCCTrieCache::undoSpendClaim(const std::string name, uint256 txhash, uint32_t nOut, CAmount nAmount, int nHeight, int nValidAtHeight) const
{
if (nValidAtHeight < nCurrentHeight)
{
CNodeValue val(txhash, nOut, nAmount, nHeight, nValidAtHeight);
CValueQueueEntry entry(name, val);
insertClaimIntoTrie(name, CNodeValue(txhash, nOut, nAmount, nHeight, nValidAtHeight));
}
else
{
addClaimToQueue(name, txhash, nOut, nAmount, nHeight, nValidAtHeight);
}
return true;
}
bool CNCCTrieCache::addClaimToQueue(const std::string name, uint256 txhash, uint32_t nOut, CAmount nAmount, int nHeight, int nValidAtHeight) const
{
CNodeValue val(txhash, nOut, nAmount, nHeight, nValidAtHeight);
CValueQueueEntry entry(name, val);
valueQueueType::iterator itQueueRow = getQueueCacheRow(nValidAtHeight, true);
itQueueRow->second.push_back(entry);
return true;
}
bool CNCCTrieCache::removeClaimFromQueue(const std::string name, uint256 txhash, uint32_t nOut, int nHeightToCheck, int& nValidAtHeight) const
{
valueQueueType::iterator itQueueRow = getQueueCacheRow(nHeightToCheck, false);
if (itQueueRow == valueQueueCache.end())
{
return false;
}
std::vector<CValueQueueEntry>::iterator itQueue;
for (itQueue = itQueueRow->second.begin(); itQueue != itQueueRow->second.end(); ++itQueue)
{
CNodeValue& val = itQueue->val;
if (name == itQueue->name && val.txhash == txhash && val.nOut == nOut)
{
nValidAtHeight = val.nValidAtHeight;
break;
}
}
if (itQueue != itQueueRow->second.end())
{
itQueueRow->second.erase(itQueue);
return true;
}
return false;
}
bool CNCCTrieCache::undoAddClaim(const std::string name, uint256 txhash, uint32_t nOut, int nHeight) const
{
int throwaway;
return removeClaim(name, txhash, nOut, nHeight, throwaway);
}
bool CNCCTrieCache::spendClaim(const std::string name, uint256 txhash, uint32_t nOut, int nHeight, int& nValidAtHeight) const
{
return removeClaim(name, txhash, nOut, nHeight, nValidAtHeight);
}
bool CNCCTrieCache::removeClaim(const std::string name, uint256 txhash, uint32_t nOut, int nHeight, int& nValidAtHeight) const
{
if (nHeight + DEFAULT_DELAY >= nCurrentHeight)
{
if (removeClaimFromQueue(name, txhash, nOut, nHeight + DEFAULT_DELAY, nValidAtHeight))
return true;
if (removeClaimFromQueue(name, txhash, nOut, nHeight, nValidAtHeight))
return true;
}
if (removeClaimFromQueue(name, txhash, nOut, nHeight, nCurrentHeight))
return true;
return removeClaimFromTrie(name, txhash, nOut, nValidAtHeight);
}
bool CNCCTrieCache::incrementBlock(CNCCTrieQueueUndo& undo) const
{
valueQueueType::iterator itQueueRow = getQueueCacheRow(nCurrentHeight, false);
nCurrentHeight++;
if (itQueueRow == valueQueueCache.end())
{
return true;
}
for (std::vector<CValueQueueEntry>::iterator itEntry = itQueueRow->second.begin(); itEntry != itQueueRow->second.end(); ++itEntry)
{
insertClaimIntoTrie(itEntry->name, itEntry->val);
undo.push_back(*itEntry);
}
itQueueRow->second.clear();
return true;
}
bool CNCCTrieCache::decrementBlock(CNCCTrieQueueUndo& undo) const
{
nCurrentHeight--;
valueQueueType::iterator itQueueRow = getQueueCacheRow(nCurrentHeight, true);
for (CNCCTrieQueueUndo::iterator itUndo = undo.begin(); itUndo != undo.end(); ++itUndo)
{
int nValidHeightInTrie;
assert(removeClaimFromTrie(itUndo->name, itUndo->val.txhash, itUndo->val.nOut, nValidHeightInTrie));
assert(nValidHeightInTrie == itUndo->val.nValidAtHeight);
itQueueRow->second.push_back(*itUndo);
}
return true;
}
uint256 CNCCTrieCache::getBestBlock() uint256 CNCCTrieCache::getBestBlock()
{ {
if (hashBlock.IsNull()) if (hashBlock.IsNull())
@ -714,6 +958,7 @@ bool CNCCTrieCache::clear() const
cache.clear(); cache.clear();
dirtyHashes.clear(); dirtyHashes.clear();
cacheHashes.clear(); cacheHashes.clear();
valueQueueCache.clear();
return true; return true;
} }
@ -721,7 +966,7 @@ bool CNCCTrieCache::flush()
{ {
if (dirty()) if (dirty())
getMerkleHash(); getMerkleHash();
bool success = base->update(cache, cacheHashes, hashBlock); bool success = base->update(cache, cacheHashes, hashBlock, valueQueueCache, nCurrentHeight);
if (success) if (success)
{ {
success = clear(); success = clear();

View file

@ -15,6 +15,8 @@
#include <vector> #include <vector>
#include "json/json_spirit_value.h" #include "json/json_spirit_value.h"
#define DEFAULT_DELAY 100
class CNodeValue class CNodeValue
{ {
public: public:
@ -22,9 +24,10 @@ public:
uint32_t nOut; uint32_t nOut;
CAmount nAmount; CAmount nAmount;
int nHeight; int nHeight;
int nValidAtHeight;
CNodeValue() {}; CNodeValue() {};
CNodeValue(uint256 txhash, uint32_t nOut) : txhash(txhash), nOut(nOut), nAmount(0), nHeight(0) {} //CNodeValue(uint256 txhash, uint32_t nOut) : txhash(txhash), nOut(nOut), nAmount(0), nHeight(0), nValidAtHeight(0) {}
CNodeValue(uint256 txhash, uint32_t nOut, CAmount nAmount, int nHeight) : txhash(txhash), nOut(nOut), nAmount(nAmount), nHeight(nHeight) {} CNodeValue(uint256 txhash, uint32_t nOut, CAmount nAmount, int nHeight, int nValidAtHeight) : txhash(txhash), nOut(nOut), nAmount(nAmount), nHeight(nHeight), nValidAtHeight(nValidAtHeight) {}
std::string ToString(); std::string ToString();
ADD_SERIALIZE_METHODS; ADD_SERIALIZE_METHODS;
@ -35,6 +38,7 @@ public:
READWRITE(nOut); READWRITE(nOut);
READWRITE(nAmount); READWRITE(nAmount);
READWRITE(nHeight); READWRITE(nHeight);
READWRITE(nValidAtHeight);
} }
bool operator<(const CNodeValue& other) const bool operator<(const CNodeValue& other) const
@ -58,7 +62,7 @@ public:
} }
bool operator==(const CNodeValue& other) const bool operator==(const CNodeValue& other) const
{ {
return txhash == other.txhash && nOut == other.nOut; return txhash == other.txhash && nOut == other.nOut && nAmount == other.nAmount && nHeight == other.nHeight && nValidAtHeight == other.nValidAtHeight;
} }
bool operator!=(const CNodeValue& other) const bool operator!=(const CNodeValue& other) const
{ {
@ -81,8 +85,8 @@ public:
nodeMapType children; nodeMapType children;
std::vector<CNodeValue> values; std::vector<CNodeValue> values;
bool insertValue(CNodeValue val, bool * fChanged = NULL); bool insertValue(CNodeValue val, bool * fChanged = NULL);
bool removeValue(CNodeValue val, bool * fChanged = NULL); bool removeValue(uint256& txhash, uint32_t nOut, CNodeValue& val, bool * fChanged = NULL);
bool getValue(CNodeValue& val) const; bool getBestValue(CNodeValue& val) const;
bool empty() const {return children.empty() && values.empty();} bool empty() const {return children.empty() && values.empty();}
ADD_SERIALIZE_METHODS; ADD_SERIALIZE_METHODS;
@ -102,7 +106,8 @@ public:
{ {
return !(*this == other); return !(*this == other);
} }
private:
bool getValue(uint256& txhash, uint32_t nOut, CNodeValue& val) const;
}; };
struct nodenamecompare struct nodenamecompare
@ -115,9 +120,30 @@ struct nodenamecompare
} }
}; };
class CValueQueueEntry
{
public:
CValueQueueEntry() {}
CValueQueueEntry(std::string name, CNodeValue val) : name(name), val(val) {}
std::string name;
CNodeValue val;
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
READWRITE(name);
READWRITE(val);
}
};
typedef std::map<int, std::vector<CValueQueueEntry> > valueQueueType;
typedef std::vector<CValueQueueEntry> CNCCTrieQueueUndo;
typedef std::map<std::string, CNCCTrieNode*, nodenamecompare> nodeCacheType; typedef std::map<std::string, CNCCTrieNode*, nodenamecompare> nodeCacheType;
typedef std::map<std::string, uint256> hashMapType; typedef std::map<std::string, uint256> hashMapType;
class CNCCTrieCache; class CNCCTrieCache;
class CNCCTrie class CNCCTrie
@ -131,42 +157,65 @@ public:
bool ReadFromDisk(bool check = false); bool ReadFromDisk(bool check = false);
json_spirit::Array dumpToJSON() const; json_spirit::Array dumpToJSON() const;
bool getInfoForName(const std::string& name, CNodeValue& val) const; bool getInfoForName(const std::string& name, CNodeValue& val) const;
int nCurrentHeight;
bool queueEmpty() const;
friend class CNCCTrieCache; friend class CNCCTrieCache;
private: private:
bool update(nodeCacheType& cache, hashMapType& hashes, const uint256& hashBlock); bool update(nodeCacheType& cache, hashMapType& hashes, const uint256& hashBlock, valueQueueType& queueCache, int nNewHeight);
bool updateName(const std::string& name, CNCCTrieNode* updatedNode, std::vector<std::string>& deletedNames, CNCCTrieNode** pNodeRet); bool updateName(const std::string& name, CNCCTrieNode* updatedNode, std::vector<std::string>& deletedNames, CNCCTrieNode** pNodeRet);
bool updateHash(const std::string& name, uint256& hash, CNCCTrieNode** pNodeRet); bool updateHash(const std::string& name, uint256& hash, CNCCTrieNode** pNodeRet);
bool recursiveNullify(CNCCTrieNode* node, std::string& name, std::vector<std::string>& deletedNames); bool recursiveNullify(CNCCTrieNode* node, std::string& name, std::vector<std::string>& deletedNames);
bool recursiveCheckConsistency(CNCCTrieNode* node); bool recursiveCheckConsistency(CNCCTrieNode* node);
bool BatchWrite(nodeCacheType& changedNodes, std::vector<std::string>& deletedNames, const uint256& hashBlock); bool BatchWrite(nodeCacheType& changedNodes, std::vector<std::string>& deletedNames, const uint256& hashBlock, std::vector<int> vChangedQueueRows, std::vector<int> vDeletedQueueRows, int nNewHeight);
bool InsertFromDisk(const std::string& name, CNCCTrieNode* node); bool InsertFromDisk(const std::string& name, CNCCTrieNode* node);
bool recursiveDumpToJSON(const std::string& name, const CNCCTrieNode* current, json_spirit::Array& ret) const; bool recursiveDumpToJSON(const std::string& name, const CNCCTrieNode* current, json_spirit::Array& ret) const;
CNCCTrieNode root; CNCCTrieNode root;
uint256 hashBlock; uint256 hashBlock;
valueQueueType valueQueue;
valueQueueType::iterator getQueueRow(int nHeight);
void deleteQueueRow(int nHeight);
void BatchWriteNode(CLevelDBBatch& batch, const std::string& name, const CNCCTrieNode* pNode) const;
void BatchEraseNode(CLevelDBBatch& batch, const std::string& nome) const;
void BatchWriteQueueRow(CLevelDBBatch& batch, int nRowNum);
void BatchEraseQueueRow(CLevelDBBatch& batch, int nRowNum);
}; };
class CNCCTrieCache class CNCCTrieCache
{ {
public: public:
CNCCTrieCache(CNCCTrie* base): base(base) {assert(base);} CNCCTrieCache(CNCCTrie* base): base(base), nCurrentHeight(base->nCurrentHeight) {}
uint256 getMerkleHash() const; uint256 getMerkleHash() const;
bool empty() const; bool empty() const;
bool flush(); bool flush();
bool dirty() const { return !dirtyHashes.empty(); } bool dirty() const { return !dirtyHashes.empty(); }
bool insertName (const std::string name, uint256 txhash, int nOut, CAmount nAmount, int nDepth) const; bool addClaim(const std::string name, uint256 txhash, uint32_t nOut, CAmount nAmount, int nHeight) const;
bool removeName (const std::string name, uint256 txhash, int nOut) const; bool addClaim(const std::string name, uint256 txhash, uint32_t nOut, CAmount nAmount, int nHeight, uint256 prevTxhash, uint32_t nPrevOut) const;
bool undoAddClaim(const std::string name, uint256 txhash, uint32_t nOut, int nHeight) const;
bool spendClaim(const std::string name, uint256 txhash, uint32_t nOut, int nHeight, int& nValidAtHeight) const;
bool undoSpendClaim(const std::string name, uint256 txhash, uint32_t nOut, CAmount nAmount, int nHeight, int nValidAtHeight) const;
uint256 getBestBlock(); uint256 getBestBlock();
void setBestBlock(const uint256& hashBlock); void setBestBlock(const uint256& hashBlock);
bool incrementBlock(CNCCTrieQueueUndo& undo) const;
bool decrementBlock(CNCCTrieQueueUndo& undo) const;
~CNCCTrieCache() { clear(); } ~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;
private: private:
CNCCTrie* base; CNCCTrie* base;
bool getInfoForName(const std::string name, CNodeValue& val) const;
mutable nodeCacheType cache; mutable nodeCacheType cache;
mutable std::set<std::string> dirtyHashes; mutable std::set<std::string> dirtyHashes;
mutable hashMapType cacheHashes; mutable hashMapType cacheHashes;
mutable valueQueueType valueQueueCache;
mutable int nCurrentHeight;
uint256 computeHash() const; uint256 computeHash() const;
bool recursiveComputeMerkleHash(CNCCTrieNode* tnCurrent, std::string sPos) const; bool recursiveComputeMerkleHash(CNCCTrieNode* tnCurrent, std::string sPos) const;
bool recursivePruneName(CNCCTrieNode* tnCurrent, unsigned int nPos, std::string sName, bool* pfNullified = NULL) const; bool recursivePruneName(CNCCTrieNode* tnCurrent, unsigned int nPos, std::string sName, bool* pfNullified = NULL) const;
bool clear() const; bool clear() const;
bool removeClaim(const std::string name, uint256 txhash, uint32_t nOut, int nHeight, int& nValidAtHeight) const;
bool addClaimToQueue(const std::string name, uint256 txhash, uint32_t nOut, CAmount nAmount, int nHeight, int nValidAtHeight) const;
bool removeClaimFromQueue(const std::string name, uint256 txhash, uint32_t nOut, int nHeightToCheck, int& nValidAtHeight) const;
valueQueueType::iterator getQueueCacheRow(int nHeight, bool createIfNotExists) const;
uint256 hashBlock; uint256 hashBlock;
}; };

View file

@ -246,8 +246,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
delete pblocktemplate; delete pblocktemplate;
mempool.clear(); mempool.clear();
/* This has been removed because we don't have that many blocks in the active chain yet. /* TODO: fix this
It should be returned when we do.
// subsidy changing // subsidy changing
int nHeight = chainActive.Height(); int nHeight = chainActive.Height();
chainActive.Tip()->nHeight = 209999; chainActive.Tip()->nHeight = 209999;

View file

@ -31,8 +31,23 @@ CMutableTransaction BuildTransaction(const uint256& prevhash)
return tx; return tx;
} }
BOOST_AUTO_TEST_CASE(ncctrie_create_insert_remove) void AttachBlock(CNCCTrieCache& cache, std::vector<CNCCTrieQueueUndo>& block_undos)
{ {
CNCCTrieQueueUndo undo;
cache.incrementBlock(undo);
block_undos.push_back(undo);
}
void DetachBlock(CNCCTrieCache& cache, std::vector<CNCCTrieQueueUndo>& block_undos)
{
CNCCTrieQueueUndo& undo = block_undos.back();
cache.decrementBlock(undo);
block_undos.pop_back();
}
BOOST_AUTO_TEST_CASE(ncctrie_merkle_hash)
{
int unused;
uint256 hash0(uint256S("0000000000000000000000000000000000000000000000000000000000000001")); uint256 hash0(uint256S("0000000000000000000000000000000000000000000000000000000000000001"));
CMutableTransaction tx1 = BuildTransaction(hash0); CMutableTransaction tx1 = BuildTransaction(hash0);
CMutableTransaction tx2 = BuildTransaction(tx1.GetHash()); CMutableTransaction tx2 = BuildTransaction(tx1.GetHash());
@ -56,19 +71,19 @@ BOOST_AUTO_TEST_CASE(ncctrie_create_insert_remove)
BOOST_CHECK(pnccTrie->empty()); BOOST_CHECK(pnccTrie->empty());
CNCCTrieCache ntState(pnccTrie); CNCCTrieCache ntState(pnccTrie);
ntState.insertName(std::string("test"), tx1.GetHash(), 0, 50, 100); ntState.insertClaimIntoTrie(std::string("test"), CNodeValue(tx1.GetHash(), 0, 50, 100, 200));
ntState.insertName(std::string("test2"), tx2.GetHash(), 0, 50, 100); ntState.insertClaimIntoTrie(std::string("test2"), CNodeValue(tx2.GetHash(), 0, 50, 100, 200));
BOOST_CHECK(pnccTrie->empty()); BOOST_CHECK(pnccTrie->empty());
BOOST_CHECK(!ntState.empty()); BOOST_CHECK(!ntState.empty());
BOOST_CHECK(ntState.getMerkleHash() == hash1); BOOST_CHECK(ntState.getMerkleHash() == hash1);
ntState.insertName(std::string("test"), tx3.GetHash(), 0, 50, 101); ntState.insertClaimIntoTrie(std::string("test"), CNodeValue(tx3.GetHash(), 0, 50, 101, 201));
BOOST_CHECK(ntState.getMerkleHash() == hash1); BOOST_CHECK(ntState.getMerkleHash() == hash1);
ntState.insertName(std::string("tes"), tx4.GetHash(), 0, 50, 100); ntState.insertClaimIntoTrie(std::string("tes"), CNodeValue(tx4.GetHash(), 0, 50, 100, 200));
BOOST_CHECK(ntState.getMerkleHash() == hash2); BOOST_CHECK(ntState.getMerkleHash() == hash2);
ntState.insertName(std::string("testtesttesttest"), tx5.GetHash(), 0, 50, 100); ntState.insertClaimIntoTrie(std::string("testtesttesttest"), CNodeValue(tx5.GetHash(), 0, 50, 100, 200));
ntState.removeName(std::string("testtesttesttest"), tx5.GetHash(), 0); ntState.removeClaimFromTrie(std::string("testtesttesttest"), tx5.GetHash(), 0, unused);
BOOST_CHECK(ntState.getMerkleHash() == hash2); BOOST_CHECK(ntState.getMerkleHash() == hash2);
ntState.flush(); ntState.flush();
@ -77,16 +92,16 @@ BOOST_AUTO_TEST_CASE(ncctrie_create_insert_remove)
BOOST_CHECK(pnccTrie->checkConsistency()); BOOST_CHECK(pnccTrie->checkConsistency());
CNCCTrieCache ntState1(pnccTrie); CNCCTrieCache ntState1(pnccTrie);
ntState1.removeName(std::string("test"), tx1.GetHash(), 0); ntState1.removeClaimFromTrie(std::string("test"), tx1.GetHash(), 0, unused);
ntState1.removeName(std::string("test2"), tx2.GetHash(), 0); ntState1.removeClaimFromTrie(std::string("test2"), tx2.GetHash(), 0, unused);
ntState1.removeName(std::string("test"), tx3.GetHash(), 0); ntState1.removeClaimFromTrie(std::string("test"), tx3.GetHash(), 0, unused);
ntState1.removeName(std::string("tes"), tx4.GetHash(), 0); ntState1.removeClaimFromTrie(std::string("tes"), tx4.GetHash(), 0, unused);
BOOST_CHECK(ntState1.getMerkleHash() == hash0); BOOST_CHECK(ntState1.getMerkleHash() == hash0);
CNCCTrieCache ntState2(pnccTrie); CNCCTrieCache ntState2(pnccTrie);
ntState2.insertName(std::string("abab"), tx6.GetHash(), 0, 50, 100); ntState2.insertClaimIntoTrie(std::string("abab"), CNodeValue(tx6.GetHash(), 0, 50, 100, 200));
ntState2.removeName(std::string("test"), tx1.GetHash(), 0); ntState2.removeClaimFromTrie(std::string("test"), tx1.GetHash(), 0, unused);
BOOST_CHECK(ntState2.getMerkleHash() == hash3); BOOST_CHECK(ntState2.getMerkleHash() == hash3);
@ -97,7 +112,7 @@ BOOST_AUTO_TEST_CASE(ncctrie_create_insert_remove)
BOOST_CHECK(pnccTrie->checkConsistency()); BOOST_CHECK(pnccTrie->checkConsistency());
CNCCTrieCache ntState3(pnccTrie); CNCCTrieCache ntState3(pnccTrie);
ntState3.insertName(std::string("test"), tx1.GetHash(), 0, 50, 100); ntState3.insertClaimIntoTrie(std::string("test"), CNodeValue(tx1.GetHash(), 0, 50, 100, 200));
BOOST_CHECK(ntState3.getMerkleHash() == hash4); BOOST_CHECK(ntState3.getMerkleHash() == hash4);
ntState3.flush(); ntState3.flush();
BOOST_CHECK(!pnccTrie->empty()); BOOST_CHECK(!pnccTrie->empty());
@ -105,7 +120,7 @@ BOOST_AUTO_TEST_CASE(ncctrie_create_insert_remove)
BOOST_CHECK(pnccTrie->checkConsistency()); BOOST_CHECK(pnccTrie->checkConsistency());
CNCCTrieCache ntState4(pnccTrie); CNCCTrieCache ntState4(pnccTrie);
ntState4.removeName(std::string("abab"), tx6.GetHash(), 0); ntState4.removeClaimFromTrie(std::string("abab"), tx6.GetHash(), 0, unused);
BOOST_CHECK(ntState4.getMerkleHash() == hash2); BOOST_CHECK(ntState4.getMerkleHash() == hash2);
ntState4.flush(); ntState4.flush();
BOOST_CHECK(!pnccTrie->empty()); BOOST_CHECK(!pnccTrie->empty());
@ -113,7 +128,7 @@ BOOST_AUTO_TEST_CASE(ncctrie_create_insert_remove)
BOOST_CHECK(pnccTrie->checkConsistency()); BOOST_CHECK(pnccTrie->checkConsistency());
CNCCTrieCache ntState5(pnccTrie); CNCCTrieCache ntState5(pnccTrie);
ntState5.removeName(std::string("test"), tx3.GetHash(), 0); ntState5.removeClaimFromTrie(std::string("test"), tx3.GetHash(), 0, unused);
BOOST_CHECK(ntState5.getMerkleHash() == hash2); BOOST_CHECK(ntState5.getMerkleHash() == hash2);
ntState5.flush(); ntState5.flush();
@ -122,13 +137,380 @@ BOOST_AUTO_TEST_CASE(ncctrie_create_insert_remove)
BOOST_CHECK(pnccTrie->checkConsistency()); BOOST_CHECK(pnccTrie->checkConsistency());
CNCCTrieCache ntState6(pnccTrie); CNCCTrieCache ntState6(pnccTrie);
ntState6.insertName(std::string("test"), tx3.GetHash(), 0, 50, 101); ntState6.insertClaimIntoTrie(std::string("test"), CNodeValue(tx3.GetHash(), 0, 50, 101, 201));
BOOST_CHECK(ntState6.getMerkleHash() == hash2); BOOST_CHECK(ntState6.getMerkleHash() == hash2);
ntState6.flush(); ntState6.flush();
BOOST_CHECK(!pnccTrie->empty()); BOOST_CHECK(!pnccTrie->empty());
BOOST_CHECK(pnccTrie->getMerkleHash() == hash2); BOOST_CHECK(pnccTrie->getMerkleHash() == hash2);
BOOST_CHECK(pnccTrie->checkConsistency()); BOOST_CHECK(pnccTrie->checkConsistency());
CNCCTrieCache ntState7(pnccTrie);
ntState7.removeClaimFromTrie(std::string("test"), tx3.GetHash(), 0, unused);
ntState7.removeClaimFromTrie(std::string("test"), tx1.GetHash(), 0, unused);
ntState7.removeClaimFromTrie(std::string("tes"), tx4.GetHash(), 0, unused);
ntState7.removeClaimFromTrie(std::string("test2"), tx2.GetHash(), 0, unused);
BOOST_CHECK(ntState7.getMerkleHash() == hash0);
ntState7.flush();
BOOST_CHECK(pnccTrie->empty());
BOOST_CHECK(pnccTrie->getMerkleHash() == hash0);
BOOST_CHECK(pnccTrie->checkConsistency());
}
BOOST_AUTO_TEST_CASE(ncctrie_insert_update_claim)
{
std::vector<CNCCTrieQueueUndo> block_undos;
int start_block = pnccTrie->nCurrentHeight;
int current_block = start_block;
uint256 hash0(uint256S("0000000000000000000000000000000000000000000000000000000000000001"));
CMutableTransaction tx1 = BuildTransaction(hash0);
CMutableTransaction tx2 = BuildTransaction(tx1.GetHash());
CMutableTransaction tx3 = BuildTransaction(tx2.GetHash());
CMutableTransaction tx4 = BuildTransaction(tx3.GetHash());
int tx1_height, tx2_height, tx3_height, tx4_height, tx1_undo_height, tx3_undo_height;
BOOST_CHECK(pnccTrie->getMerkleHash() == hash0);
CNCCTrieCache ntState(pnccTrie);
tx1_height = current_block;
ntState.addClaim(std::string("atest"), tx1.GetHash(), 0, 50, tx1_height);
tx3_height = current_block;
ntState.addClaim(std::string("btest"), tx3.GetHash(), 0, 50, tx3_height);
AttachBlock(ntState, block_undos);
current_block++;
ntState.flush();
for (; current_block < start_block + 100; ++current_block)
{
CNCCTrieCache s(pnccTrie);
AttachBlock(s, block_undos);
s.flush();
}
CNodeValue v;
BOOST_CHECK(!pnccTrie->getInfoForName(std::string("atest"), v));
BOOST_CHECK(!pnccTrie->getInfoForName(std::string("btest"), v));
CNCCTrieCache ntState1(pnccTrie);
AttachBlock(ntState1, block_undos);
ntState1.flush();
current_block++;
CNCCTrieCache ntState2(pnccTrie);
BOOST_CHECK(pnccTrie->getInfoForName(std::string("atest"), v));
BOOST_CHECK(pnccTrie->getInfoForName(std::string("btest"), v));
BOOST_CHECK(ntState2.spendClaim(std::string("atest"), tx1.GetHash(), 0, tx1_height, tx1_undo_height));
BOOST_CHECK(ntState2.spendClaim(std::string("btest"), tx3.GetHash(), 0, tx3_height, tx3_undo_height));
tx2_height = current_block;
ntState2.addClaim(std::string("atest"), tx2.GetHash(), 0, 50, tx2_height, tx1.GetHash(), 0);
tx4_height = current_block;
ntState2.addClaim(std::string("btest"), tx4.GetHash(), 0, 50, tx4_height);
AttachBlock(ntState2, block_undos);
current_block++;
ntState2.flush();
BOOST_CHECK(pnccTrie->getInfoForName(std::string("atest"), v));
BOOST_CHECK(!pnccTrie->getInfoForName(std::string("btest"), v));
BOOST_CHECK(v.txhash == tx2.GetHash());
CNCCTrieCache ntState3(pnccTrie);
DetachBlock(ntState3, block_undos);
current_block--;
BOOST_CHECK(ntState3.undoAddClaim(std::string("atest"), tx2.GetHash(), 0, tx2_height));
BOOST_CHECK(ntState3.undoAddClaim(std::string("btest"), tx4.GetHash(), 0, tx4_height));
ntState3.undoSpendClaim(std::string("atest"), tx1.GetHash(), 0, 50, tx1_height, tx1_undo_height);
ntState3.undoSpendClaim(std::string("btest"), tx3.GetHash(), 0, 50, tx3_height, tx3_undo_height);
ntState3.flush();
for (; current_block > start_block; --current_block)
{
CNCCTrieCache s(pnccTrie);
DetachBlock(s, block_undos);
s.flush();
}
CNCCTrieCache ntState4(pnccTrie);
BOOST_CHECK(ntState4.undoAddClaim(std::string("atest"), tx1.GetHash(), 0, tx1_height));
BOOST_CHECK(ntState4.undoAddClaim(std::string("btest"), tx3.GetHash(), 0, tx3_height));
ntState4.flush();
BOOST_CHECK(pnccTrie->empty());
BOOST_CHECK(pnccTrie->getMerkleHash() == hash0);
BOOST_CHECK(pnccTrie->queueEmpty());
}
BOOST_AUTO_TEST_CASE(ncctrie_undo)
{
std::vector<CNCCTrieQueueUndo> block_undos;
int start_block = pnccTrie->nCurrentHeight;
int current_block = start_block;
CNodeValue val;
uint256 hash0(uint256S("0000000000000000000000000000000000000000000000000000000000000001"));
CMutableTransaction tx1 = BuildTransaction(hash0);
CMutableTransaction tx2 = BuildTransaction(tx1.GetHash());
int tx1_height, tx2_height;
int tx1_undo_height, tx2_undo_height;
BOOST_CHECK(pnccTrie->getMerkleHash() == hash0);
BOOST_CHECK(pnccTrie->queueEmpty());
CNCCTrieCache ntState(pnccTrie);
std::string name("a");
/* Test undoing a claim before the claim gets into the trie */
// make claim at start_block
tx1_height = current_block;
ntState.addClaim(name, tx1.GetHash(), 0, 50, tx1_height);
AttachBlock(ntState, block_undos);
current_block++;
ntState.flush();
BOOST_CHECK(!pnccTrie->queueEmpty());
// undo block and remove claim
DetachBlock(ntState, block_undos);
current_block--;
ntState.undoAddClaim(name, tx1.GetHash(), 0, tx1_height);
ntState.flush();
BOOST_CHECK(pnccTrie->queueEmpty());
/* Test undoing a claim that has gotten into the trie */
// make claim at start_block
tx1_height = current_block;
ntState.addClaim(name, tx1.GetHash(), 0, 50, tx1_height);
AttachBlock(ntState, block_undos);
current_block++;
ntState.flush();
BOOST_CHECK(!pnccTrie->queueEmpty());
// move to block start_block + 101
for (; current_block < start_block + 101; ++current_block)
{
AttachBlock(ntState, block_undos);
ntState.flush();
}
BOOST_CHECK(!pnccTrie->empty());
BOOST_CHECK(pnccTrie->queueEmpty());
// undo block and remove claim
for (; current_block > start_block; --current_block)
{
DetachBlock(ntState, block_undos);
ntState.flush();
}
ntState.undoAddClaim(name, tx1.GetHash(), 0, tx1_height);
ntState.flush();
BOOST_CHECK(pnccTrie->empty());
BOOST_CHECK(pnccTrie->queueEmpty());
/* Test undoing a spend which involves a claim just inserted into the queue */
// make and spend claim at start_block
tx1_height = current_block;
ntState.addClaim(name, tx1.GetHash(), 0, 50, tx1_height);
ntState.spendClaim(name, tx1.GetHash(), 0, tx1_height, tx1_undo_height);
AttachBlock(ntState, block_undos);
current_block++;
ntState.flush();
BOOST_CHECK(pnccTrie->empty());
BOOST_CHECK(pnccTrie->queueEmpty());
// roll back the block
DetachBlock(ntState, block_undos);
current_block--;
ntState.undoSpendClaim(name, tx1.GetHash(), 0, 50, tx1_height, tx1_undo_height);
ntState.undoAddClaim(name, tx1.GetHash(), 0, tx1_height);
ntState.flush();
BOOST_CHECK(pnccTrie->empty());
BOOST_CHECK(pnccTrie->queueEmpty());
/* Test undoing a spend which involves a claim inserted into the queue by a previous block*/
// make claim at block start_block
tx1_height = current_block;
ntState.addClaim(name, tx1.GetHash(), 0, 50, tx1_height);
AttachBlock(ntState, block_undos);
current_block++;
ntState.flush();
// spend the claim in block start_block + 50
for (; current_block < start_block + 50; ++current_block)
{
AttachBlock(ntState, block_undos);
ntState.flush();
}
BOOST_CHECK(pnccTrie->empty());
BOOST_CHECK(!pnccTrie->queueEmpty());
ntState.spendClaim(name, tx1.GetHash(), 0, tx1_height, tx1_undo_height);
AttachBlock(ntState, block_undos);
++current_block;
ntState.flush();
BOOST_CHECK(pnccTrie->empty());
BOOST_CHECK(pnccTrie->queueEmpty());
// attach block start_block + 100 and make sure nothing is inserted
for (; current_block < start_block + 101; ++current_block)
{
AttachBlock(ntState, block_undos);
ntState.flush();
}
BOOST_CHECK(pnccTrie->empty());
BOOST_CHECK(pnccTrie->queueEmpty());
// roll back to block start_block + 50 and undo the spend
for (; current_block > start_block + 50; --current_block)
{
DetachBlock(ntState, block_undos);
ntState.flush();
}
ntState.undoSpendClaim(name, tx1.GetHash(), 0, 50, tx1_height, tx1_undo_height);
ntState.flush();
BOOST_CHECK(pnccTrie->empty());
BOOST_CHECK(!pnccTrie->queueEmpty());
// make sure it still gets inserted at block start_block + 100
for (; current_block < start_block + 100; ++current_block)
{
AttachBlock(ntState, block_undos);
ntState.flush();
}
BOOST_CHECK(pnccTrie->empty());
BOOST_CHECK(!pnccTrie->queueEmpty());
AttachBlock(ntState, block_undos);
++current_block;
ntState.flush();
BOOST_CHECK(!pnccTrie->empty());
BOOST_CHECK(pnccTrie->queueEmpty());
/* Test undoing a spend which involves a claim in the trie */
// spend the claim at block start_block + 150
for (; current_block < start_block + 150; ++current_block)
{
AttachBlock(ntState, block_undos);
ntState.flush();
}
BOOST_CHECK(!pnccTrie->empty());
BOOST_CHECK(pnccTrie->queueEmpty());
ntState.spendClaim(name, tx1.GetHash(), 0, tx1_height, tx1_undo_height);
AttachBlock(ntState, block_undos);
++current_block;
ntState.flush();
BOOST_CHECK(pnccTrie->empty());
BOOST_CHECK(pnccTrie->queueEmpty());
// roll back the block
DetachBlock(ntState, block_undos);
--current_block;
ntState.undoSpendClaim(name, tx1.GetHash(), 0, 50, tx1_height, tx1_undo_height);
ntState.flush();
BOOST_CHECK(!pnccTrie->empty());
BOOST_CHECK(pnccTrie->queueEmpty());
/* Test undoing an spent update which updated the best claim to a name*/
// update the claim at block start_block + 150
tx2_height = current_block;
ntState.spendClaim(name, tx1.GetHash(), 0, tx1_height, tx1_undo_height);
ntState.addClaim(name, tx2.GetHash(), 0, 75, tx2_height, tx1.GetHash(), 0);
AttachBlock(ntState, block_undos);
++current_block;
ntState.flush();
BOOST_CHECK(!pnccTrie->empty());
BOOST_CHECK(pnccTrie->queueEmpty());
// move forward a bit
for (; current_block < start_block + 200; ++current_block)
{
AttachBlock(ntState, block_undos);
ntState.flush();
}
// spend the update
ntState.spendClaim(name, tx2.GetHash(), 0, tx2_height, tx2_undo_height);
AttachBlock(ntState, block_undos);
++current_block;
ntState.flush();
BOOST_CHECK(pnccTrie->empty());
BOOST_CHECK(pnccTrie->queueEmpty());
// undo the spend
DetachBlock(ntState, block_undos);
--current_block;
ntState.undoSpendClaim(name, tx2.GetHash(), 0, 75, tx2_height, tx2_undo_height);
ntState.flush();
BOOST_CHECK(!pnccTrie->empty());
BOOST_CHECK(pnccTrie->queueEmpty());
/* Test undoing a spent update which updated a claim still in the queue */
// roll everything back to block start_block + 50
for (; current_block > start_block + 151; --current_block)
{
DetachBlock(ntState, block_undos);
ntState.flush();
}
DetachBlock(ntState, block_undos);
--current_block;
ntState.undoAddClaim(name, tx2.GetHash(), 0, tx2_height);
ntState.undoSpendClaim(name, tx1.GetHash(), 0, 50, tx1_height, tx1_undo_height);
ntState.flush();
for (; current_block > start_block + 50; --current_block)
{
DetachBlock(ntState, block_undos);
ntState.flush();
}
BOOST_CHECK(pnccTrie->empty());
BOOST_CHECK(!pnccTrie->queueEmpty());
// update the claim at block start_block + 50
tx2_height = current_block;
ntState.spendClaim(name, tx1.GetHash(), 0, tx1_height, tx1_undo_height);
ntState.addClaim(name, tx2.GetHash(), 0, 75, tx2_height, tx1.GetHash(), 0);
AttachBlock(ntState, block_undos);
++current_block;
ntState.flush();
BOOST_CHECK(pnccTrie->empty());
BOOST_CHECK(!pnccTrie->queueEmpty());
// check that it gets inserted at block start_block + 150
for (; current_block < start_block + 150; ++current_block)
{
AttachBlock(ntState, block_undos);
ntState.flush();
}
BOOST_CHECK(pnccTrie->empty());
BOOST_CHECK(!pnccTrie->queueEmpty());
AttachBlock(ntState, block_undos);
++current_block;
ntState.flush();
BOOST_CHECK(!pnccTrie->empty());
BOOST_CHECK(pnccTrie->queueEmpty());
// roll back to start_block
for (; current_block > start_block + 50; --current_block)
{
DetachBlock(ntState, block_undos);
ntState.flush();
}
ntState.undoAddClaim(name, tx2.GetHash(), 0, tx2_height);
ntState.undoSpendClaim(name, tx1.GetHash(), 0, 75, tx1_height, tx1_undo_height);
ntState.flush();
for (; current_block > start_block; --current_block)
{
DetachBlock(ntState, block_undos);
ntState.flush();
}
ntState.undoAddClaim(name, tx1.GetHash(), 0, tx1_height);
ntState.flush();
BOOST_CHECK(pnccTrie->empty());
BOOST_CHECK(pnccTrie->queueEmpty());
BOOST_CHECK(pnccTrie->nCurrentHeight == start_block);
} }
BOOST_AUTO_TEST_CASE(ncctrienode_serialize_unserialize) BOOST_AUTO_TEST_CASE(ncctrienode_serialize_unserialize)
@ -137,6 +519,7 @@ BOOST_AUTO_TEST_CASE(ncctrienode_serialize_unserialize)
CNCCTrieNode n1; CNCCTrieNode n1;
CNCCTrieNode n2; CNCCTrieNode n2;
CNodeValue throwaway;
ss << n1; ss << n1;
ss >> n2; ss >> n2;
@ -154,8 +537,8 @@ BOOST_AUTO_TEST_CASE(ncctrienode_serialize_unserialize)
ss >> n2; ss >> n2;
BOOST_CHECK(n1 == n2); BOOST_CHECK(n1 == n2);
CNodeValue v1(uint256S("0000000000000000000000000000000000000000000000000000000000000001"), 0, 50, 0); CNodeValue v1(uint256S("0000000000000000000000000000000000000000000000000000000000000001"), 0, 50, 0, 100);
CNodeValue v2(uint256S("0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"), 1, 100, 1); CNodeValue v2(uint256S("0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"), 1, 100, 1, 101);
n1.insertValue(v1); n1.insertValue(v1);
BOOST_CHECK(n1 != n2); BOOST_CHECK(n1 != n2);
@ -169,13 +552,13 @@ BOOST_AUTO_TEST_CASE(ncctrienode_serialize_unserialize)
ss >> n2; ss >> n2;
BOOST_CHECK(n1 == n2); BOOST_CHECK(n1 == n2);
n1.removeValue(v1); n1.removeValue(v1.txhash, v1.nOut, throwaway);
BOOST_CHECK(n1 != n2); BOOST_CHECK(n1 != n2);
ss << n1; ss << n1;
ss >> n2; ss >> n2;
BOOST_CHECK(n1 == n2); BOOST_CHECK(n1 == n2);
n1.removeValue(v2); n1.removeValue(v2.txhash, v2.nOut, throwaway);
BOOST_CHECK(n1 != n2); BOOST_CHECK(n1 != n2);
ss << n1; ss << n1;
ss >> n2; ss >> n2;

View file

@ -7,6 +7,7 @@
#define BITCOIN_UNDO_H #define BITCOIN_UNDO_H
#include "compressor.h" #include "compressor.h"
#include "ncctrie.h"
#include "primitives/transaction.h" #include "primitives/transaction.h"
#include "serialize.h" #include "serialize.h"
@ -23,14 +24,16 @@ public:
bool fCoinBase; // if the outpoint was the last unspent: whether it belonged to a coinbase bool fCoinBase; // if the outpoint was the last unspent: whether it belonged to a coinbase
unsigned int nHeight; // if the outpoint was the last unspent: its height unsigned int nHeight; // if the outpoint was the last unspent: its height
int nVersion; // if the outpoint was the last unspent: its version int nVersion; // if the outpoint was the last unspent: its version
unsigned int nNCCValidHeight; // If the outpoint was an NCC claim, the height at which the claim should be inserted into the trie
CTxInUndo() : txout(), fCoinBase(false), nHeight(0), nVersion(0) {} CTxInUndo() : txout(), fCoinBase(false), nHeight(0), nVersion(0), nNCCValidHeight(0) {}
CTxInUndo(const CTxOut &txoutIn, bool fCoinBaseIn = false, unsigned int nHeightIn = 0, int nVersionIn = 0) : txout(txoutIn), fCoinBase(fCoinBaseIn), nHeight(nHeightIn), nVersion(nVersionIn) { } CTxInUndo(const CTxOut &txoutIn, bool fCoinBaseIn = false, unsigned int nHeightIn = 0, int nVersionIn = 0, unsigned int nNCCValidHeight = 0) : txout(txoutIn), fCoinBase(fCoinBaseIn), nHeight(nHeightIn), nVersion(nVersionIn), nNCCValidHeight(nNCCValidHeight) { }
unsigned int GetSerializeSize(int nType, int nVersion) const { unsigned int GetSerializeSize(int nType, int nVersion) const {
return ::GetSerializeSize(VARINT(nHeight*2+(fCoinBase ? 1 : 0)), nType, nVersion) + return ::GetSerializeSize(VARINT(nHeight*2+(fCoinBase ? 1 : 0)), nType, nVersion) +
(nHeight > 0 ? ::GetSerializeSize(VARINT(this->nVersion), nType, nVersion) : 0) + (nHeight > 0 ? ::GetSerializeSize(VARINT(this->nVersion), nType, nVersion) : 0) +
::GetSerializeSize(CTxOutCompressor(REF(txout)), nType, nVersion); ::GetSerializeSize(CTxOutCompressor(REF(txout)), nType, nVersion) +
::GetSerializeSize(VARINT(nNCCValidHeight), nType, nVersion);
} }
template<typename Stream> template<typename Stream>
@ -39,6 +42,7 @@ public:
if (nHeight > 0) if (nHeight > 0)
::Serialize(s, VARINT(this->nVersion), nType, nVersion); ::Serialize(s, VARINT(this->nVersion), nType, nVersion);
::Serialize(s, CTxOutCompressor(REF(txout)), nType, nVersion); ::Serialize(s, CTxOutCompressor(REF(txout)), nType, nVersion);
::Serialize(s, VARINT(nNCCValidHeight), nType, nVersion);
} }
template<typename Stream> template<typename Stream>
@ -50,6 +54,7 @@ public:
if (nHeight > 0) if (nHeight > 0)
::Unserialize(s, VARINT(this->nVersion), nType, nVersion); ::Unserialize(s, VARINT(this->nVersion), nType, nVersion);
::Unserialize(s, REF(CTxOutCompressor(REF(txout))), nType, nVersion); ::Unserialize(s, REF(CTxOutCompressor(REF(txout))), nType, nVersion);
::Unserialize(s, VARINT(nNCCValidHeight), nType, nVersion);
} }
}; };
@ -73,12 +78,14 @@ class CBlockUndo
{ {
public: public:
std::vector<CTxUndo> vtxundo; // for all but the coinbase std::vector<CTxUndo> vtxundo; // for all but the coinbase
CNCCTrieQueueUndo queueUndo; // any claims that went from the queue to the trie
ADD_SERIALIZE_METHODS; ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation> template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
READWRITE(vtxundo); READWRITE(vtxundo);
READWRITE(queueUndo);
} }
}; };