Implement a hard fork for extended/infinite claim expiration times #112
7 changed files with 771 additions and 70 deletions
39
src/main.cpp
39
src/main.cpp
|
@ -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 "
|
||||||
|
|
|
@ -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__);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
281
src/ncctrie.cpp
281
src/ncctrie.cpp
|
@ -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)
|
||||||
|
@ -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();
|
||||||
|
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
13
src/undo.h
13
src/undo.h
|
@ -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);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue