diff --git a/src/claimtrie.cpp b/src/claimtrie.cpp index a00c14a1f..f5dad6af4 100644 --- a/src/claimtrie.cpp +++ b/src/claimtrie.cpp @@ -28,35 +28,35 @@ uint256 CNodeValue::GetHash() const return valHash; } -bool CClaimTrieNode::insertValue(CNodeValue val, bool * pfChanged) +bool CClaimTrieNode::insertValue(CNodeValue val)//, bool * pfChanged) { LogPrintf("%s: Inserting %s:%d (amount: %d) into the claim trie\n", __func__, val.txhash.ToString(), val.nOut, val.nAmount); - bool fChanged = false; + //bool fChanged = false; - if (values.empty()) - { - values.push_back(val); - fChanged = true; - } - else - { - CNodeValue currentTop = values.front(); - values.push_back(val); - std::make_heap(values.begin(), values.end()); - if (currentTop != values.front()) - fChanged = true; - } - if (pfChanged) - *pfChanged = fChanged; + //if (values.empty()) + //{ + values.push_back(val); + // fChanged = true; + //} + //else + //{ + // CNodeValue currentTop = values.front(); + // values.push_back(val); + // std::make_heap(values.begin(), values.end()); + // if (currentTop != values.front()) + // fChanged = true; + //} + //if (pfChanged) + // *pfChanged = fChanged; return true; } -bool CClaimTrieNode::removeValue(uint256& txhash, uint32_t nOut, CNodeValue& val, bool * pfChanged) +bool CClaimTrieNode::removeValue(uint256& txhash, uint32_t nOut, CNodeValue& val)//, bool * pfChanged) { LogPrintf("%s: Removing txid: %s, nOut: %d from the claim trie\n", __func__, txhash.ToString(), nOut); - bool fChanged = false; + //bool fChanged = false; - CNodeValue currentTop = values.front(); + //CNodeValue currentTop = values.front(); std::vector::iterator position; for (position = values.begin(); position != values.end(); ++position) @@ -77,18 +77,19 @@ bool CClaimTrieNode::removeValue(uint256& txhash, uint32_t nOut, CNodeValue& val { LogPrintf("\ttxid: %s, nOut: %d\n", values[i].txhash.ToString(), values[i].nOut); } + std::cout << "couldnt find the thing" << std::endl; return false; } - if (!values.empty()) - { - std::make_heap(values.begin(), values.end()); - if (currentTop != values.front()) - fChanged = true; - } - else - fChanged = true; - if (pfChanged) - *pfChanged = fChanged; + //if (!values.empty()) + //{ + // std::make_heap(values.begin(), values.end()); + // if (currentTop != values.front()) + // fChanged = true; + //} + //else + // fChanged = true; + //if (pfChanged) + // *pfChanged = fChanged; return true; } @@ -111,6 +112,11 @@ bool CClaimTrieNode::haveValue(const uint256& txhash, uint32_t nOut) const return false; } +void CClaimTrieNode::reorderValues() +{ + std::make_heap(values.begin(), values.end()); +} + uint256 CClaimTrie::getMerkleHash() { return root.hash; @@ -121,13 +127,8 @@ bool CClaimTrie::empty() const return root.empty(); } -bool CClaimTrie::queueEmpty() const +bool CClaimTrie::keyTypeEmpty(char key) const { - for (valueQueueType::const_iterator itRow = dirtyQueueRows.begin(); itRow != dirtyQueueRows.end(); ++itRow) - { - if (!itRow->second.empty()) - return false; - } boost::scoped_ptr pcursor(const_cast(&db)->NewIterator()); pcursor->SeekToFirst(); @@ -139,7 +140,7 @@ bool CClaimTrie::queueEmpty() const CDataStream ssKey(slKey.data(), slKey.data()+slKey.size(), SER_DISK, CLIENT_VERSION); char chType; ssKey >> chType; - if (chType == 'r') + if (chType == key) { return false; } @@ -153,6 +154,16 @@ bool CClaimTrie::queueEmpty() const return true; } +bool CClaimTrie::queueEmpty() const +{ + for (valueQueueType::const_iterator itRow = dirtyQueueRows.begin(); itRow != dirtyQueueRows.end(); ++itRow) + { + if (!itRow->second.empty()) + return false; + } + return keyTypeEmpty(QUEUE_ROW); +} + bool CClaimTrie::expirationQueueEmpty() const { for (valueQueueType::const_iterator itRow = dirtyExpirationQueueRows.begin(); itRow != dirtyExpirationQueueRows.end(); ++itRow) @@ -160,29 +171,27 @@ bool CClaimTrie::expirationQueueEmpty() const if (!itRow->second.empty()) return false; } - boost::scoped_ptr pcursor(const_cast(&db)->NewIterator()); - pcursor->SeekToFirst(); + return keyTypeEmpty(EXP_QUEUE_ROW); +} - while(pcursor->Valid()) +bool CClaimTrie::supportEmpty() const +{ + for (supportMapType::const_iterator itNode = dirtySupportNodes.begin(); itNode != dirtySupportNodes.end(); ++itNode) { - try - { - leveldb::Slice slKey = pcursor->key(); - CDataStream ssKey(slKey.data(), slKey.data()+slKey.size(), SER_DISK, CLIENT_VERSION); - char chType; - ssKey >> chType; - if (chType == 'e') - { - return false; - } - } - catch (const std::exception& e) - { - return error("%s: Deserialize or I/O error - %s", __func__, e.what()); - } - pcursor->Next(); + if (!itNode->second.empty()) + return false; } - return true; + return keyTypeEmpty(SUPPORT); +} + +bool CClaimTrie::supportQueueEmpty() const +{ + for (supportValueQueueType::const_iterator itRow = dirtySupportQueueRows.begin(); itRow != dirtySupportQueueRows.end(); ++itRow) + { + if (!itRow->second.empty()) + return false; + } + return keyTypeEmpty(SUPPORT_QUEUE); } void CClaimTrie::setExpirationTime(int t) @@ -361,7 +370,7 @@ bool CClaimTrie::getQueueRow(int nHeight, std::vector& row) row = itQueueRow->second; return true; } - return db.Read(std::make_pair('r', nHeight), row); + return db.Read(std::make_pair(QUEUE_ROW, nHeight), row); } bool CClaimTrie::getExpirationQueueRow(int nHeight, std::vector& row) @@ -372,7 +381,7 @@ bool CClaimTrie::getExpirationQueueRow(int nHeight, std::vectorsecond; return true; } - return db.Read(std::make_pair('e', nHeight), row); + return db.Read(std::make_pair(EXP_QUEUE_ROW, nHeight), row); } void CClaimTrie::updateQueueRow(int nHeight, std::vector& row) @@ -403,7 +412,57 @@ void CClaimTrie::updateExpirationRow(int nHeight, std::vector& itQueueRow->second.swap(row); } -bool CClaimTrie::update(nodeCacheType& cache, hashMapType& hashes, const uint256& hashBlockIn, valueQueueType& queueCache, valueQueueType& expirationQueueCache, int nNewHeight) +void CClaimTrie::updateSupportMap(const std::string& name, supportMapNodeType& node) +{ + supportMapType::iterator itNode = dirtySupportNodes.find(name); + if (itNode == dirtySupportNodes.end()) + { + supportMapNodeType newNode; + std::pair ret; + ret = dirtySupportNodes.insert(std::pair(name, newNode)); + assert(ret.second); + itNode = ret.first; + } + itNode->second.swap(node); +} + +void CClaimTrie::updateSupportQueue(int nHeight, std::vector& row) +{ + supportValueQueueType::iterator itQueueRow = dirtySupportQueueRows.find(nHeight); + if (itQueueRow == dirtySupportQueueRows.end()) + { + std::vector newRow; + std::pair ret; + ret = dirtySupportQueueRows.insert(std::pair >(nHeight, newRow)); + assert(ret.second); + itQueueRow = ret.first; + } + itQueueRow->second.swap(row); +} + +bool CClaimTrie::getSupportNode(std::string name, supportMapNodeType& node) +{ + supportMapType::iterator itNode = dirtySupportNodes.find(name); + if (itNode != dirtySupportNodes.end()) + { + node = itNode->second; + return true; + } + return db.Read(std::make_pair(SUPPORT, name), node); +} + +bool CClaimTrie::getSupportQueueRow(int nHeight, std::vector& row) +{ + supportValueQueueType::iterator itQueueRow = dirtySupportQueueRows.find(nHeight); + if (itQueueRow != dirtySupportQueueRows.end()) + { + row = itQueueRow->second; + return true; + } + return db.Read(std::make_pair(SUPPORT_QUEUE, nHeight), row); +} + +bool CClaimTrie::update(nodeCacheType& cache, hashMapType& hashes, const uint256& hashBlockIn, valueQueueType& queueCache, valueQueueType& expirationQueueCache, int nNewHeight, supportMapType& supportCache, supportValueQueueType& supportQueueCache) { // General strategy: the cache is ordered by length, ensuring child // nodes are always inserted after their parents. Insert each node @@ -445,6 +504,14 @@ bool CClaimTrie::update(nodeCacheType& cache, hashMapType& hashes, const uint256 { updateExpirationRow(itExpirationRow->first, itExpirationRow->second); } + for (supportMapType::iterator itSupportCache = supportCache.begin(); itSupportCache != supportCache.end(); ++itSupportCache) + { + updateSupportMap(itSupportCache->first, itSupportCache->second); + } + for (supportValueQueueType::iterator itSupportQueue = supportQueueCache.begin(); itSupportQueue != supportQueueCache.end(); ++itSupportQueue) + { + updateSupportQueue(itSupportQueue->first, itSupportQueue->second); + } hashBlock = hashBlockIn; nCurrentHeight = nNewHeight; return true; @@ -540,9 +607,9 @@ void CClaimTrie::BatchWriteNode(CLevelDBBatch& batch, const std::string& name, c { LogPrintf("%s: Writing %s to disk with %d values\n", __func__, name, pNode->values.size()); if (pNode) - batch.Write(std::make_pair('n', name), *pNode); + batch.Write(std::make_pair(TRIE_NODE, name), *pNode); else - batch.Erase(std::make_pair('n', name)); + batch.Erase(std::make_pair(TRIE_NODE, name)); } void CClaimTrie::BatchWriteQueueRows(CLevelDBBatch& batch) @@ -551,11 +618,11 @@ void CClaimTrie::BatchWriteQueueRows(CLevelDBBatch& batch) { if (itQueue->second.empty()) { - batch.Erase(std::make_pair('r', itQueue->first)); + batch.Erase(std::make_pair(QUEUE_ROW, itQueue->first)); } else { - batch.Write(std::make_pair('r', itQueue->first), itQueue->second); + batch.Write(std::make_pair(QUEUE_ROW, itQueue->first), itQueue->second); } } } @@ -566,11 +633,41 @@ void CClaimTrie::BatchWriteExpirationQueueRows(CLevelDBBatch& batch) { if (itQueue->second.empty()) { - batch.Erase(std::make_pair('e', itQueue->first)); + batch.Erase(std::make_pair(EXP_QUEUE_ROW, itQueue->first)); } else { - batch.Write(std::make_pair('e', itQueue->first), itQueue->second); + batch.Write(std::make_pair(EXP_QUEUE_ROW, itQueue->first), itQueue->second); + } + } +} + +void CClaimTrie::BatchWriteSupportNodes(CLevelDBBatch& batch) +{ + for (supportMapType::iterator itSupport = dirtySupportNodes.begin(); itSupport != dirtySupportNodes.end(); ++itSupport) + { + if (itSupport->second.empty()) + { + batch.Erase(std::make_pair(SUPPORT, itSupport->first)); + } + else + { + batch.Write(std::make_pair(SUPPORT, itSupport->first), itSupport->second); + } + } +} + +void CClaimTrie::BatchWriteSupportQueueRows(CLevelDBBatch& batch) +{ + for (supportValueQueueType::iterator itQueue = dirtySupportQueueRows.begin(); itQueue != dirtySupportQueueRows.end(); ++itQueue) + { + if (itQueue->second.empty()) + { + batch.Erase(std::make_pair(SUPPORT_QUEUE, itQueue->first)); + } + else + { + batch.Write(std::make_pair(SUPPORT_QUEUE, itQueue->first), itQueue->second); } } } @@ -585,8 +682,12 @@ bool CClaimTrie::WriteToDisk() dirtyQueueRows.clear(); BatchWriteExpirationQueueRows(batch); dirtyExpirationQueueRows.clear(); - batch.Write('h', hashBlock); - batch.Write('t', nCurrentHeight); + BatchWriteSupportNodes(batch); + dirtySupportNodes.clear(); + BatchWriteSupportQueueRows(batch); + dirtySupportQueueRows.clear(); + batch.Write(HASH_BLOCK, hashBlock); + batch.Write(CURRENT_HEIGHT, nCurrentHeight); return db.WriteBatch(batch); } @@ -611,9 +712,9 @@ bool CClaimTrie::InsertFromDisk(const std::string& name, CClaimTrieNode* node) bool CClaimTrie::ReadFromDisk(bool check) { - if (!db.Read('h', hashBlock)) + if (!db.Read(HASH_BLOCK, hashBlock)) LogPrintf("%s: Couldn't read the best block's hash\n", __func__); - if (!db.Read('t', nCurrentHeight)) + if (!db.Read(CURRENT_HEIGHT, nCurrentHeight)) LogPrintf("%s: Couldn't read the current height\n", __func__); boost::scoped_ptr pcursor(const_cast(&db)->NewIterator()); pcursor->SeekToFirst(); @@ -626,7 +727,7 @@ bool CClaimTrie::ReadFromDisk(bool check) CDataStream ssKey(slKey.data(), slKey.data()+slKey.size(), SER_DISK, CLIENT_VERSION); char chType; ssKey >> chType; - if (chType == 'n') + if (chType == TRIE_NODE) { leveldb::Slice slValue = pcursor->value(); std::string name; @@ -810,7 +911,19 @@ bool CClaimTrieCache::insertClaimIntoTrie(const std::string name, CNodeValue val cache[name] = currentNode; } bool fChanged = false; - currentNode->insertValue(val, &fChanged); + if (currentNode->values.empty()) + { + fChanged = true; + currentNode->insertValue(val);//, &fChanged); + } + else + { + CNodeValue currentTop = currentNode->values.front(); + currentNode->insertValue(val); + currentNode->reorderValues(); + if (currentTop != currentNode->values.front()) + fChanged = true; + } if (fChanged) { for (std::string::const_iterator itCur = name.begin(); itCur != name.end(); ++itCur) @@ -864,7 +977,26 @@ bool CClaimTrieCache::removeClaimFromTrie(const std::string name, uint256 txhash bool fChanged = false; assert(currentNode != NULL); CNodeValue val; - bool success = currentNode->removeValue(txhash, nOut, val, &fChanged); + bool success = false; + + if (currentNode->values.empty()) + { + LogPrintf("%s: Asked to remove value from node without values\n", __func__); + return false; + } + CNodeValue currentTop = currentNode->values.front(); + + success = currentNode->removeValue(txhash, nOut, val);//, &fChanged); + + if (!currentNode->values.empty()) + { + currentNode->reorderValues(); + if (currentTop != currentNode->values.front()) + fChanged = true; + } + else + fChanged = true; + if (!success) { LogPrintf("%s: Removing a value was unsuccessful. name = %s, txhash = %s, nOut = %d", __func__, name.c_str(), txhash.GetHex(), nOut); @@ -1013,14 +1145,13 @@ bool CClaimTrieCache::undoSpendClaim(const std::string name, uint256 txhash, uin { CNodeValue val(txhash, nOut, nAmount, nHeight, nValidAtHeight); CValueQueueEntry entry(name, val); - insertClaimIntoTrie(name, CNodeValue(txhash, nOut, nAmount, nHeight, nValidAtHeight)); addToExpirationQueue(entry); + return insertClaimIntoTrie(name, val); } else { - addClaimToQueues(name, txhash, nOut, nAmount, nHeight, nValidAtHeight); + return addClaimToQueues(name, txhash, nOut, nAmount, nHeight, nValidAtHeight); } - return true; } bool CClaimTrieCache::addClaimToQueues(const std::string name, uint256 txhash, uint32_t nOut, CAmount nAmount, int nHeight, int nValidAtHeight) const @@ -1081,8 +1212,8 @@ bool CClaimTrieCache::removeClaim(const std::string name, uint256 txhash, uint32 else if (removeClaimFromQueue(name, txhash, nOut, nHeight, nValidAtHeight)) removed = true; } - if (removed == false && removeClaimFromQueue(name, txhash, nOut, nHeight, nCurrentHeight)) - removed = true; + //if (removed == false && removeClaimFromQueue(name, txhash, nOut, nHeight, nCurrentHeight)) + // removed = true; if (removed == false && removeClaimFromTrie(name, txhash, nOut, nValidAtHeight)) removed = true; if (removed == true) @@ -1138,7 +1269,228 @@ valueQueueType::iterator CClaimTrieCache::getExpirationQueueCacheRow(int nHeight return itQueueRow; } -bool CClaimTrieCache::incrementBlock(CClaimTrieQueueUndo& insertUndo, CClaimTrieQueueUndo& expireUndo) const +bool CClaimTrieCache::reorderTrieNode(const std::string name) const +{ + assert(base); + nodeCacheType::iterator cachedNode; + cachedNode = cache.find("name"); + if (cachedNode == cache.end()) + { + CClaimTrieNode* currentNode = &(base->root); + for (std::string::const_iterator itCur = name.begin(); itCur != name.end(); ++itCur) + { + std::string sCurrentSubstring(name.begin(), itCur); + std::string sNextSubstring(name.begin(), itCur + 1); + + cachedNode = cache.find(sNextSubstring); + if (cachedNode != cache.end()) + { + currentNode = cachedNode->second; + continue; + } + nodeMapType::iterator childNode = currentNode->children.find(*itCur); + if (childNode != currentNode->children.end()) + { + currentNode = childNode->second; + continue; + } + // The node doesn't exist, so it can't be reordered. + return true; + } + currentNode = new CClaimTrieNode(*currentNode); + std::pair ret; + ret = cache.insert(std::pair(name, currentNode)); + assert(ret.second); + cachedNode = ret.first; + } + bool fChanged = false; + if (cachedNode->second->values.empty()) + { + // Nothing in there to reorder + return true; + } + else + { + CNodeValue currentTop = cachedNode->second->values.front(); + cachedNode->second->reorderValues(); + if (currentTop != cachedNode->second->values.front()) + fChanged = true; + } + if (fChanged) + { + for (std::string::const_iterator itCur = name.begin(); itCur != name.end(); ++itCur) + { + std::string sub(name.begin(), itCur); + dirtyHashes.insert(sub); + } + dirtyHashes.insert(name); + } + return true; +} + +bool CClaimTrieCache::insertSupportIntoMap(const std::string name, CSupportNodeValue val) const +{ + supportMapType::iterator cachedNode; + // If this node is already in the cache, use that + cachedNode = supportCache.find(name); + // If not, copy the one from base if it exists, and use that + if (cachedNode == supportCache.end()) + { + supportMapNodeType node; + base->getSupportNode(name, node); + std::pair ret; + ret = supportCache.insert(std::pair(name, node)); + assert(ret.second); + cachedNode = ret.first; + } + cachedNode->second.push_back(val); + // See if this changed the biggest bid + return reorderTrieNode(name); +} + +bool CClaimTrieCache::removeSupportFromMap(const std::string name, uint256 txhash, uint32_t nOut, uint256 supportedTxhash, int supportednOut, int nHeight, int& nValidAtHeight) const +{ + supportMapType::iterator cachedNode; + cachedNode = supportCache.find(name); + if (cachedNode == supportCache.end()) + { + supportMapNodeType node; + if (!base->getSupportNode(name, node)) + { + // clearly, this support does not exist + return false; + } + std::pair ret; + ret = supportCache.insert(std::pair(name, node)); + assert(ret.second); + cachedNode = ret.first; + } + for (supportMapNodeType::iterator itSupport = cachedNode->second.begin(); itSupport != cachedNode->second.end(); ++itSupport) + { + if (itSupport->txhash == txhash && itSupport->nOut == nOut && itSupport->supportTxhash == supportedTxhash && itSupport->supportnOut == supportednOut && itSupport->nHeight == nHeight) + { + nValidAtHeight = itSupport->nValidAtHeight; + return true; + } + } + return false; +} + +supportValueQueueType::iterator CClaimTrieCache::getSupportQueueCacheRow(int nHeight, bool createIfNotExists) const +{ + supportValueQueueType::iterator itQueueRow = supportQueueCache.find(nHeight); + if (itQueueRow == supportQueueCache.end()) + { + std::vector queueRow; + bool exists = base->getSupportQueueRow(nHeight, queueRow); + if (!exists) + if (!createIfNotExists) + return itQueueRow; + // Stick the new row in the cache + std::pair ret; + ret = supportQueueCache.insert(std::pair >(nHeight, queueRow)); + assert(ret.second); + itQueueRow = ret.first; + } + return itQueueRow; +} + +bool CClaimTrieCache::addSupportToQueue(const std::string name, uint256 txhash, uint32_t nOut, CAmount nAmount, uint256 supportedTxhash, int supportednOut, int nHeight, int nValidAtHeight) const +{ + LogPrintf("%s: nValidAtHeight: %d\n", __func__, nValidAtHeight); + CSupportNodeValue val(txhash, nOut, supportedTxhash, supportednOut, nAmount, nHeight, nValidAtHeight); + CSupportValueQueueEntry entry(name, val); + supportValueQueueType::iterator itQueueRow = getSupportQueueCacheRow(nValidAtHeight, true); + itQueueRow->second.push_back(entry); + return true; +} + +bool CClaimTrieCache::removeSupportFromQueue(const std::string name, uint256 txhash, uint32_t nOut, uint256 supportedTxhash, int supportednOut, int nHeightToCheck, int& nValidAtHeight) const +{ + supportValueQueueType::iterator itQueueRow = getSupportQueueCacheRow(nHeightToCheck, false); + if (itQueueRow == supportQueueCache.end()) + { + return false; + } + std::vector::iterator itQueue; + for (itQueue = itQueueRow->second.begin(); itQueue != itQueueRow->second.end(); ++itQueue) + { + CSupportNodeValue& val = itQueue->val; + if (name == itQueue->name && val.txhash == txhash && val.nOut == nOut && val.supportTxhash == supportedTxhash && val.supportnOut == supportednOut) + { + nValidAtHeight = val.nValidAtHeight; + break; + } + } + if (itQueue != itQueueRow->second.end()) + { + itQueueRow->second.erase(itQueue); + return true; + } + return false; +} + +bool CClaimTrieCache::addSupport(const std::string name, uint256 txhash, uint32_t nOut, CAmount nAmount, uint256 supportedTxhash, int supportednOut, int nHeight) const +{ + std::cout << "just adding some support" << std::endl; + LogPrintf("%s: name: %s, txhash: %s, nOut: %d, nAmount: %d, supportedTxhash: %s, supportednOut: %d, nHeight: %d, nCurrentHeight: %d\n", __func__, name, txhash.GetHex(), nOut, nAmount, supportedTxhash.GetHex(), supportednOut, nHeight); + assert(nHeight == nCurrentHeight); + CNodeValue val; + if (base->getInfoForName(name, val)) + { + if (val.txhash == supportedTxhash && val.nOut == supportednOut) + { + LogPrintf("%s: This is a support to a best claim.\n", __func__); + return addSupportToQueue(name, txhash, nOut, nAmount, supportedTxhash, supportednOut, nHeight, nHeight); + } + } + return addSupportToQueue(name, txhash, nOut, nAmount, supportedTxhash, supportednOut, nHeight, nHeight + DEFAULT_DELAY); +} + +bool CClaimTrieCache::undoSpendSupport(const std::string name, uint256 txhash, uint32_t nOut, CAmount nAmount, uint256 supportedTxhash, int supportednOut, int nHeight, int nValidAtHeight) const +{ + LogPrintf("%s: name: %s, txhash: %s, nOut: %d, nAmount: %d, supportedTxhash: %s, supportednOut: %d, nHeight: %d, nCurrentHeight: %d\n", __func__, name, txhash.GetHex(), nOut, nAmount, supportedTxhash.GetHex(), supportednOut, nHeight); + if (nValidAtHeight < nCurrentHeight) + { + CSupportNodeValue val(txhash, nOut, supportedTxhash, supportednOut, nAmount, nHeight, nValidAtHeight); + CSupportValueQueueEntry entry(name, val); + return insertSupportIntoMap(name, val); + } + else + { + return addSupportToQueue(name, txhash, nOut, nAmount, supportedTxhash, supportednOut, nHeight, nValidAtHeight); + } +} + +bool CClaimTrieCache::removeSupport(const std::string name, uint256 txhash, uint32_t nOut, uint256 supportedTxhash, int supportednOut, int nHeight, int& nValidAtHeight) const +{ + bool removed = false; + if (nHeight + DEFAULT_DELAY >= nCurrentHeight) + { + if (removeSupportFromQueue(name, txhash, nOut, supportedTxhash, supportednOut, nHeight + DEFAULT_DELAY, nValidAtHeight)) + removed = true; + else if (removeSupportFromQueue(name, txhash, nOut, supportedTxhash, supportednOut, nHeight, nValidAtHeight)) + removed = true; + } + if (removed == false && removeSupportFromMap(name, txhash, nOut, supportedTxhash, supportednOut, nHeight, nValidAtHeight)) + removed = true; + return removed; +} + +bool CClaimTrieCache::undoAddSupport(const std::string name, uint256 txhash, uint32_t nOut, uint256 supportedTxhash, int supportednOut, int nHeight) const +{ + LogPrintf("%s: name: %s, txhash: %s, nOut: %d, supportedTxhash: %s, supportednOut: %d, nHeight: %d, nCurrentHeight: %d\n", __func__, name, txhash.GetHex(), nOut, supportedTxhash.GetHex(), supportednOut, nHeight); + int throwaway; + return removeSupport(name, txhash, nOut, supportedTxhash, supportednOut, nHeight, throwaway); +} + +bool CClaimTrieCache::spendSupport(const std::string name, uint256 txhash, uint32_t nOut, uint256 supportedTxhash, int supportednOut, int nHeight, int& nValidAtHeight) const +{ + LogPrintf("%s: name: %s, txhash: %s, nOut: %d, supportedTxhash: %s, supportednOut: %d, nHeight: %d, nCurrentHeight: %d\n", __func__, name, txhash.GetHex(), nOut, supportedTxhash.GetHex(), supportednOut, nHeight); + return removeSupport(name, txhash, nOut, supportedTxhash, supportednOut, nHeight, nValidAtHeight); +} + +bool CClaimTrieCache::incrementBlock(CClaimTrieQueueUndo& insertUndo, CClaimTrieQueueUndo& expireUndo, CSupportValueQueueUndo& insertSupportUndo) const { LogPrintf("%s: nCurrentHeight (before increment): %d\n", __func__, nCurrentHeight); valueQueueType::iterator itQueueRow = getQueueCacheRow(nCurrentHeight, false); @@ -1163,11 +1515,21 @@ bool CClaimTrieCache::incrementBlock(CClaimTrieQueueUndo& insertUndo, CClaimTrie } itExpirationRow->second.clear(); } + supportValueQueueType::iterator itSupportRow = getSupportQueueCacheRow(nCurrentHeight, false); + if (itSupportRow != supportQueueCache.end()) + { + for (std::vector::iterator itSupport = itSupportRow->second.begin(); itSupport != itSupportRow->second.end(); ++itSupport) + { + insertSupportIntoMap(itSupport->name, itSupport->val); + insertSupportUndo.push_back(*itSupport); + } + itSupportRow->second.clear(); + } nCurrentHeight++; return true; } -bool CClaimTrieCache::decrementBlock(CClaimTrieQueueUndo& insertUndo, CClaimTrieQueueUndo& expireUndo) const +bool CClaimTrieCache::decrementBlock(CClaimTrieQueueUndo& insertUndo, CClaimTrieQueueUndo& expireUndo, CSupportValueQueueUndo& insertSupportUndo) const { LogPrintf("%s: nCurrentHeight (before decrement): %d\n", __func__, nCurrentHeight); nCurrentHeight--; @@ -1191,6 +1553,17 @@ bool CClaimTrieCache::decrementBlock(CClaimTrieQueueUndo& insertUndo, CClaimTrie itExpireRow->second.push_back(*itExpireUndo); } } + if (insertSupportUndo.begin() != insertSupportUndo.end()) + { + supportValueQueueType::iterator itSupportRow = getSupportQueueCacheRow(nCurrentHeight, true); + for (CSupportValueQueueUndo::iterator itSupportUndo = insertSupportUndo.begin(); itSupportUndo != insertSupportUndo.end(); ++itSupportUndo) + { + int nValidHeightInMap; + assert(removeSupportFromMap(itSupportUndo->name, itSupportUndo->val.txhash, itSupportUndo->val.nOut, itSupportUndo->val.supportTxhash, itSupportUndo->val.supportnOut, itSupportUndo->val.nHeight, nValidHeightInMap)); + assert(nValidHeightInMap == itSupportUndo->val.nValidAtHeight); + itSupportRow->second.push_back(*itSupportUndo); + } + } return true; } @@ -1217,6 +1590,9 @@ bool CClaimTrieCache::clear() const dirtyHashes.clear(); cacheHashes.clear(); valueQueueCache.clear(); + expirationQueueCache.clear(); + supportCache.clear(); + supportQueueCache.clear(); return true; } @@ -1224,7 +1600,7 @@ bool CClaimTrieCache::flush() { if (dirty()) getMerkleHash(); - bool success = base->update(cache, cacheHashes, getBestBlock(), valueQueueCache, expirationQueueCache, nCurrentHeight); + bool success = base->update(cache, cacheHashes, getBestBlock(), valueQueueCache, expirationQueueCache, nCurrentHeight, supportCache, supportQueueCache); if (success) { success = clear(); diff --git a/src/claimtrie.h b/src/claimtrie.h index bbcc14330..438f5bc85 100644 --- a/src/claimtrie.h +++ b/src/claimtrie.h @@ -16,6 +16,15 @@ #define DEFAULT_DELAY 100 +// leveldb keys +#define HASH_BLOCK 'h' +#define CURRENT_HEIGHT 't' +#define TRIE_NODE 'n' +#define QUEUE_ROW 'r' +#define EXP_QUEUE_ROW 'e' +#define SUPPORT 's' +#define SUPPORT_QUEUE 'u' + class CNodeValue { public: @@ -67,6 +76,41 @@ public: } }; +class CSupportNodeValue +{ +public: + uint256 txhash; + uint32_t nOut; + uint256 supportTxhash; + int supportnOut; + CAmount nAmount; + int nHeight; + int nValidAtHeight; + CSupportNodeValue() {}; + CSupportNodeValue(uint256 txhash, uint32_t nOut, uint256 supportTxhash, uint32_t supportnOut, CAmount nAmount, int nHeight, int nValidAtHeight) : txhash(txhash), nOut(nOut), supportTxhash(supportTxhash), supportnOut(supportnOut), nAmount(nAmount), nHeight(nHeight), nValidAtHeight(nValidAtHeight) {} + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { + READWRITE(txhash); + READWRITE(nOut); + READWRITE(supportTxhash); + READWRITE(supportnOut); + READWRITE(nAmount); + READWRITE(nHeight); + READWRITE(nValidAtHeight); + } + + bool operator==(const CSupportNodeValue& other) const + { + return txhash == other.txhash && nOut == other.nOut && supportTxhash == other.supportTxhash && supportnOut == other.supportnOut && nAmount == other.nAmount && nHeight == other.nHeight && nValidAtHeight == other.nValidAtHeight; + } + bool operator!=(const CSupportNodeValue& other) const + { + return !(*this == other); + } +}; + class CClaimTrieNode; class CClaimTrie; @@ -80,14 +124,14 @@ public: CClaimTrieNode() {} CClaimTrieNode(uint256 hash) : hash(hash) {} uint256 hash; - uint256 bestBlock; nodeMapType children; std::vector values; - bool insertValue(CNodeValue val, bool * fChanged = NULL); - bool removeValue(uint256& txhash, uint32_t nOut, CNodeValue& val, bool * fChanged = NULL); + bool insertValue(CNodeValue val);//, bool * fChanged = NULL); + 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();} bool haveValue(const uint256& txhash, uint32_t nOut) const; + void reorderValues(); ADD_SERIALIZE_METHODS; template @@ -105,8 +149,6 @@ public: { return !(*this == other); } -private: - bool getValue(uint256& txhash, uint32_t nOut, CNodeValue& val) const; }; struct nodenamecompare @@ -121,7 +163,7 @@ struct nodenamecompare class CValueQueueEntry { - public: +public: CValueQueueEntry() {} CValueQueueEntry(std::string name, CNodeValue val) : name(name), val(val) {} std::string name; @@ -136,9 +178,32 @@ class CValueQueueEntry } }; +class CSupportValueQueueEntry +{ +public: + CSupportValueQueueEntry() {} + CSupportValueQueueEntry(std::string name, CSupportNodeValue val) : name(name), val(val) {} + std::string name; + CSupportNodeValue val; + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream &s, Operation ser_action, int nType, int nVersion) { + READWRITE(name); + READWRITE(val); + } +}; + +typedef std::vector supportMapNodeType; +typedef std::map supportMapType; + typedef std::map > valueQueueType; typedef std::vector CClaimTrieQueueUndo; +typedef std::map > supportValueQueueType; +typedef std::vector CSupportValueQueueUndo; + typedef std::map nodeCacheType; typedef std::map hashMapType; @@ -160,10 +225,14 @@ public: bool getInfoForName(const std::string& name, CNodeValue& val) const; int nCurrentHeight; bool queueEmpty() const; + bool supportEmpty() const; + bool supportQueueEmpty() const; bool expirationQueueEmpty() const; void setExpirationTime(int t); bool getQueueRow(int nHeight, std::vector& row); bool getExpirationQueueRow(int nHeight, std::vector& row); + bool getSupportNode(std::string name, supportMapNodeType& node); + bool getSupportQueueRow(int nHeight, std::vector& row); bool haveClaim(const std::string& name, const uint256& txhash, uint32_t nOut) const; unsigned int getTotalNamesInTrie() const; unsigned int getTotalClaimsInTrie() const; @@ -172,7 +241,7 @@ public: int nExpirationTime; private: void clear(CClaimTrieNode* current); - bool update(nodeCacheType& cache, hashMapType& hashes, const uint256& hashBlock, valueQueueType& queueCache, valueQueueType& expirationQueueCache, int nNewHeight); + bool update(nodeCacheType& cache, hashMapType& hashes, const uint256& hashBlock, valueQueueType& queueCache, valueQueueType& expirationQueueCache, int nNewHeight, supportMapType& supportCache, supportValueQueueType& supportQueueCache); bool updateName(const std::string& name, CClaimTrieNode* updatedNode); bool updateHash(const std::string& name, uint256& hash); bool recursiveNullify(CClaimTrieNode* node, std::string& name); @@ -183,18 +252,26 @@ private: CAmount getTotalValueOfClaimsRecursive(const CClaimTrieNode* current, bool fControllingOnly) const; bool recursiveFlattenTrie(const std::string& name, const CClaimTrieNode* current, std::vector& nodes) const; CClaimTrieNode root; + //supportMapType support; uint256 hashBlock; valueQueueType dirtyQueueRows; valueQueueType dirtyExpirationQueueRows; + supportValueQueueType dirtySupportQueueRows; nodeCacheType dirtyNodes; + supportMapType dirtySupportNodes; void markNodeDirty(const std::string& name, CClaimTrieNode* node); void updateQueueRow(int nHeight, std::vector& row); void updateExpirationRow(int nHeight, std::vector& row); + void updateSupportMap(const std::string& name, supportMapNodeType& node); + void updateSupportQueue(int nHeight, std::vector& row); void BatchWriteNode(CLevelDBBatch& batch, const std::string& name, const CClaimTrieNode* pNode) const; void BatchEraseNode(CLevelDBBatch& batch, const std::string& nome) const; void BatchWriteQueueRows(CLevelDBBatch& batch); void BatchWriteExpirationQueueRows(CLevelDBBatch& batch); + void BatchWriteSupportNodes(CLevelDBBatch& batch); + void BatchWriteSupportQueueRows(CLevelDBBatch& batch); + bool keyTypeEmpty(char key) const; }; class CClaimTrieCache @@ -210,10 +287,14 @@ public: 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; + bool addSupport(const std::string name, uint256 txhash, uint32_t nOut, CAmount nAmount, uint256 supportedTxhash, int supportednOut, int nHeight) const; + bool undoAddSupport(const std::string name, uint256 txhash, uint32_t nOut, uint256 supportedTxhash, int supportednOut, int nHeight) const; + bool spendSupport(const std::string name, uint256 txhash, uint32_t nOut, uint256 supportedTxhash, int supportednOut, int nHeight, int& nValidAtHeight) const; + bool undoSpendSupport(const std::string name, uint256 txhash, uint32_t nOut, CAmount nAmount, uint256 supportedTxhash, int supportednOut, int nHeight, int nValidAtHeight) const; uint256 getBestBlock(); void setBestBlock(const uint256& hashBlock); - bool incrementBlock(CClaimTrieQueueUndo& insertUndo, CClaimTrieQueueUndo& expireUndo) const; - bool decrementBlock(CClaimTrieQueueUndo& insertUndo, CClaimTrieQueueUndo& expireUndo) const; + bool incrementBlock(CClaimTrieQueueUndo& insertUndo, CClaimTrieQueueUndo& expireUndo, CSupportValueQueueUndo& insertSupportUndo) const; + bool decrementBlock(CClaimTrieQueueUndo& insertUndo, CClaimTrieQueueUndo& expireUndo, CSupportValueQueueUndo& insertSupportUndo) const; ~CClaimTrieCache() { clear(); } bool insertClaimIntoTrie(const std::string name, CNodeValue val) const; bool removeClaimFromTrie(const std::string name, uint256 txhash, uint32_t nOut, int& nValidAtHeight) const; @@ -224,9 +305,12 @@ private: mutable hashMapType cacheHashes; mutable valueQueueType valueQueueCache; mutable valueQueueType expirationQueueCache; + mutable supportMapType supportCache; + mutable supportValueQueueType supportQueueCache; mutable int nCurrentHeight; // Height of the block that is being worked on, which is // one greater than the height of the chain's tip uint256 computeHash() const; + bool reorderTrieNode(const std::string name) const; bool recursiveComputeMerkleHash(CClaimTrieNode* tnCurrent, std::string sPos) const; bool recursivePruneName(CClaimTrieNode* tnCurrent, unsigned int nPos, std::string sName, bool* pfNullified = NULL) const; bool clear() const; @@ -237,6 +321,12 @@ private: void removeFromExpirationQueue(const std::string name, uint256 txhash, uint32_t nOut, int nHeight) const; valueQueueType::iterator getQueueCacheRow(int nHeight, bool createIfNotExists) const; valueQueueType::iterator getExpirationQueueCacheRow(int nHeight, bool createIfNotExists) const; + bool removeSupport(const std::string name, uint256 txhash, uint32_t nOut, uint256 supportedTxhash, int supportednOut, int nHeight, int& nValidAtHeight) const; + bool removeSupportFromMap(const std::string name, uint256 txhash, uint32_t nOut, uint256 supportedTxhash, int supportednOut, int nHeight, int& nValidAtHeight) const; + bool insertSupportIntoMap(const std::string name, CSupportNodeValue val) const; + supportValueQueueType::iterator getSupportQueueCacheRow(int nHeight, bool createIfNotExists) const; + bool addSupportToQueue(const std::string name, uint256 txhash, uint32_t nOut, CAmount nAmount, uint256 supportedTxhash, int supportednOut, int nHeight, int nValidAtHeight) const; + bool removeSupportFromQueue(const std::string name, uint256 txhash, uint32_t nOut, uint256 supportedTxhash, int supportednOut, int nHeightToCheck, int& nValidAtHeight) const; uint256 hashBlock; }; diff --git a/src/main.cpp b/src/main.cpp index 13ec761b4..1a156c16a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1623,14 +1623,40 @@ static bool ApplyTxInUndo(const CTxInUndo& undo, CCoinsViewCache& view, CClaimTr std::vector > vvchParams; if (DecodeClaimScript(undo.txout.scriptPubKey, op, vvchParams)) { - assert(vvchParams.size() == 2); - std::string name(vvchParams[0].begin(), vvchParams[0].end()); - LogPrintf("%s: Restoring %s to the claim trie due to a block being disconnected\n", __func__, name.c_str()); - int nValidHeight = undo.nClaimValidHeight; - if (nValidHeight > 0 && nValidHeight >= coins->nHeight) + if (op == OP_CLAIM_NAME) { - if (!trieCache.undoSpendClaim(name, out.hash, out.n, undo.txout.nValue, coins->nHeight, nValidHeight)) - LogPrintf("%s: Something went wrong inserting the name\n", __func__); + assert(vvchParams.size() == 2); + std::string name(vvchParams[0].begin(), vvchParams[0].end()); + int nValidHeight = undo.nClaimValidHeight; + if (nValidHeight > 0 && nValidHeight >= coins->nHeight) + { + LogPrintf("%s: (txid: %s, nOut: %d) Restoring %s to the claim trie due to a block being disconnected\n", __func__, out.hash.ToString(), out.n, name.c_str()); + if (!trieCache.undoSpendClaim(name, out.hash, out.n, undo.txout.nValue, coins->nHeight, nValidHeight)) + LogPrintf("%s: Something went wrong inserting the claim\n", __func__); + } + else + { + LogPrintf("%s: (txid: %s, nOut: %d) Not restoring %s to the claim trie because it expired before it was spent\n", __func__, out.hash.ToString(), out.n, name.c_str()); + } + } + else if (op == OP_SUPPORT_CLAIM) + { + assert(vvchParams.size() == 3); + std::string name(vvchParams[0].begin(), vvchParams[0].end()); + uint256 supportedTxid(vvchParams[1]); + CScriptNum snOut(vvchParams[2], true); + int supportednOut = snOut.getint(); + int nValidHeight = undo.nClaimValidHeight; + if (nValidHeight > 0 && nValidHeight >= coins->nHeight) + { + LogPrintf("%s: (txid: %s, nOut: %d) Restoring support for %s in txid %s nOut %d due to a block being disconnected\n", __func__, out.hash.ToString(), out.n, name, supportedTxid.ToString(), supportednOut); + if (!trieCache.undoSpendSupport(name, out.hash, out.n, undo.txout.nValue, supportedTxid, supportednOut, coins->nHeight, nValidHeight)) + LogPrintf("%s: Something went wrong inserting support for the claim\n", __func__); + } + else + { + LogPrintf("%s: (txid: %s, nOut: %d) Not restoring support for %s in txid %s nOut %d because the support expired before it was spent\n", __func__, out.hash.ToString(), out.n, name, supportedTxid.ToString(), supportednOut); + } } } @@ -1659,7 +1685,7 @@ 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.insertUndo, blockUndo.expireUndo)); + assert(trieCache.decrementBlock(blockUndo.insertUndo, blockUndo.expireUndo, blockUndo.insertSupportUndo)); // undo transactions in reverse order for (int i = block.vtx.size() - 1; i >= 0; i--) { @@ -1690,11 +1716,25 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex std::vector > vvchParams; if (DecodeClaimScript(txout.scriptPubKey, op, vvchParams)) { - assert(vvchParams.size() == 2); - std::string name(vvchParams[0].begin(), vvchParams[0].end()); - LogPrintf("%s: Removing %s from the claim trie due to its block being disconnected\n", __func__, name.c_str()); - 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()); + if (op == OP_CLAIM_NAME) + { + assert(vvchParams.size() == 2); + std::string name(vvchParams[0].begin(), vvchParams[0].end()); + LogPrintf("%s: (txid: %s, nOut: %d) Removing %s from the claim trie due to its block being disconnected\n", __func__, hash.ToString(), i, name.c_str()); + 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()); + } + else if (op == OP_SUPPORT_CLAIM) + { + assert(vvchParams.size() == 3); + std::string name(vvchParams[0].begin(), vvchParams[0].end()); + uint256 supportedTxid(vvchParams[1]); + CScriptNum snOut(vvchParams[2], true); + int supportednOut = snOut.getint(); + LogPrintf("%s: (txid: %s, nOut: %d) Removing support for %s from txid %s nOut %d due to its block being disconnected\n", __func__, hash.ToString(), i, supportedTxid.ToString(), supportednOut, name.c_str()); + if (!trieCache.undoAddSupport(name, hash, i, supportedTxid, supportednOut, pindex->nHeight)) + LogPrintf("%s: Something went wrong removing support for name %s in hash %s\n", __func__, name.c_str(), hash.ToString()); + } } } @@ -1969,16 +2009,33 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin std::vector > vvchParams; if (DecodeClaimScript(coins->vout[txin.prevout.n].scriptPubKey, op, vvchParams)) { - assert(vvchParams.size() == 2); - std::string name(vvchParams[0].begin(), vvchParams[0].end()); - int nValidAtHeight; - LogPrintf("%s: Removing %s from the claim trie. Tx: %s, nOut: %d\n", __func__, name, txin.prevout.hash.GetHex(), txin.prevout.n); - if (trieCache.spendClaim(name, txin.prevout.hash, txin.prevout.n, coins->nHeight, nValidAtHeight)) + if (op == OP_CLAIM_NAME) { - mClaimUndoHeights[i] = nValidAtHeight; - std::pair val(txin.prevout.hash, txin.prevout.n); - std::pair > entry(name, val); - spentClaims.insert(entry); + assert(vvchParams.size() == 2); + std::string name(vvchParams[0].begin(), vvchParams[0].end()); + int nValidAtHeight; + LogPrintf("%s: Removing %s from the claim trie. Tx: %s, nOut: %d\n", __func__, name, txin.prevout.hash.GetHex(), txin.prevout.n); + if (trieCache.spendClaim(name, txin.prevout.hash, txin.prevout.n, coins->nHeight, nValidAtHeight)) + { + mClaimUndoHeights[i] = nValidAtHeight; + std::pair val(txin.prevout.hash, txin.prevout.n); + std::pair > entry(name, val); + spentClaims.insert(entry); + } + } + else if (op == OP_SUPPORT_CLAIM) + { + assert(vvchParams.size() == 3); + std::string name(vvchParams[0].begin(), vvchParams[0].end()); + uint256 supportedTxid(vvchParams[1]); + CScriptNum snOut(vvchParams[2], true); + int supportednOut = snOut.getint(); + int nValidAtHeight; + LogPrintf("%s: Removing support for %s in txid %s nOut %d. Tx: %s, nOut: %d\n", __func__, name, supportedTxid.ToString(), supportednOut, txin.prevout.hash.ToString(), txin.prevout.n); + if (trieCache.spendSupport(name, txin.prevout.hash, txin.prevout.n, supportedTxid, supportednOut, coins->nHeight, nValidAtHeight)) + { + mClaimUndoHeights[0] = nValidAtHeight; + } } } } @@ -1991,21 +2048,34 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin std::vector > vvchParams; if (DecodeClaimScript(txout.scriptPubKey, op, vvchParams)) { - assert(vvchParams.size() == 2); - std::string name(vvchParams[0].begin(), vvchParams[0].end()); - LogPrintf("%s: Inserting %s into the claim trie. Tx: %s, nOut: %d\n", __func__, name, tx.GetHash().GetHex(), i); - spentClaimsType::iterator itSpent = spentClaims.find(name); - bool success; - if (itSpent != spentClaims.end()) + if (op == OP_CLAIM_NAME) { - LogPrintf("%s: Updating a previous transaction. Old tx: %s, old nOut: %d\n", __func__, itSpent->second.first.GetHex(), itSpent->second.second); - success = trieCache.addClaim(name, tx.GetHash(), i, txout.nValue, pindex->nHeight, itSpent->second.first, itSpent->second.second); - spentClaims.erase(itSpent); + assert(vvchParams.size() == 2); + std::string name(vvchParams[0].begin(), vvchParams[0].end()); + LogPrintf("%s: Inserting %s into the claim trie. Tx: %s, nOut: %d\n", __func__, name, tx.GetHash().GetHex(), i); + spentClaimsType::iterator itSpent = spentClaims.find(name); + bool success; + if (itSpent != spentClaims.end()) + { + LogPrintf("%s: Updating a previous transaction. Old tx: %s, old nOut: %d\n", __func__, itSpent->second.first.GetHex(), itSpent->second.second); + success = trieCache.addClaim(name, tx.GetHash(), i, txout.nValue, pindex->nHeight, itSpent->second.first, itSpent->second.second); + spentClaims.erase(itSpent); + } + else + success = trieCache.addClaim(name, tx.GetHash(), i, txout.nValue, pindex->nHeight); + if (!success) + LogPrintf("%s: Something went wrong inserting the name\n", __func__); + } + else if (op == OP_SUPPORT_CLAIM) + { + assert(vvchParams.size() == 3); + std::string name(vvchParams[0].begin(), vvchParams[0].end()); + uint256 supportedTxid(vvchParams[1]); + CScriptNum snOut(vvchParams[2], true); + int supportednOut = snOut.getint(); + if (!trieCache.addSupport(name, tx.GetHash(), i, txout.nValue, supportedTxid, supportednOut, pindex->nHeight)) + LogPrintf("%s: Something went wrong inserting the support\n", __func__); } - else - success = trieCache.addClaim(name, tx.GetHash(), i, txout.nValue, pindex->nHeight); - if (!success) - LogPrintf("%s: Something went wrong inserting the name\n", __func__); } } } @@ -2034,7 +2104,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin pos.nTxOffset += ::GetSerializeSize(tx, SER_DISK, CLIENT_VERSION); } - assert(trieCache.incrementBlock(blockundo.insertUndo, blockundo.expireUndo)); + assert(trieCache.incrementBlock(blockundo.insertUndo, blockundo.expireUndo, blockundo.insertSupportUndo)); if (trieCache.getMerkleHash() != block.hashClaimTrie) return state.DoS(100, diff --git a/src/miner.cpp b/src/miner.cpp index 174bf8192..d50725d5b 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -310,14 +310,28 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn) if (DecodeClaimScript(coins->vout[txin.prevout.n].scriptPubKey, op, vvchParams)) { - assert(vvchParams.size() == 2); - std::string name(vvchParams[0].begin(), vvchParams[0].end()); - 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__); - std::pair val(txin.prevout.hash, txin.prevout.n); - std::pair >entry(name, val); - spentClaims.insert(entry); + if (op == OP_CLAIM_NAME) + { + assert(vvchParams.size() == 2); + std::string name(vvchParams[0].begin(), vvchParams[0].end()); + 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__); + std::pair val(txin.prevout.hash, txin.prevout.n); + std::pair >entry(name, val); + spentClaims.insert(entry); + } + else if (op == OP_SUPPORT_CLAIM) + { + assert(vvchParams.size() == 3); + std::string name(vvchParams[0].begin(), vvchParams[0].end()); + uint256 supportedTxid(vvchParams[1]); + CScriptNum snOut(vvchParams[2], true); + int supportednOut = snOut.getint(); + int throwaway; + if (!trieCache.spendSupport(name, txin.prevout.hash, txin.prevout.n, supportedTxid, supportednOut, coins->nHeight, throwaway)) + LogPrintf("%s: Something went wrong removing the support\n", __func__); + } } } @@ -331,19 +345,34 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn) int op; if (DecodeClaimScript(txout.scriptPubKey, op, vvchParams)) { - assert(vvchParams.size() == 2); - std::string name(vvchParams[0].begin(), vvchParams[0].end()); - spentClaimsType::iterator itSpent = spentClaims.find(name); - bool success; - if (itSpent != spentClaims.end()) + if (op == OP_CLAIM_NAME) { - success = trieCache.addClaim(name, tx.GetHash(), i, txout.nValue, nHeight, itSpent->second.first, itSpent->second.second); - spentClaims.erase(itSpent); + assert(vvchParams.size() == 2); + std::string name(vvchParams[0].begin(), vvchParams[0].end()); + spentClaimsType::iterator itSpent = spentClaims.find(name); + bool success; + if (itSpent != spentClaims.end()) + { + success = trieCache.addClaim(name, tx.GetHash(), i, txout.nValue, nHeight, itSpent->second.first, itSpent->second.second); + spentClaims.erase(itSpent); + } + else + success = trieCache.addClaim(name, tx.GetHash(), i, txout.nValue, nHeight); + if (!success) + LogPrintf("%s: Something went wrong inserting the name\n", __func__); + } + else if (op == OP_SUPPORT_CLAIM) + { + assert(vvchParams.size() == 3); + std::string name(vvchParams[0].begin(), vvchParams[0].end()); + uint256 supportedTxid(vvchParams[1]); + CScriptNum snOut(vvchParams[2], true); + int supportednOut = snOut.getint(); + if (!trieCache.addSupport(name, tx.GetHash(), i, txout.nValue, supportedTxid, supportednOut, nHeight)) + { + LogPrintf("%s: Something went wrong inserting the name\n", __func__); + } } - else - success = trieCache.addClaim(name, tx.GetHash(), i, txout.nValue, nHeight); - if (!success) - LogPrintf("%s: Something went wrong inserting the name\n", __func__); } } @@ -398,7 +427,8 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn) pblocktemplate->vTxSigOps[0] = GetLegacySigOpCount(pblock->vtx[0]); CClaimTrieQueueUndo dummyInsertUndo; CClaimTrieQueueUndo dummyExpireUndo; - trieCache.incrementBlock(dummyInsertUndo, dummyExpireUndo); + CSupportValueQueueUndo dummyInsertSupportUndo; + trieCache.incrementBlock(dummyInsertUndo, dummyExpireUndo, dummyInsertSupportUndo); pblock->hashClaimTrie = trieCache.getMerkleHash(); CValidationState state; diff --git a/src/nameclaim.cpp b/src/nameclaim.cpp index d65b483a2..2c597b8a8 100644 --- a/src/nameclaim.cpp +++ b/src/nameclaim.cpp @@ -14,40 +14,54 @@ bool DecodeClaimScript(const CScript& scriptIn, int& op, std::vector vchName; - std::vector vchValue; - - // The correct format is: - // OP_CLAIM_NAME vchName vchValue OP_DROP2 OP_DROP pubkeyscript + std::vector vchParam1; + std::vector vchParam2; + std::vector vchParam3; + // Valid formats: + // OP_CLAIM_NAME vchName vchValue OP_2DROP OP_DROP pubkeyscript + // OP_SUPPORT_CLAIM vchName vchClaimHash vchClaimIndex OP_2DROP OP_2DROP pubkeyscript // All others are invalid. - if (!scriptIn.GetOp(pc, opcode, vchName) || opcode < 0 || opcode > OP_PUSHDATA4) + + if (!scriptIn.GetOp(pc, opcode, vchParam1) || opcode < 0 || opcode > OP_PUSHDATA4) { return false; } - if (!scriptIn.GetOp(pc, opcode, vchValue) || opcode < 0 || opcode > OP_PUSHDATA4) + if (!scriptIn.GetOp(pc, opcode, vchParam2) || opcode < 0 || opcode > OP_PUSHDATA4) { return false; } + if (op == OP_SUPPORT_CLAIM) + { + if (!scriptIn.GetOp(pc, opcode) || opcode < 0 || opcode > OP_PUSHDATA4) + { + return false; + } + } if (!scriptIn.GetOp(pc, opcode) || opcode != OP_2DROP) { return false; } - if (!scriptIn.GetOp(pc, opcode) || opcode != OP_DROP) + if (!scriptIn.GetOp(pc, opcode) || (op == OP_CLAIM_NAME && opcode != OP_DROP) || (op == OP_SUPPORT_CLAIM && opcode != OP_2DROP)) { return false; } - vvchParams.push_back(vchName); - vvchParams.push_back(vchValue); - + vvchParams.push_back(vchParam1); + vvchParams.push_back(vchParam2); + if (op == OP_SUPPORT_CLAIM) + { + vvchParams.push_back(vchParam3); + } + return true; } diff --git a/src/test/claimtrie_tests.cpp b/src/test/claimtrie_tests.cpp index 446347d1b..8cbecb7d8 100644 --- a/src/test/claimtrie_tests.cpp +++ b/src/test/claimtrie_tests.cpp @@ -143,7 +143,57 @@ const unsigned int expire_nonces[] = { 4359, 35786, 24830, 102413, 17399, 48845, 22866, 8417, 24049, 50162, 36921, 162891, 38509, 121018, 54548, 158171, 54355, 12742, 6174, }; -const unsigned int support_nonces[] = {}; +const unsigned int support_nonces[] = { + 6685, 83337, 74116, 50981, 51352, 158110, 132142, 161310, 95702, 32959, + 5785, 229298, 59738, 71206, 24625, 6, 141161, 43901, 35697, 98865, + 41764, 104919, 89611, 813, 54564, 13614, 24774, 39079, 67709, 94367, + 24006, 137451, 87265, 4096, 17540, 93657, 64419, 178853, 45385, 18571, + 49357, 67111, 92635, 73880, 7810, 15338, 56201, 1419, 92096, 121328, + 60987, 32796, 63605, 2178, 25415, 19115, 62190, 76200, 155888, 208604, + 43921, 623, 63809, 129207, 59080, 111270, 58799, 47014, 26935, 140745, + 15982, 22417, 54877, 64708, 3508, 63688, 117163, 93037, 27595, 49051, + 109801, 54794, 38790, 192113, 14920, 2056, 419624, 20495, 86719, 27455, + 94870, 5539, 12871, 24142, 103201, 12358, 60226, 11163, 57429, 49086, + 115051, 39940, 68936, 392, 23999, 23185, 37265, 793, 196124, 133047, + 95771, 4927, 2410, 191047, 8416, 36182, 126426, 101492, 268543, 66462, + 132688, 9709, 25766, 10781, 56169, 17484, 191643, 46857, 180718, 129600, + 76319, 275342, 113429, 52200, 8584, 205931, 60264, 99367, 71513, 10931, + 2470, 146435, 35660, 30357, 53621, 126053, 198310, 7340, 23329, 56232, + 43152, 1290, 178803, 58294, 28730, 135307, 26024, 33903, 23202, 69984, + 91861, 4406, 21564, 12088, 5307, 17517, 177507, 3629, 81666, 196118, + 37329, 42816, 1766, 5227, 98516, 62284, 1449, 10331, 4915, 1086, + 257130, 125081, 63069, 34059, 51482, 15396, 25319, 208595, 45717, 145038, + 51317, 34896, 60597, 8930, 150489, 15827, 94634, 19809, 90784, 102628, + 26279, 36205, 239377, 98432, 1949, 167692, 123222, 36572, 5435, 239413, + 85529, 124924, 17443, 10391, 21356, 109441, 711, 27883, 15410, 172902, + 9155, 6372, 93226, 31199, 47383, 77311, 107243, 1248, 3968, 88072, + 50741, 175826, 9475, 19473, 78911, 59587, 172626, 54698, 127135, 4356, + 70568, 9206, 41708, 162673, 82436, 8787, 12851, 17524, 27151, 34992, + 19003, 17118, 1353, 173957, 62721, 10956, 28301, 38722, 35000, 51572, + 122622, 26131, 219537, 24299, 8306, 22556, 117394, 5874, 1658, 4299, + 85895, 59207, 17620, 65379, 53730, 66114, 31973, 80054, 39898, 88576, + 35918, 54740, 43218, 310351, 18849, 65424, 18941, 49216, 21837, 1044, + 36089, 89042, 1064, 57622, 18277, 30812, 392721, 68449, 21958, 59353, + 230626, 192876, 152661, 83303, 12403, 48000, 322, 36098, 216060, 261073, + 10127, 40078, 13820, 37595, 2465, 67578, 8074, 17069, 23001, 75590, + 59540, 38500, 81671, 83017, 21630, 42072, 87988, 34685, 54463, 73723, + 64583, 11708, 27819, 60914, 44671, 73532, 481251, 50437, 51482, 140164, + 17802, 52420, 18605, 39313, 5815, 130397, 47241, 41700, 73784, 38254, + 31816, 81033, 63873, 61180, 73597, 31012, 46596, 34360, 16076, 3553, + 19667, 70678, 76463, 14007, 6695, 34346, 177277, 82740, 10695, 195656, + 199473, 19061, 12235, 118857, 5890, 158834, 14991, 9908, 40669, 76476, + 5798, 56010, 12434, 136848, 44171, 33686, 38022, 85052, 88529, 96055, + 77808, 14052, 26881, 183273, 110552, 14780, 62505, 29327, 16832, 146503, + 4492, 3210, 60633, 117771, 14125, 30949, 20800, 35101, 72610, 3023, + 39933, 7829, 21639, 14090, 59951, 46100, 26005, 57832, 3410, 58340, + 83407, 189530, 1991, 46036, 39758, 26344, 36726, 13556, 54091, 52219, + 10445, 23350, 62863, 41887, 39607, 47051, 170358, 62714, 54450, 44956, + 90394, 89040, 82532, 10732, 30853, 69521, 27096, 129159, 25700, 56643, + 4510, 61375, 45066, 84264, 47513, 27524, 25215, 95656, 73959, 20581, + 101988, 14797, 76360, 120161, 17567, 3903, 126413, 64154, 317038, 33995, + 25108, 8165, 132499, 174571, 4312, 63941, 109366, 12461, 81720, 36019, + 57768, 30058, 64038, 60147, 5536, 87586, 10954, 84112, 42404, 109189, +}; BOOST_FIXTURE_TEST_SUITE(claimtrie_tests, TestingSetup) @@ -1248,34 +1298,181 @@ BOOST_AUTO_TEST_CASE(claimtrie_supporting_claims) tx1.vout[0].scriptPubKey = CScript() << OP_CLAIM_NAME << vchName << vchValue1 << OP_2DROP << OP_DROP << OP_TRUE; CMutableTransaction tx2 = BuildTransaction(coinbases[1]); - tx1.vout[0].scriptPubKey = CScript() << OP_CLAIM_NAME << vchName << vchValue2 << OP_2DROP << OP_DROP << OP_TRUE; + tx2.vout[0].scriptPubKey = CScript() << OP_CLAIM_NAME << vchName << vchValue2 << OP_2DROP << OP_DROP << OP_TRUE; CMutableTransaction tx3 = BuildTransaction(coinbases[2]); std::string sTx1Hash = tx1.GetHash().ToString(); std::vector vchTx1Hash(sTx1Hash.begin(), sTx1Hash.end()); - tx1.vout[0].scriptPubKey = CScript() << OP_SUPPORT_CLAIM << vchTx1Hash << 0 << OP_2DROP << OP_DROP << OP_TRUE; + tx3.vout[0].scriptPubKey = CScript() << OP_SUPPORT_CLAIM << vchName << vchTx1Hash << CScriptNum(0) << OP_2DROP << OP_2DROP << OP_TRUE; + CNodeValue val; std::vector blocks_to_invalidate; - // Test 1: create 1 LBC claim (A), create 5 LBC support (A), create 5 LBC claim (B) - // Verify that A retains control throughout + // Test 1: create 1 LBC claim (tx1), create 5 LBC support (tx3), create 5 LBC claim (tx2) + // Verify that tx1 retains control throughout + // spend tx3, verify that tx2 gains control + // roll back to before tx3 is valid + // advance until tx3 and tx2 are valid, verify tx1 retains control + // spend tx3, verify tx2 gains control + // roll back to before tx3 is spent, verify tx1 gains control + // roll back to before tx2 is valid, spend tx3 + // advance to tx2 valid, verify tx2 gains control + // roll back to before tx3 is valid, spend tx3 + // advance to tx2 valid, verify tx2 gains control + // roll back to insertion of tx3, and don't insert it + // insert tx2, advance until it is valid, verify tx2 gains control - // Test 2: create 1 LBC claim (A), create 5 LBC claim (B), create 5 LBC support (A) - // Verify that A loses control when B becomes valid, and then A gains control when support becomes valid + // Put tx1 in the blockchain - // Test 3: create 1 LBC claim (A), create 5 LBC support (A), create 5 LBC claim(B), spend (A) support - // Verify that A retains control until support is spent + AddToMempool(tx1); + BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey)); + BOOST_CHECK(pblocktemplate->block.vtx.size() == 2); + pblocktemplate->block.hashPrevBlock = chainActive.Tip()->GetBlockHash(); + BOOST_CHECK(CreateBlock(pblocktemplate, pnp, support_nonces[block_counter++])); + delete pblocktemplate; - // Test 4: create 1 LBC claim (A), wait till valid, create 5 LBC claim (B), create 5 LBC support (A) - // Verify that A retains control throughout + BOOST_CHECK(pcoinsTip->HaveCoins(tx1.GetHash())); + BOOST_CHECK(pclaimTrie->empty()); + BOOST_CHECK(!pclaimTrie->queueEmpty()); + BOOST_CHECK(pclaimTrie->supportEmpty()); + BOOST_CHECK(pclaimTrie->supportQueueEmpty()); - // Test 5: create 5 LBC claim (A), wait till valid, create 1 LBC claim (B), create 5 LBC support (B) - // Verify that A retains control until support becomes valid + // advance 20 blocks - // Test 6: create 1 LBC claim (A), wait till valid, create 5 LBC claim (B), create 5 LBC support (A), spend A - // Verify that A retains control until it is spent + BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey)); + pblocktemplate->block.hashPrevBlock = chainActive.Tip()->GetBlockHash(); + for (unsigned int i = 1; i < 20; ++i) + { + BOOST_CHECK(CreateBlock(pblocktemplate, pnp, support_nonces[block_counter++])); + } + delete pblocktemplate; - // Test 7: create 1 LBC claim (A), wait till valid, create 5 LBC support (A), spend A + // Put tx3 into the blockchain + + AddToMempool(tx3); + BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey)); + BOOST_CHECK(pblocktemplate->block.vtx.size() == 2); + pblocktemplate->block.hashPrevBlock = chainActive.Tip()->GetBlockHash(); + BOOST_CHECK(CreateBlock(pblocktemplate, pnp, support_nonces[block_counter++])); + blocks_to_invalidate.push_back(pblocktemplate->block.hashPrevBlock); + delete pblocktemplate; + + BOOST_CHECK(pcoinsTip->HaveCoins(tx3.GetHash())); + BOOST_CHECK(pclaimTrie->supportEmpty()); + BOOST_CHECK(!pclaimTrie->supportQueueEmpty()); + + // advance 20 blocks + + BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey)); + pblocktemplate->block.hashPrevBlock = chainActive.Tip()->GetBlockHash(); + for (unsigned int i = 21; i < 40; ++i) + { + BOOST_CHECK(CreateBlock(pblocktemplate, pnp, support_nonces[block_counter++])); + } + delete pblocktemplate; + + // Put tx2 into the blockchain + + AddToMempool(tx2); + BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey)); + BOOST_CHECK(pblocktemplate->block.vtx.size() == 2); + pblocktemplate->block.hashPrevBlock = chainActive.Tip()->GetBlockHash(); + BOOST_CHECK(CreateBlock(pblocktemplate, pnp, support_nonces[block_counter++])); + delete pblocktemplate; + + BOOST_CHECK(pcoinsTip->HaveCoins(tx2.GetHash())); + BOOST_CHECK(pclaimTrie->empty()); + BOOST_CHECK(!pclaimTrie->queueEmpty()); + + // advance until tx1 is valid + + BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey)); + pblocktemplate->block.hashPrevBlock = chainActive.Tip()->GetBlockHash(); + for (unsigned int i = 41; i < 100; ++i) + { + BOOST_CHECK(CreateBlock(pblocktemplate, pnp, support_nonces[block_counter++])); + } + delete pblocktemplate; + + BOOST_CHECK(pclaimTrie->empty()); + BOOST_CHECK(!pclaimTrie->queueEmpty()); + + BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey)); + pblocktemplate->block.hashPrevBlock = chainActive.Tip()->GetBlockHash(); + BOOST_CHECK(CreateBlock(pblocktemplate, pnp, support_nonces[block_counter++])); + delete pblocktemplate; + + BOOST_CHECK(!pclaimTrie->empty()); + BOOST_CHECK(!pclaimTrie->queueEmpty()); + BOOST_CHECK(pclaimTrie->supportEmpty()); + BOOST_CHECK(!pclaimTrie->supportQueueEmpty()); + + BOOST_CHECK(pclaimTrie->getInfoForName(sName, val)); + BOOST_CHECK(val.txhash == tx1.GetHash()); + + // advance until tx3 is valid + + BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey)); + pblocktemplate->block.hashPrevBlock = chainActive.Tip()->GetBlockHash(); + for (unsigned int i = 101; i < 120; ++i) + { + BOOST_CHECK(CreateBlock(pblocktemplate, pnp, support_nonces[block_counter++])); + } + delete pblocktemplate; + + BOOST_CHECK(pclaimTrie->supportEmpty()); + BOOST_CHECK(!pclaimTrie->supportQueueEmpty()); + + BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey)); + pblocktemplate->block.hashPrevBlock = chainActive.Tip()->GetBlockHash(); + BOOST_CHECK(CreateBlock(pblocktemplate, pnp, support_nonces[block_counter++])); + delete pblocktemplate; + + BOOST_CHECK(!pclaimTrie->supportEmpty()); + BOOST_CHECK(pclaimTrie->supportQueueEmpty()); + + // advance until tx2 is valid + + BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey)); + pblocktemplate->block.hashPrevBlock = chainActive.Tip()->GetBlockHash(); + for (unsigned int i = 121; i < 140; ++i) + { + BOOST_CHECK(CreateBlock(pblocktemplate, pnp, support_nonces[block_counter++])); + } + delete pblocktemplate; + + BOOST_CHECK(!pclaimTrie->empty()); + BOOST_CHECK(!pclaimTrie->queueEmpty()); + + BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey)); + pblocktemplate->block.hashPrevBlock = chainActive.Tip()->GetBlockHash(); + BOOST_CHECK(CreateBlock(pblocktemplate, pnp, support_nonces[block_counter++])); + delete pblocktemplate; + + BOOST_CHECK(!pclaimTrie->empty()); + BOOST_CHECK(pclaimTrie->queueEmpty()); + BOOST_CHECK(pclaimTrie->getInfoForName(sName, val)); + BOOST_CHECK(val.txhash == tx1.GetHash()); + + // Test 2: create 1 LBC claim (tx1), create 5 LBC claim (tx2), create 5 LBC support (tx3) + // Verify that tx1 loses control when tx2 becomes valid, and then tx1 gains control when tx3 becomes valid + + // Test 3: create 1 LBC claim (tx1), create 5 LBC support (tx3), create 5 LBC claim(tx2), spend tx3 + // Verify that tx1 retains control until tx3 is spent + + // Test 4: create 1 LBC claim (tx1), wait till valid, create 5 LBC claim (tx2), create 5 LBC support (tx3) + // Verify that tx1 retains control throughout + + // Test 5: create 5 LBC claim (tx2), wait till valid, create 1 LBC claim (tx1), create 5 LBC support (tx3) + // Verify that tx2 retains control until support becomes valid + + // Test 6: create 1 LBC claim (tx1), wait till valid, create 5 LBC claim (tx2), create 5 LBC support (tx3), spend tx1 + // Verify that tx1 retains control until it is spent + + // Test 7: create 1 LBC claim (tx1), wait till valid, create 5 LBC support (tx3), spend tx1 + // Verify name trie is empty + + // Test 8: create 1 LBC claim (tx1), create 5 LBC support (tx3), wait till tx1 valid, spend tx1, wait till tx3 valid // Verify name trie is empty } diff --git a/src/undo.h b/src/undo.h index 34ee7f638..0ece53274 100644 --- a/src/undo.h +++ b/src/undo.h @@ -82,6 +82,7 @@ public: std::vector vtxundo; // for all but the coinbase CClaimTrieQueueUndo insertUndo; // any claims that went from the queue to the trie CClaimTrieQueueUndo expireUndo; // any claims that expired + CSupportValueQueueUndo insertSupportUndo; // any claims that went from the support queue to the support map ADD_SERIALIZE_METHODS; @@ -90,6 +91,7 @@ public: READWRITE(vtxundo); READWRITE(insertUndo); READWRITE(expireUndo); + READWRITE(insertSupportUndo); } };