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);
|
||||
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());
|
||||
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__);
|
||||
}
|
||||
|
||||
|
@ -1659,6 +1661,8 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex
|
|||
if (blockUndo.vtxundo.size() + 1 != block.vtx.size())
|
||||
return error("DisconnectBlock(): block and undo data inconsistent");
|
||||
|
||||
assert(trieCache.decrementBlock(blockUndo.queueUndo));
|
||||
|
||||
// undo transactions in reverse order
|
||||
for (int i = block.vtx.size() - 1; i >= 0; i--) {
|
||||
const CTransaction &tx = block.vtx[i];
|
||||
|
@ -1691,7 +1695,7 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex
|
|||
assert(vvchParams.size() == 2);
|
||||
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());
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
@ -1849,6 +1853,8 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
|
|||
return state.DoS(100, error("ConnectBlock(): too many sigops"),
|
||||
REJECT_INVALID, "bad-blk-sigops");
|
||||
|
||||
std::map<unsigned int, unsigned int> mNCCUndoHeights;
|
||||
|
||||
if (!tx.IsCoinBase())
|
||||
{
|
||||
if (!view.HaveInputs(tx))
|
||||
|
@ -1884,8 +1890,9 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
|
|||
{
|
||||
assert(vvchParams.size() == 2);
|
||||
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);
|
||||
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__);
|
||||
}
|
||||
}
|
||||
|
@ -1901,28 +1908,38 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
|
|||
assert(vvchParams.size() == 2);
|
||||
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);
|
||||
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__);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CTxUndo undoDummy;
|
||||
if (i > 0) {
|
||||
if (i > 0)
|
||||
{
|
||||
blockundo.vtxundo.push_back(CTxUndo());
|
||||
}
|
||||
UpdateCoins(tx, state, view, i == 0 ? undoDummy : blockundo.vtxundo.back(), pindex->nHeight);
|
||||
|
||||
// The CUndo vector contains all of the
|
||||
// necessary information for putting NCC claims
|
||||
// removed by this block back into the trie.
|
||||
// (As long as the coinbase can't remove any
|
||||
// NCC claims from the trie.)
|
||||
if (i > 0 && !mNCCUndoHeights.empty())
|
||||
{
|
||||
std::vector<CTxInUndo>& txinUndos = blockundo.vtxundo.back().vprevout;
|
||||
for (std::map<unsigned int, unsigned int>::iterator itHeight = mNCCUndoHeights.begin(); itHeight != mNCCUndoHeights.end(); ++itHeight)
|
||||
{
|
||||
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));
|
||||
pos.nTxOffset += ::GetSerializeSize(tx, SER_DISK, CLIENT_VERSION);
|
||||
}
|
||||
|
||||
assert(trieCache.incrementBlock(blockundo.queueUndo));
|
||||
|
||||
if (trieCache.getMerkleHash() != block.hashNCCTrie)
|
||||
return state.DoS(100,
|
||||
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);
|
||||
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__);
|
||||
}
|
||||
}
|
||||
|
@ -317,7 +318,7 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn)
|
|||
{
|
||||
assert(vvchParams.size() == 2);
|
||||
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__);
|
||||
}
|
||||
}
|
||||
|
|
285
src/ncctrie.cpp
285
src/ncctrie.cpp
|
@ -33,14 +33,23 @@ bool CNCCTrieNode::insertValue(CNodeValue val, bool * pfChanged)
|
|||
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());
|
||||
bool fChanged = false;
|
||||
|
||||
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())
|
||||
values.erase(position);
|
||||
else
|
||||
|
@ -67,7 +76,7 @@ bool CNCCTrieNode::removeValue(CNodeValue val, bool * pfChanged)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool CNCCTrieNode::getValue(CNodeValue& value) const
|
||||
bool CNCCTrieNode::getBestValue(CNodeValue& value) const
|
||||
{
|
||||
if (values.empty())
|
||||
return false;
|
||||
|
@ -88,6 +97,11 @@ bool CNCCTrie::empty() const
|
|||
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
|
||||
{
|
||||
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("hash", current->hash.GetHex()));
|
||||
CNodeValue val;
|
||||
if (current->getValue(val))
|
||||
if (current->getBestValue(val))
|
||||
{
|
||||
objNode.push_back(Pair("txid", val.txhash.GetHex()));
|
||||
objNode.push_back(Pair("n", (int)val.nOut));
|
||||
|
@ -131,7 +145,7 @@ bool CNCCTrie::getInfoForName(const std::string& name, CNodeValue& val) const
|
|||
return false;
|
||||
current = itchildren->second;
|
||||
}
|
||||
return current->getValue(val);
|
||||
return current->getBestValue(val);
|
||||
}
|
||||
|
||||
bool CNCCTrie::checkConsistency()
|
||||
|
@ -157,9 +171,9 @@ bool CNCCTrie::recursiveCheckConsistency(CNCCTrieNode* node)
|
|||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
CNodeValue val;
|
||||
bool hasValue = node->getValue(val);
|
||||
bool hasValue = node->getBestValue(val);
|
||||
|
||||
if (hasValue)
|
||||
{
|
||||
|
@ -179,7 +193,30 @@ bool CNCCTrie::recursiveCheckConsistency(CNCCTrieNode* node)
|
|||
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
|
||||
// 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;
|
||||
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;
|
||||
nCurrentHeight = nNewHeight;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -302,25 +356,41 @@ bool CNCCTrie::updateHash(const std::string& name, uint256& hash, CNCCTrieNode**
|
|||
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());
|
||||
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));
|
||||
}
|
||||
|
||||
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;
|
||||
for (nodeCacheType::iterator itcache = changedNodes.begin(); itcache != changedNodes.end(); ++itcache)
|
||||
BatchWriteNode(batch, itcache->first, itcache->second);
|
||||
for (std::vector<std::string>::iterator itname = deletedNames.begin(); itname != deletedNames.end(); ++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('t', nNewHeight);
|
||||
return db.WriteBatch(batch);
|
||||
}
|
||||
|
||||
|
@ -370,6 +440,15 @@ bool CNCCTrie::ReadFromDisk(bool check)
|
|||
if (!InsertFromDisk(name, node))
|
||||
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();
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
|
@ -381,7 +460,7 @@ bool CNCCTrie::ReadFromDisk(bool check)
|
|||
if (check)
|
||||
{
|
||||
LogPrintf("Checking NCC trie consistency...");
|
||||
if( checkConsistency())
|
||||
if (checkConsistency())
|
||||
{
|
||||
LogPrintf("consistent\n");
|
||||
return true;
|
||||
|
@ -427,7 +506,7 @@ bool CNCCTrieCache::recursiveComputeMerkleHash(CNCCTrieNode* tnCurrent, std::str
|
|||
}
|
||||
|
||||
CNodeValue val;
|
||||
bool hasValue = tnCurrent->getValue(val);
|
||||
bool hasValue = tnCurrent->getBestValue(val);
|
||||
|
||||
if (hasValue)
|
||||
{
|
||||
|
@ -477,7 +556,7 @@ bool CNCCTrieCache::empty() const
|
|||
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);
|
||||
CNCCTrieNode* currentNode = &(base->root);
|
||||
|
@ -544,7 +623,7 @@ bool CNCCTrieCache::insertName(const std::string name, uint256 txhash, int nOut,
|
|||
cache[name] = currentNode;
|
||||
}
|
||||
bool fChanged = false;
|
||||
currentNode->insertValue(CNodeValue(txhash, nOut, nAmount, nHeight), &fChanged);
|
||||
currentNode->insertValue(val, &fChanged);
|
||||
if (fChanged)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
CNCCTrieNode* currentNode = &(base->root);
|
||||
|
@ -583,7 +662,6 @@ bool CNCCTrieCache::removeName(const std::string name, uint256 txhash, int nOut)
|
|||
currentNode = childNode->second;
|
||||
continue;
|
||||
}
|
||||
|
||||
LogPrintf("%s: The name %s does not exist in the trie\n", __func__, name.c_str());
|
||||
return false;
|
||||
}
|
||||
|
@ -598,11 +676,16 @@ bool CNCCTrieCache::removeName(const std::string name, uint256 txhash, int nOut)
|
|||
}
|
||||
bool fChanged = false;
|
||||
assert(currentNode != NULL);
|
||||
bool success = currentNode->removeValue(CNodeValue(txhash, nOut), &fChanged);
|
||||
CNodeValue val;
|
||||
bool success = currentNode->removeValue(txhash, nOut, val, &fChanged);
|
||||
if (!success)
|
||||
{
|
||||
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);
|
||||
if (fChanged)
|
||||
{
|
||||
|
@ -692,6 +775,167 @@ bool CNCCTrieCache::recursivePruneName(CNCCTrieNode* tnCurrent, unsigned int nPo
|
|||
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()
|
||||
{
|
||||
if (hashBlock.IsNull())
|
||||
|
@ -714,6 +958,7 @@ bool CNCCTrieCache::clear() const
|
|||
cache.clear();
|
||||
dirtyHashes.clear();
|
||||
cacheHashes.clear();
|
||||
valueQueueCache.clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -721,7 +966,7 @@ bool CNCCTrieCache::flush()
|
|||
{
|
||||
if (dirty())
|
||||
getMerkleHash();
|
||||
bool success = base->update(cache, cacheHashes, hashBlock);
|
||||
bool success = base->update(cache, cacheHashes, hashBlock, valueQueueCache, nCurrentHeight);
|
||||
if (success)
|
||||
{
|
||||
success = clear();
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
#include <vector>
|
||||
#include "json/json_spirit_value.h"
|
||||
|
||||
#define DEFAULT_DELAY 100
|
||||
|
||||
class CNodeValue
|
||||
{
|
||||
public:
|
||||
|
@ -22,9 +24,10 @@ public:
|
|||
uint32_t nOut;
|
||||
CAmount nAmount;
|
||||
int nHeight;
|
||||
int nValidAtHeight;
|
||||
CNodeValue() {};
|
||||
CNodeValue(uint256 txhash, uint32_t nOut) : txhash(txhash), nOut(nOut), nAmount(0), nHeight(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) : txhash(txhash), nOut(nOut), nAmount(0), nHeight(0), nValidAtHeight(0) {}
|
||||
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();
|
||||
|
||||
ADD_SERIALIZE_METHODS;
|
||||
|
@ -35,6 +38,7 @@ public:
|
|||
READWRITE(nOut);
|
||||
READWRITE(nAmount);
|
||||
READWRITE(nHeight);
|
||||
READWRITE(nValidAtHeight);
|
||||
}
|
||||
|
||||
bool operator<(const CNodeValue& other) const
|
||||
|
@ -58,7 +62,7 @@ public:
|
|||
}
|
||||
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
|
||||
{
|
||||
|
@ -81,8 +85,8 @@ public:
|
|||
nodeMapType children;
|
||||
std::vector<CNodeValue> values;
|
||||
bool insertValue(CNodeValue val, bool * fChanged = NULL);
|
||||
bool removeValue(CNodeValue val, bool * fChanged = NULL);
|
||||
bool getValue(CNodeValue& val) const;
|
||||
bool removeValue(uint256& txhash, uint32_t nOut, CNodeValue& val, bool * fChanged = NULL);
|
||||
bool getBestValue(CNodeValue& val) const;
|
||||
bool empty() const {return children.empty() && values.empty();}
|
||||
|
||||
ADD_SERIALIZE_METHODS;
|
||||
|
@ -102,7 +106,8 @@ public:
|
|||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
private:
|
||||
bool getValue(uint256& txhash, uint32_t nOut, CNodeValue& val) const;
|
||||
};
|
||||
|
||||
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, uint256> hashMapType;
|
||||
|
||||
class CNCCTrieCache;
|
||||
|
||||
class CNCCTrie
|
||||
|
@ -131,42 +157,65 @@ public:
|
|||
bool ReadFromDisk(bool check = false);
|
||||
json_spirit::Array dumpToJSON() const;
|
||||
bool getInfoForName(const std::string& name, CNodeValue& val) const;
|
||||
int nCurrentHeight;
|
||||
bool queueEmpty() const;
|
||||
friend class CNCCTrieCache;
|
||||
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 updateHash(const std::string& name, uint256& hash, CNCCTrieNode** pNodeRet);
|
||||
bool recursiveNullify(CNCCTrieNode* node, std::string& name, std::vector<std::string>& deletedNames);
|
||||
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 recursiveDumpToJSON(const std::string& name, const CNCCTrieNode* current, json_spirit::Array& ret) const;
|
||||
CNCCTrieNode root;
|
||||
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
|
||||
{
|
||||
public:
|
||||
CNCCTrieCache(CNCCTrie* base): base(base) {assert(base);}
|
||||
CNCCTrieCache(CNCCTrie* base): base(base), nCurrentHeight(base->nCurrentHeight) {}
|
||||
uint256 getMerkleHash() const;
|
||||
bool empty() const;
|
||||
bool flush();
|
||||
bool dirty() const { return !dirtyHashes.empty(); }
|
||||
bool insertName (const std::string name, uint256 txhash, int nOut, CAmount nAmount, int nDepth) 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) 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();
|
||||
void setBestBlock(const uint256& hashBlock);
|
||||
bool incrementBlock(CNCCTrieQueueUndo& undo) const;
|
||||
bool decrementBlock(CNCCTrieQueueUndo& undo) const;
|
||||
~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:
|
||||
CNCCTrie* base;
|
||||
bool getInfoForName(const std::string name, CNodeValue& val) const;
|
||||
mutable nodeCacheType cache;
|
||||
mutable std::set<std::string> dirtyHashes;
|
||||
mutable hashMapType cacheHashes;
|
||||
mutable valueQueueType valueQueueCache;
|
||||
mutable int nCurrentHeight;
|
||||
uint256 computeHash() const;
|
||||
bool recursiveComputeMerkleHash(CNCCTrieNode* tnCurrent, std::string sPos) const;
|
||||
bool recursivePruneName(CNCCTrieNode* tnCurrent, unsigned int nPos, std::string sName, bool* pfNullified = NULL) 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;
|
||||
};
|
||||
|
||||
|
|
|
@ -246,8 +246,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
|
|||
delete pblocktemplate;
|
||||
mempool.clear();
|
||||
|
||||
/* This has been removed because we don't have that many blocks in the active chain yet.
|
||||
It should be returned when we do.
|
||||
/* TODO: fix this
|
||||
// subsidy changing
|
||||
int nHeight = chainActive.Height();
|
||||
chainActive.Tip()->nHeight = 209999;
|
||||
|
|
|
@ -31,8 +31,23 @@ CMutableTransaction BuildTransaction(const uint256& prevhash)
|
|||
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"));
|
||||
CMutableTransaction tx1 = BuildTransaction(hash0);
|
||||
CMutableTransaction tx2 = BuildTransaction(tx1.GetHash());
|
||||
|
@ -56,19 +71,19 @@ BOOST_AUTO_TEST_CASE(ncctrie_create_insert_remove)
|
|||
BOOST_CHECK(pnccTrie->empty());
|
||||
|
||||
CNCCTrieCache ntState(pnccTrie);
|
||||
ntState.insertName(std::string("test"), tx1.GetHash(), 0, 50, 100);
|
||||
ntState.insertName(std::string("test2"), tx2.GetHash(), 0, 50, 100);
|
||||
ntState.insertClaimIntoTrie(std::string("test"), CNodeValue(tx1.GetHash(), 0, 50, 100, 200));
|
||||
ntState.insertClaimIntoTrie(std::string("test2"), CNodeValue(tx2.GetHash(), 0, 50, 100, 200));
|
||||
|
||||
BOOST_CHECK(pnccTrie->empty());
|
||||
BOOST_CHECK(!ntState.empty());
|
||||
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);
|
||||
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);
|
||||
ntState.insertName(std::string("testtesttesttest"), tx5.GetHash(), 0, 50, 100);
|
||||
ntState.removeName(std::string("testtesttesttest"), tx5.GetHash(), 0);
|
||||
ntState.insertClaimIntoTrie(std::string("testtesttesttest"), CNodeValue(tx5.GetHash(), 0, 50, 100, 200));
|
||||
ntState.removeClaimFromTrie(std::string("testtesttesttest"), tx5.GetHash(), 0, unused);
|
||||
BOOST_CHECK(ntState.getMerkleHash() == hash2);
|
||||
ntState.flush();
|
||||
|
||||
|
@ -77,16 +92,16 @@ BOOST_AUTO_TEST_CASE(ncctrie_create_insert_remove)
|
|||
BOOST_CHECK(pnccTrie->checkConsistency());
|
||||
|
||||
CNCCTrieCache ntState1(pnccTrie);
|
||||
ntState1.removeName(std::string("test"), tx1.GetHash(), 0);
|
||||
ntState1.removeName(std::string("test2"), tx2.GetHash(), 0);
|
||||
ntState1.removeName(std::string("test"), tx3.GetHash(), 0);
|
||||
ntState1.removeName(std::string("tes"), tx4.GetHash(), 0);
|
||||
ntState1.removeClaimFromTrie(std::string("test"), tx1.GetHash(), 0, unused);
|
||||
ntState1.removeClaimFromTrie(std::string("test2"), tx2.GetHash(), 0, unused);
|
||||
ntState1.removeClaimFromTrie(std::string("test"), tx3.GetHash(), 0, unused);
|
||||
ntState1.removeClaimFromTrie(std::string("tes"), tx4.GetHash(), 0, unused);
|
||||
|
||||
BOOST_CHECK(ntState1.getMerkleHash() == hash0);
|
||||
|
||||
CNCCTrieCache ntState2(pnccTrie);
|
||||
ntState2.insertName(std::string("abab"), tx6.GetHash(), 0, 50, 100);
|
||||
ntState2.removeName(std::string("test"), tx1.GetHash(), 0);
|
||||
ntState2.insertClaimIntoTrie(std::string("abab"), CNodeValue(tx6.GetHash(), 0, 50, 100, 200));
|
||||
ntState2.removeClaimFromTrie(std::string("test"), tx1.GetHash(), 0, unused);
|
||||
|
||||
BOOST_CHECK(ntState2.getMerkleHash() == hash3);
|
||||
|
||||
|
@ -97,7 +112,7 @@ BOOST_AUTO_TEST_CASE(ncctrie_create_insert_remove)
|
|||
BOOST_CHECK(pnccTrie->checkConsistency());
|
||||
|
||||
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);
|
||||
ntState3.flush();
|
||||
BOOST_CHECK(!pnccTrie->empty());
|
||||
|
@ -105,7 +120,7 @@ BOOST_AUTO_TEST_CASE(ncctrie_create_insert_remove)
|
|||
BOOST_CHECK(pnccTrie->checkConsistency());
|
||||
|
||||
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);
|
||||
ntState4.flush();
|
||||
BOOST_CHECK(!pnccTrie->empty());
|
||||
|
@ -113,7 +128,7 @@ BOOST_AUTO_TEST_CASE(ncctrie_create_insert_remove)
|
|||
BOOST_CHECK(pnccTrie->checkConsistency());
|
||||
|
||||
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);
|
||||
ntState5.flush();
|
||||
|
@ -122,13 +137,380 @@ BOOST_AUTO_TEST_CASE(ncctrie_create_insert_remove)
|
|||
BOOST_CHECK(pnccTrie->checkConsistency());
|
||||
|
||||
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);
|
||||
ntState6.flush();
|
||||
BOOST_CHECK(!pnccTrie->empty());
|
||||
BOOST_CHECK(pnccTrie->getMerkleHash() == hash2);
|
||||
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)
|
||||
|
@ -137,6 +519,7 @@ BOOST_AUTO_TEST_CASE(ncctrienode_serialize_unserialize)
|
|||
|
||||
CNCCTrieNode n1;
|
||||
CNCCTrieNode n2;
|
||||
CNodeValue throwaway;
|
||||
|
||||
ss << n1;
|
||||
ss >> n2;
|
||||
|
@ -154,8 +537,8 @@ BOOST_AUTO_TEST_CASE(ncctrienode_serialize_unserialize)
|
|||
ss >> n2;
|
||||
BOOST_CHECK(n1 == n2);
|
||||
|
||||
CNodeValue v1(uint256S("0000000000000000000000000000000000000000000000000000000000000001"), 0, 50, 0);
|
||||
CNodeValue v2(uint256S("0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"), 1, 100, 1);
|
||||
CNodeValue v1(uint256S("0000000000000000000000000000000000000000000000000000000000000001"), 0, 50, 0, 100);
|
||||
CNodeValue v2(uint256S("0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"), 1, 100, 1, 101);
|
||||
|
||||
n1.insertValue(v1);
|
||||
BOOST_CHECK(n1 != n2);
|
||||
|
@ -169,13 +552,13 @@ BOOST_AUTO_TEST_CASE(ncctrienode_serialize_unserialize)
|
|||
ss >> n2;
|
||||
BOOST_CHECK(n1 == n2);
|
||||
|
||||
n1.removeValue(v1);
|
||||
n1.removeValue(v1.txhash, v1.nOut, throwaway);
|
||||
BOOST_CHECK(n1 != n2);
|
||||
ss << n1;
|
||||
ss >> n2;
|
||||
BOOST_CHECK(n1 == n2);
|
||||
|
||||
n1.removeValue(v2);
|
||||
n1.removeValue(v2.txhash, v2.nOut, throwaway);
|
||||
BOOST_CHECK(n1 != n2);
|
||||
ss << n1;
|
||||
ss >> n2;
|
||||
|
|
13
src/undo.h
13
src/undo.h
|
@ -7,6 +7,7 @@
|
|||
#define BITCOIN_UNDO_H
|
||||
|
||||
#include "compressor.h"
|
||||
#include "ncctrie.h"
|
||||
#include "primitives/transaction.h"
|
||||
#include "serialize.h"
|
||||
|
||||
|
@ -23,14 +24,16 @@ public:
|
|||
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
|
||||
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(const CTxOut &txoutIn, bool fCoinBaseIn = false, unsigned int nHeightIn = 0, int nVersionIn = 0) : txout(txoutIn), fCoinBase(fCoinBaseIn), nHeight(nHeightIn), nVersion(nVersionIn) { }
|
||||
CTxInUndo() : txout(), fCoinBase(false), nHeight(0), nVersion(0), nNCCValidHeight(0) {}
|
||||
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 {
|
||||
return ::GetSerializeSize(VARINT(nHeight*2+(fCoinBase ? 1 : 0)), nType, nVersion) +
|
||||
(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>
|
||||
|
@ -39,6 +42,7 @@ public:
|
|||
if (nHeight > 0)
|
||||
::Serialize(s, VARINT(this->nVersion), nType, nVersion);
|
||||
::Serialize(s, CTxOutCompressor(REF(txout)), nType, nVersion);
|
||||
::Serialize(s, VARINT(nNCCValidHeight), nType, nVersion);
|
||||
}
|
||||
|
||||
template<typename Stream>
|
||||
|
@ -50,6 +54,7 @@ public:
|
|||
if (nHeight > 0)
|
||||
::Unserialize(s, VARINT(this->nVersion), nType, nVersion);
|
||||
::Unserialize(s, REF(CTxOutCompressor(REF(txout))), nType, nVersion);
|
||||
::Unserialize(s, VARINT(nNCCValidHeight), nType, nVersion);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -73,12 +78,14 @@ class CBlockUndo
|
|||
{
|
||||
public:
|
||||
std::vector<CTxUndo> vtxundo; // for all but the coinbase
|
||||
CNCCTrieQueueUndo queueUndo; // any claims that went from the queue to the trie
|
||||
|
||||
ADD_SERIALIZE_METHODS;
|
||||
|
||||
template <typename Stream, typename Operation>
|
||||
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
|
||||
READWRITE(vtxundo);
|
||||
READWRITE(queueUndo);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in a new issue