diff --git a/src/main.cpp b/src/main.cpp index a5a8de444..1098c6f8c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1625,7 +1625,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.queueUndo)); + assert(trieCache.decrementBlock(blockUndo.insertUndo, blockUndo.expireUndo)); // undo transactions in reverse order for (int i = block.vtx.size() - 1; i >= 0; i--) { @@ -1934,7 +1934,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin pos.nTxOffset += ::GetSerializeSize(tx, SER_DISK, CLIENT_VERSION); } - assert(trieCache.incrementBlock(blockundo.queueUndo)); + assert(trieCache.incrementBlock(blockundo.insertUndo, blockundo.expireUndo)); if (trieCache.getMerkleHash() != block.hashNCCTrie) return state.DoS(100, diff --git a/src/miner.cpp b/src/miner.cpp index 65f661ce5..0101901c6 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -394,8 +394,9 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn) pblock->nBits = GetNextWorkRequired(pindexPrev, pblock, Params().GetConsensus()); pblock->nNonce = 0; pblocktemplate->vTxSigOps[0] = GetLegacySigOpCount(pblock->vtx[0]); - CNCCTrieQueueUndo dummyundo; - trieCache.incrementBlock(dummyundo); + CNCCTrieQueueUndo dummyInsertUndo; + CNCCTrieQueueUndo dummyExpireUndo; + trieCache.incrementBlock(dummyInsertUndo, dummyExpireUndo); pblock->hashNCCTrie = trieCache.getMerkleHash(); CValidationState state; diff --git a/src/ncctrie.cpp b/src/ncctrie.cpp index 7163a1d9f..4551681f1 100644 --- a/src/ncctrie.cpp +++ b/src/ncctrie.cpp @@ -153,6 +153,43 @@ bool CNCCTrie::queueEmpty() const return true; } +bool CNCCTrie::expirationQueueEmpty() const +{ + for (valueQueueType::const_iterator itRow = dirtyExpirationQueueRows.begin(); itRow != dirtyExpirationQueueRows.end(); ++itRow) + { + if (!itRow->second.empty()) + return false; + } + boost::scoped_ptr pcursor(const_cast(&db)->NewIterator()); + pcursor->SeekToFirst(); + + while(pcursor->Valid()) + { + 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(); + } + return true; +} + +void CNCCTrie::setExpirationTime(int t) +{ + nExpirationTime = t; +} + void CNCCTrie::clear() { clear(&root); @@ -338,6 +375,17 @@ bool CNCCTrie::getQueueRow(int nHeight, std::vector& row) return db.Read(std::make_pair('r', nHeight), row); } +bool CNCCTrie::getExpirationQueueRow(int nHeight, std::vector& row) +{ + valueQueueType::iterator itQueueRow = dirtyExpirationQueueRows.find(nHeight); + if (itQueueRow != dirtyExpirationQueueRows.end()) + { + row = itQueueRow->second; + return true; + } + return db.Read(std::make_pair('e', nHeight), row); +} + void CNCCTrie::updateQueueRow(int nHeight, std::vector& row) { valueQueueType::iterator itQueueRow = dirtyQueueRows.find(nHeight); @@ -352,7 +400,21 @@ void CNCCTrie::updateQueueRow(int nHeight, std::vector& row) itQueueRow->second.swap(row); } -bool CNCCTrie::update(nodeCacheType& cache, hashMapType& hashes, const uint256& hashBlockIn, valueQueueType& queueCache, int nNewHeight) +void CNCCTrie::updateExpirationRow(int nHeight, std::vector& row) +{ + valueQueueType::iterator itQueueRow = dirtyExpirationQueueRows.find(nHeight); + if (itQueueRow == dirtyExpirationQueueRows.end()) + { + std::vector newRow; + std::pair ret; + ret = dirtyExpirationQueueRows.insert(std::pair >(nHeight, newRow)); + assert(ret.second); + itQueueRow = ret.first; + } + itQueueRow->second.swap(row); +} + +bool CNCCTrie::update(nodeCacheType& cache, hashMapType& hashes, const uint256& hashBlockIn, valueQueueType& queueCache, valueQueueType& expirationQueueCache, int nNewHeight) { // General strategy: the cache is ordered by length, ensuring child // nodes are always inserted after their parents. Insert each node @@ -390,6 +452,10 @@ bool CNCCTrie::update(nodeCacheType& cache, hashMapType& hashes, const uint256& { updateQueueRow(itQueueCacheRow->first, itQueueCacheRow->second); } + for (valueQueueType::iterator itExpirationRow = expirationQueueCache.begin(); itExpirationRow != expirationQueueCache.end(); ++itExpirationRow) + { + updateExpirationRow(itExpirationRow->first, itExpirationRow->second); + } hashBlock = hashBlockIn; nCurrentHeight = nNewHeight; return true; @@ -505,6 +571,21 @@ void CNCCTrie::BatchWriteQueueRows(CLevelDBBatch& batch) } } +void CNCCTrie::BatchWriteExpirationQueueRows(CLevelDBBatch& batch) +{ + for (valueQueueType::iterator itQueue = dirtyExpirationQueueRows.begin(); itQueue != dirtyExpirationQueueRows.end(); ++itQueue) + { + if (itQueue->second.empty()) + { + batch.Erase(std::make_pair('e', itQueue->first)); + } + else + { + batch.Write(std::make_pair('e', itQueue->first), itQueue->second); + } + } +} + bool CNCCTrie::WriteToDisk() { CLevelDBBatch batch; @@ -513,6 +594,8 @@ bool CNCCTrie::WriteToDisk() dirtyNodes.clear(); BatchWriteQueueRows(batch); dirtyQueueRows.clear(); + BatchWriteExpirationQueueRows(batch); + dirtyExpirationQueueRows.clear(); batch.Write('h', hashBlock); batch.Write('t', nCurrentHeight); return db.WriteBatch(batch); @@ -928,7 +1011,7 @@ bool CNCCTrieCache::addClaim(const std::string name, uint256 txhash, uint32_t nO { LogPrintf("%s: name: %s, txhash: %s, nOut: %d, nAmount: %d, nHeight: %d, nCurrentHeight: %d\n", __func__, name, txhash.GetHex(), nOut, nAmount, nHeight, nCurrentHeight); assert(nHeight == nCurrentHeight); - return addClaimToQueue(name, txhash, nOut, nAmount, nHeight, nHeight + DEFAULT_DELAY); + return addClaimToQueues(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 @@ -941,7 +1024,7 @@ bool CNCCTrieCache::addClaim(const std::string name, uint256 txhash, uint32_t nO if (val.txhash == prevTxhash && val.nOut == nPrevOut) { LogPrintf("%s: This is an update to a best claim. Previous claim txhash: %s, nOut: %d\n", __func__, prevTxhash.GetHex(), nPrevOut); - return addClaimToQueue(name, txhash, nOut, nAmount, nHeight, nHeight); + return addClaimToQueues(name, txhash, nOut, nAmount, nHeight, nHeight); } } return addClaim(name, txhash, nOut, nAmount, nHeight); @@ -955,21 +1038,23 @@ bool CNCCTrieCache::undoSpendClaim(const std::string name, uint256 txhash, uint3 CNodeValue val(txhash, nOut, nAmount, nHeight, nValidAtHeight); CValueQueueEntry entry(name, val); insertClaimIntoTrie(name, CNodeValue(txhash, nOut, nAmount, nHeight, nValidAtHeight)); + addToExpirationQueue(entry); } else { - addClaimToQueue(name, txhash, nOut, nAmount, nHeight, nValidAtHeight); + addClaimToQueues(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 +bool CNCCTrieCache::addClaimToQueues(const std::string name, uint256 txhash, uint32_t nOut, CAmount nAmount, int nHeight, int nValidAtHeight) const { LogPrintf("%s: nValidAtHeight: %d\n", __func__, nValidAtHeight); CNodeValue val(txhash, nOut, nAmount, nHeight, nValidAtHeight); CValueQueueEntry entry(name, val); valueQueueType::iterator itQueueRow = getQueueCacheRow(nValidAtHeight, true); itQueueRow->second.push_back(entry); + addToExpirationQueue(entry); return true; } @@ -1012,49 +1097,122 @@ bool CNCCTrieCache::spendClaim(const std::string name, uint256 txhash, uint32_t bool CNCCTrieCache::removeClaim(const std::string name, uint256 txhash, uint32_t nOut, int nHeight, int& nValidAtHeight) const { LogPrintf("%s: name: %s, txhash: %s, nOut: %s, nHeight: %s, nCurrentHeight: %s\n", __func__, name, txhash.GetHex(), nOut, nHeight, nCurrentHeight); + bool removed = false; 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; + removed = true; + else if (removeClaimFromQueue(name, txhash, nOut, nHeight, nValidAtHeight)) + removed = true; } - if (removeClaimFromQueue(name, txhash, nOut, nHeight, nCurrentHeight)) - return true; - return removeClaimFromTrie(name, txhash, nOut, nValidAtHeight); + if (removed == false && removeClaimFromQueue(name, txhash, nOut, nHeight, nCurrentHeight)) + removed = true; + if (removed == false && removeClaimFromTrie(name, txhash, nOut, nValidAtHeight)) + removed = true; + if (removed == true) + removeFromExpirationQueue(name, txhash, nOut, nHeight); + return removed; } -bool CNCCTrieCache::incrementBlock(CNCCTrieQueueUndo& undo) const +void CNCCTrieCache::addToExpirationQueue(CValueQueueEntry& entry) const +{ + int expirationHeight = entry.val.nHeight + base->nExpirationTime; + valueQueueType::iterator itQueueRow = getExpirationQueueCacheRow(expirationHeight, true); + itQueueRow->second.push_back(entry); +} + +void CNCCTrieCache::removeFromExpirationQueue(const std::string name, uint256 txhash, uint32_t nOut, int nHeight) const +{ + int expirationHeight = nHeight + base->nExpirationTime; + valueQueueType::iterator itQueueRow = getExpirationQueueCacheRow(expirationHeight, false); + std::vector::iterator itQueue; + if (itQueueRow != valueQueueCache.end()) + { + for (itQueue = itQueueRow->second.begin(); itQueue != itQueueRow->second.end(); ++itQueue) + { + CNodeValue& val = itQueue->val; + if (name == itQueue->name && val.txhash == txhash && val.nOut == nOut) + break; + } + } + if (itQueue != itQueueRow->second.end()) + { + itQueueRow->second.erase(itQueue); + } +} + +valueQueueType::iterator CNCCTrieCache::getExpirationQueueCacheRow(int nHeight, bool createIfNotExists) const +{ + valueQueueType::iterator itQueueRow = expirationQueueCache.find(nHeight); + if (itQueueRow == expirationQueueCache.end()) + { + // Have to make a new row it put in the cache, if createIfNotExists is true + std::vector queueRow; + // If the row exists in the base, copy its values into the new row. + bool exists = base->getExpirationQueueRow(nHeight, queueRow); + if (!exists) + if (!createIfNotExists) + return itQueueRow; + // Stick the new row in the cache + std::pair ret; + ret = expirationQueueCache.insert(std::pair >(nHeight, queueRow)); + assert(ret.second); + itQueueRow = ret.first; + } + return itQueueRow; +} + +bool CNCCTrieCache::incrementBlock(CNCCTrieQueueUndo& insertUndo, CNCCTrieQueueUndo& expireUndo) const { LogPrintf("%s: nCurrentHeight (before increment): %d\n", __func__, nCurrentHeight); valueQueueType::iterator itQueueRow = getQueueCacheRow(nCurrentHeight, false); + if (itQueueRow != valueQueueCache.end()) + { + for (std::vector::iterator itEntry = itQueueRow->second.begin(); itEntry != itQueueRow->second.end(); ++itEntry) + { + insertClaimIntoTrie(itEntry->name, itEntry->val); + insertUndo.push_back(*itEntry); + } + itQueueRow->second.clear(); + } + valueQueueType::iterator itExpirationRow = getExpirationQueueCacheRow(nCurrentHeight, false); + if (itExpirationRow != expirationQueueCache.end()) + { + for (std::vector::iterator itEntry = itExpirationRow->second.begin(); itEntry != itExpirationRow->second.end(); ++itEntry) + { + int nValidAtHeight; + assert(base->nExpirationTime > DEFAULT_DELAY); + assert(removeClaimFromTrie(itEntry->name, itEntry->val.txhash, itEntry->val.nOut, nValidAtHeight)); + expireUndo.push_back(*itEntry); + } + itExpirationRow->second.clear(); + } nCurrentHeight++; - if (itQueueRow == valueQueueCache.end()) - { - return true; - } - for (std::vector::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 +bool CNCCTrieCache::decrementBlock(CNCCTrieQueueUndo& insertUndo, CNCCTrieQueueUndo& expireUndo) const { LogPrintf("%s: nCurrentHeight (before decrement): %d\n", __func__, nCurrentHeight); nCurrentHeight--; - if (undo.begin() != undo.end()) + if (insertUndo.begin() != insertUndo.end()) { valueQueueType::iterator itQueueRow = getQueueCacheRow(nCurrentHeight, true); - for (CNCCTrieQueueUndo::iterator itUndo = undo.begin(); itUndo != undo.end(); ++itUndo) + for (CNCCTrieQueueUndo::iterator itInsertUndo = insertUndo.begin(); itInsertUndo != insertUndo.end(); ++itInsertUndo) { int nValidHeightInTrie; - assert(removeClaimFromTrie(itUndo->name, itUndo->val.txhash, itUndo->val.nOut, nValidHeightInTrie)); - assert(nValidHeightInTrie == itUndo->val.nValidAtHeight); - itQueueRow->second.push_back(*itUndo); + assert(removeClaimFromTrie(itInsertUndo->name, itInsertUndo->val.txhash, itInsertUndo->val.nOut, nValidHeightInTrie)); + assert(nValidHeightInTrie == itInsertUndo->val.nValidAtHeight); + itQueueRow->second.push_back(*itInsertUndo); + } + } + if (expireUndo.begin() != expireUndo.end()) + { + valueQueueType::iterator itExpireRow = getExpirationQueueCacheRow(nCurrentHeight, true); + for (CNCCTrieQueueUndo::iterator itExpireUndo = expireUndo.begin(); itExpireUndo != expireUndo.end(); ++itExpireUndo) + { + insertClaimIntoTrie(itExpireUndo->name, itExpireUndo->val); + itExpireRow->second.push_back(*itExpireUndo); } } return true; @@ -1090,7 +1248,7 @@ bool CNCCTrieCache::flush() { if (dirty()) getMerkleHash(); - bool success = base->update(cache, cacheHashes, getBestBlock(), valueQueueCache, nCurrentHeight); + bool success = base->update(cache, cacheHashes, getBestBlock(), valueQueueCache, expirationQueueCache, nCurrentHeight); if (success) { success = clear(); diff --git a/src/ncctrie.h b/src/ncctrie.h index 22aeb3411..42843f8da 100644 --- a/src/ncctrie.h +++ b/src/ncctrie.h @@ -147,7 +147,7 @@ class CNCCTrieCache; class CNCCTrie { public: - CNCCTrie(bool fMemory = false, bool fWipe = false) : db(GetDataDir() / "ncctrie", 100, fMemory, fWipe), nCurrentHeight(0), root(uint256S("0000000000000000000000000000000000000000000000000000000000000001")) {} + CNCCTrie(bool fMemory = false, bool fWipe = false) : db(GetDataDir() / "ncctrie", 100, fMemory, fWipe), nCurrentHeight(0), nExpirationTime(262974), root(uint256S("0000000000000000000000000000000000000000000000000000000000000001")) {} uint256 getMerkleHash(); CLevelDBWrapper db; bool empty() const; @@ -159,15 +159,19 @@ public: bool getInfoForName(const std::string& name, CNodeValue& val) const; int nCurrentHeight; bool queueEmpty() const; + bool expirationQueueEmpty() const; + void setExpirationTime(int t); bool getQueueRow(int nHeight, std::vector& row); + bool getExpirationQueueRow(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; CAmount getTotalValueOfClaimsInTrie(bool fControllingOnly) const; friend class CNCCTrieCache; + int nExpirationTime; private: void clear(CNCCTrieNode* current); - bool update(nodeCacheType& cache, hashMapType& hashes, const uint256& hashBlock, valueQueueType& queueCache, int nNewHeight); + bool update(nodeCacheType& cache, hashMapType& hashes, const uint256& hashBlock, valueQueueType& queueCache, valueQueueType& expirationQueueCache, int nNewHeight); bool updateName(const std::string& name, CNCCTrieNode* updatedNode); bool updateHash(const std::string& name, uint256& hash); bool recursiveNullify(CNCCTrieNode* node, std::string& name); @@ -180,14 +184,16 @@ private: CNCCTrieNode root; uint256 hashBlock; valueQueueType dirtyQueueRows; + valueQueueType dirtyExpirationQueueRows; nodeCacheType dirtyNodes; void markNodeDirty(const std::string& name, CNCCTrieNode* node); - void deleteQueueRow(int nHeight); void updateQueueRow(int nHeight, std::vector& row); + void updateExpirationRow(int nHeight, std::vector& row); void BatchWriteNode(CLevelDBBatch& batch, const std::string& name, const CNCCTrieNode* pNode) const; void BatchEraseNode(CLevelDBBatch& batch, const std::string& nome) const; void BatchWriteQueueRows(CLevelDBBatch& batch); + void BatchWriteExpirationQueueRows(CLevelDBBatch& batch); }; class CNCCTrieCache @@ -205,8 +211,8 @@ public: 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; + bool incrementBlock(CNCCTrieQueueUndo& insertUndo, CNCCTrieQueueUndo& expireUndo) const; + bool decrementBlock(CNCCTrieQueueUndo& insertUndo, CNCCTrieQueueUndo& expireUndo) 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; @@ -217,6 +223,7 @@ private: mutable std::set dirtyHashes; mutable hashMapType cacheHashes; mutable valueQueueType valueQueueCache; + mutable valueQueueType expirationQueueCache; 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; @@ -224,9 +231,12 @@ private: 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 addClaimToQueues(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; + void addToExpirationQueue(CValueQueueEntry& entry) const; + 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; uint256 hashBlock; }; diff --git a/src/test/ncctrie_tests.cpp b/src/test/ncctrie_tests.cpp index 487bd71b9..4fac1c6cc 100644 --- a/src/test/ncctrie_tests.cpp +++ b/src/test/ncctrie_tests.cpp @@ -14,7 +14,7 @@ using namespace std; -const unsigned int nonces[] = { +const unsigned int insert_nonces[] = { 62302, 78404, 42509, 88397, 232147, 34120, 48944, 8449, 3855, 99418, 35007, 36992, 18865, 48021, 117592, 61911, 26614, 26267, 171911, 49917, 68274, 19360, 48650, 22711, 102612, 73362, 7375, 39379, 413, 123283, @@ -83,6 +83,64 @@ const unsigned int nonces[] = { 102894, 41478, 509, 169951, 5571, 28284, 8138, 53248, 47878, 113791, 192277, 73645, 28672, 93670, 30741, 129667}; +const unsigned int expire_nonces[] = { + 1772, 19895, 52494, 75900, 15954, 92687, 381518, 24750, 131218, 140637, + 25151, 4751, 117588, 31281, 741, 13006, 56207, 23174, 32024, 23597, + 17093, 22998, 68975, 84976, 47024, 48818, 29830, 28891, 66091, 6304, + 117, 253782, 126584, 55982, 43172, 5862, 36712, 289031, 42833, 61902, + 81993, 26790, 80145, 111102, 27904, 16188, 55120, 7171, 67694, 214899, + 16360, 133920, 31442, 103537, 266284, 4407, 102008, 1149, 156950, 76029, + 33751, 20831, 24714, 202595, 9393, 227174, 3357, 70, 45445, 104755, + 6024, 13512, 163718, 43755, 17582, 17069, 17355, 38898, 101900, 41342, + 20701, 9827, 11663, 146362, 189082, 279537, 79142, 423101, 11154, 103046, + 44740, 90350, 41450, 4627, 4685, 14510, 13639, 13705, 2369, 326596, + 74814, 15068, 7287, 26685, 41834, 5872, 94033, 77510, 21271, 51233, + 687, 84716, 18945, 139111, 11508, 15009, 103492, 25317, 100121, 29105, + 26520, 531, 6883, 181308, 161707, 72079, 50032, 65286, 96365, 18280, + 21015, 56591, 90913, 3280, 74270, 87610, 85124, 58599, 161342, 36017, + 135104, 19625, 114763, 163177, 31400, 99871, 15939, 6822, 51007, 33240, + 15449, 28232, 242057, 45744, 253284, 218826, 25843, 118582, 228181, 7592, + 3398, 23637, 46762, 22853, 7669, 358599, 99238, 32782, 46407, 36545, + 28976, 34995, 23564, 60624, 45957, 162829, 7055, 184611, 69270, 22560, + 17314, 24374, 67156, 44705, 9482, 17611, 92669, 22471, 47688, 110960, + 18595, 46376, 23989, 47847, 41231, 2937, 34056, 61839, 9393, 122723, + 119967, 41795, 13244, 22081, 41407, 65568, 120355, 228591, 3763, 23561, + 78832, 20110, 3849, 162319, 9699, 153960, 60013, 22063, 22895, 84997, + 20008, 6201, 18985, 7065, 63655, 35849, 19788, 69977, 45664, 15647, + 111735, 1372, 2487, 38856, 39081, 74496, 71062, 20232, 33091, 26777, + 155180, 149434, 35380, 102844, 46709, 3373, 137389, 33273, 13194, 5106, + 69109, 21599, 24896, 16688, 101584, 567, 61253, 125648, 30825, 38976, + 88494, 82214, 84079, 52761, 16392, 206857, 19856, 48376, 11820, 21296, + 12707, 94783, 31272, 113581, 40682, 143247, 1206, 21720, 40627, 131077, + 78794, 9026, 38830, 39757, 94577, 20134, 15904, 185817, 35595, 21609, + 15868, 187480, 62515, 36383, 12797, 101029, 17979, 48892, 59215, 197023, + 217536, 295885, 5639, 24470, 440, 96896, 3113, 74270, 8773, 89871, + 41807, 33896, 41053, 87038, 64666, 2642, 39880, 113389, 51922, 64364, + 46026, 34424, 6856, 51050, 48871, 43033, 95444, 68098, 10463, 27119, + 101386, 28555, 94429, 38046, 13110, 39449, 976, 12015, 32827, 20156, + 79086, 90632, 24716, 11632, 27225, 11563, 45437, 41804, 240056, 26298, + 7089, 119823, 87043, 46315, 108722, 24002, 109328, 32013, 47232, 243845, + 9604, 158119, 189402, 124127, 89656, 39496, 63807, 83836, 80843, 2261, + 103229, 24028, 299032, 43513, 8506, 362389, 14113, 47973, 145654, 23170, + 23272, 35090, 17628, 28197, 96581, 84151, 694, 182728, 86514, 129029, + 4509, 48647, 63280, 86829, 138046, 8075, 59670, 53232, 37353, 51966, + 62110, 66890, 75512, 42296, 240099, 64624, 98903, 29189, 2593, 17376, + 11417, 2982, 3273, 40001, 468410, 29603, 111819, 36878, 6710, 14350, + 87778, 69800, 18820, 75880, 26049, 36685, 8500, 189289, 186944, 100520, + 167630, 7170, 115904, 79212, 4792, 82507, 13634, 71118, 27066, 66914, + 44963, 58999, 153183, 184462, 65596, 102627, 333388, 164930, 6664, 120605, + 141, 122317, 162948, 93160, 216861, 68595, 67370, 15526, 4320, 45961, + 86806, 61077, 61730, 117548, 127472, 1191, 15735, 133987, 16288, 4652, + 83674, 21872, 21351, 35691, 11584, 85669, 196, 75276, 45275, 55828, + 46586, 153482, 6183, 386, 28729, 111942, 2847, 10584, 207349, 57260, + 12075, 27552, 129129, 282325, 11954, 28300, 175155, 31256, 12953, 16345, + 34101, 57962, 16950, 9103, 94414, 1918, 69031, 151629, 41281, 85752, + 66681, 31646, 12396, 167, 72005, 3936, 27566, 5663, 24098, 73350, + 19076, 179631, 36500, 152550, 56501, 81202, 77561, 134713, 81130, 22321, + 112081, 32992, 144573, 21369, 2471, 18371, 63050, 44211, 6147, 206052, + 34252, 534, 20176, 58035, 24268, 19608, 37770, 57588, 120961, 58415, + 4780, 4614, 229320, 42279, 41295, 23501, 78183}; + BOOST_FIXTURE_TEST_SUITE(ncctrie_tests, TestingSetup) CMutableTransaction BuildTransaction(const uint256& prevhash) @@ -129,7 +187,7 @@ void AddToMempool(CMutableTransaction& tx) mempool.addUnchecked(tx.GetHash(), CTxMemPoolEntry(tx, 0, GetTime(), 111.0, chainActive.Height())); } -bool CreateBlock(CBlockTemplate* pblocktemplate, bool f = false) +bool CreateBlock(CBlockTemplate* pblocktemplate, int nonce) { static int unique_block_counter = 0; CBlock* pblock = &pblocktemplate->block; @@ -140,18 +198,24 @@ bool CreateBlock(CBlockTemplate* pblocktemplate, bool f = false) txCoinbase.vout[0].nValue = GetBlockSubsidy(chainActive.Height(), Params().GetConsensus()); pblock->vtx[0] = CTransaction(txCoinbase); pblock->hashMerkleRoot = pblock->BuildMerkleTree(); - pblock->nNonce = nonces[unique_block_counter - 1]; - /*for (int i = 0; ; ++i) + //if (nonce != -1) + //{ + pblock->nNonce = nonce; + //} + /*else { - pblock->nNonce = i; - if (CheckProofOfWork(pblock->GetHash(), pblock->nBits, Params().GetConsensus())) + for (int i = 0; ; ++i) { - std::cout << pblock->nNonce << ","; - if (unique_block_counter % 10 == 0) - std::cout << std::endl; - else - std::cout << " "; - break; + pblock->nNonce = i; + if (CheckProofOfWork(pblock->GetHash(), pblock->nBits, Params().GetConsensus())) + { + std::cout << pblock->nNonce << ","; + if (unique_block_counter % 10 == 0) + std::cout << std::endl; + else + std::cout << " "; + break; + } } }*/ CValidationState state; @@ -290,6 +354,7 @@ BOOST_AUTO_TEST_CASE(ncctrie_merkle_hash) BOOST_AUTO_TEST_CASE(ncctrie_insert_update_claim) { + int block_counter = 0; BOOST_CHECK(pnccTrie->nCurrentHeight == chainActive.Height() + 1); CBlockTemplate *pblocktemplate; @@ -315,7 +380,7 @@ BOOST_AUTO_TEST_CASE(ncctrie_insert_update_claim) for (unsigned int i = 0; i < 103; ++i) { - BOOST_CHECK(CreateBlock(pblocktemplate)); + BOOST_CHECK(CreateBlock(pblocktemplate, insert_nonces[block_counter++])); if (coinbases.size() < 3) coinbases.push_back(CTransaction(pblocktemplate->block.vtx[0])); } @@ -352,7 +417,7 @@ BOOST_AUTO_TEST_CASE(ncctrie_insert_update_claim) BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey)); BOOST_CHECK(pblocktemplate->block.vtx.size() == 3); pblocktemplate->block.hashPrevBlock = chainActive.Tip()->GetBlockHash(); - BOOST_CHECK(CreateBlock(pblocktemplate)); + BOOST_CHECK(CreateBlock(pblocktemplate, insert_nonces[block_counter++])); blocks_to_invalidate.push_back(pblocktemplate->block.hashPrevBlock); delete pblocktemplate; @@ -363,7 +428,7 @@ BOOST_AUTO_TEST_CASE(ncctrie_insert_update_claim) pblocktemplate->block.hashPrevBlock = chainActive.Tip()->GetBlockHash(); for (unsigned int i = 1; i < 100; ++i) { - BOOST_CHECK(CreateBlock(pblocktemplate)); + BOOST_CHECK(CreateBlock(pblocktemplate, insert_nonces[block_counter++])); } delete pblocktemplate; @@ -372,7 +437,7 @@ BOOST_AUTO_TEST_CASE(ncctrie_insert_update_claim) BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey)); pblocktemplate->block.hashPrevBlock = chainActive.Tip()->GetBlockHash(); - BOOST_CHECK(CreateBlock(pblocktemplate)); + BOOST_CHECK(CreateBlock(pblocktemplate, insert_nonces[block_counter++])); delete pblocktemplate; // Verify tx1 and tx2 are in the trie @@ -389,7 +454,7 @@ BOOST_AUTO_TEST_CASE(ncctrie_insert_update_claim) BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey)); BOOST_CHECK(pblocktemplate->block.vtx.size() == 4); pblocktemplate->block.hashPrevBlock = chainActive.Tip()->GetBlockHash(); - BOOST_CHECK(CreateBlock(pblocktemplate)); + BOOST_CHECK(CreateBlock(pblocktemplate, insert_nonces[block_counter++])); blocks_to_invalidate.push_back(pblocktemplate->block.hashPrevBlock); delete pblocktemplate; @@ -429,7 +494,7 @@ BOOST_AUTO_TEST_CASE(ncctrie_insert_update_claim) BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey)); BOOST_CHECK(pblocktemplate->block.vtx.size() == 2); pblocktemplate->block.hashPrevBlock = chainActive.Tip()->GetBlockHash(); - BOOST_CHECK(CreateBlock(pblocktemplate, true)); + BOOST_CHECK(CreateBlock(pblocktemplate, insert_nonces[block_counter++])); blocks_to_invalidate.push_back(pblocktemplate->block.hashPrevBlock); delete pblocktemplate; @@ -451,7 +516,7 @@ BOOST_AUTO_TEST_CASE(ncctrie_insert_update_claim) BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey)); BOOST_CHECK(pblocktemplate->block.vtx.size() == 2); pblocktemplate->block.hashPrevBlock = chainActive.Tip()->GetBlockHash(); - BOOST_CHECK(CreateBlock(pblocktemplate)); + BOOST_CHECK(CreateBlock(pblocktemplate, insert_nonces[block_counter++])); blocks_to_invalidate.push_back(pblocktemplate->block.hashPrevBlock); delete pblocktemplate; @@ -462,7 +527,7 @@ BOOST_AUTO_TEST_CASE(ncctrie_insert_update_claim) pblocktemplate->block.hashPrevBlock = chainActive.Tip()->GetBlockHash(); for (unsigned int i = 1; i < 100; ++i) { - BOOST_CHECK(CreateBlock(pblocktemplate)); + BOOST_CHECK(CreateBlock(pblocktemplate, insert_nonces[block_counter++])); } delete pblocktemplate; @@ -471,7 +536,7 @@ BOOST_AUTO_TEST_CASE(ncctrie_insert_update_claim) BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey)); pblocktemplate->block.hashPrevBlock = chainActive.Tip()->GetBlockHash(); - BOOST_CHECK(CreateBlock(pblocktemplate)); + BOOST_CHECK(CreateBlock(pblocktemplate, insert_nonces[block_counter++])); delete pblocktemplate; BOOST_CHECK(!pnccTrie->empty()); @@ -497,7 +562,7 @@ BOOST_AUTO_TEST_CASE(ncctrie_insert_update_claim) BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey)); BOOST_CHECK(pblocktemplate->block.vtx.size() == 3); pblocktemplate->block.hashPrevBlock = chainActive.Tip()->GetBlockHash(); - BOOST_CHECK(CreateBlock(pblocktemplate)); + BOOST_CHECK(CreateBlock(pblocktemplate, insert_nonces[block_counter++])); blocks_to_invalidate.push_back(pblocktemplate->block.hashPrevBlock); delete pblocktemplate; @@ -519,7 +584,7 @@ BOOST_AUTO_TEST_CASE(ncctrie_insert_update_claim) BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey)); BOOST_CHECK(pblocktemplate->block.vtx.size() == 2); pblocktemplate->block.hashPrevBlock = chainActive.Tip()->GetBlockHash(); - BOOST_CHECK(CreateBlock(pblocktemplate)); + BOOST_CHECK(CreateBlock(pblocktemplate, insert_nonces[block_counter++])); blocks_to_invalidate.push_back(pblocktemplate->block.hashPrevBlock); delete pblocktemplate; BOOST_CHECK(pnccTrie->empty()); @@ -529,7 +594,7 @@ BOOST_AUTO_TEST_CASE(ncctrie_insert_update_claim) pblocktemplate->block.hashPrevBlock = chainActive.Tip()->GetBlockHash(); for (unsigned int i = 1; i < 50; ++i) { - BOOST_CHECK(CreateBlock(pblocktemplate)); + BOOST_CHECK(CreateBlock(pblocktemplate, insert_nonces[block_counter++])); } delete pblocktemplate; BOOST_CHECK(pnccTrie->empty()); @@ -542,7 +607,7 @@ BOOST_AUTO_TEST_CASE(ncctrie_insert_update_claim) BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey)); BOOST_CHECK(pblocktemplate->block.vtx.size() == 2); pblocktemplate->block.hashPrevBlock = chainActive.Tip()->GetBlockHash(); - BOOST_CHECK(CreateBlock(pblocktemplate)); + BOOST_CHECK(CreateBlock(pblocktemplate, insert_nonces[block_counter++])); blocks_to_invalidate.push_back(pblocktemplate->block.hashPrevBlock); delete pblocktemplate; BOOST_CHECK(pnccTrie->empty()); @@ -552,7 +617,7 @@ BOOST_AUTO_TEST_CASE(ncctrie_insert_update_claim) pblocktemplate->block.hashPrevBlock = chainActive.Tip()->GetBlockHash(); for (unsigned int i = 1; i < 51; ++i) { - BOOST_CHECK(CreateBlock(pblocktemplate)); + BOOST_CHECK(CreateBlock(pblocktemplate, insert_nonces[block_counter++])); } delete pblocktemplate; BOOST_CHECK(pnccTrie->empty()); @@ -571,7 +636,7 @@ BOOST_AUTO_TEST_CASE(ncctrie_insert_update_claim) pblocktemplate->block.hashPrevBlock = chainActive.Tip()->GetBlockHash(); for (unsigned int i = 0; i < 50; ++i) { - BOOST_CHECK(CreateBlock(pblocktemplate)); + BOOST_CHECK(CreateBlock(pblocktemplate, insert_nonces[block_counter++])); } delete pblocktemplate; @@ -580,7 +645,7 @@ BOOST_AUTO_TEST_CASE(ncctrie_insert_update_claim) BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey)); pblocktemplate->block.hashPrevBlock = chainActive.Tip()->GetBlockHash(); - BOOST_CHECK(CreateBlock(pblocktemplate)); + BOOST_CHECK(CreateBlock(pblocktemplate, insert_nonces[block_counter++])); delete pblocktemplate; BOOST_CHECK(!pnccTrie->empty()); BOOST_CHECK(pnccTrie->queueEmpty()); @@ -596,7 +661,7 @@ BOOST_AUTO_TEST_CASE(ncctrie_insert_update_claim) BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey)); BOOST_CHECK(pblocktemplate->block.vtx.size() == 2); pblocktemplate->block.hashPrevBlock = chainActive.Tip()->GetBlockHash(); - BOOST_CHECK(CreateBlock(pblocktemplate)); + BOOST_CHECK(CreateBlock(pblocktemplate, insert_nonces[block_counter++])); blocks_to_invalidate.push_back(pblocktemplate->block.hashPrevBlock); delete pblocktemplate; @@ -628,7 +693,7 @@ BOOST_AUTO_TEST_CASE(ncctrie_insert_update_claim) BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey)); BOOST_CHECK(pblocktemplate->block.vtx.size() == 2); pblocktemplate->block.hashPrevBlock = chainActive.Tip()->GetBlockHash(); - BOOST_CHECK(CreateBlock(pblocktemplate, true)); + BOOST_CHECK(CreateBlock(pblocktemplate, insert_nonces[block_counter++])); blocks_to_invalidate.push_back(pblocktemplate->block.hashPrevBlock); delete pblocktemplate; @@ -641,7 +706,7 @@ BOOST_AUTO_TEST_CASE(ncctrie_insert_update_claim) pblocktemplate->block.hashPrevBlock = chainActive.Tip()->GetBlockHash(); for (unsigned int i = 1; i < 50; ++i) { - BOOST_CHECK(CreateBlock(pblocktemplate)); + BOOST_CHECK(CreateBlock(pblocktemplate, insert_nonces[block_counter++])); } delete pblocktemplate; BOOST_CHECK(pnccTrie->empty()); @@ -654,7 +719,7 @@ BOOST_AUTO_TEST_CASE(ncctrie_insert_update_claim) BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey)); BOOST_CHECK(pblocktemplate->block.vtx.size() == 2); pblocktemplate->block.hashPrevBlock = chainActive.Tip()->GetBlockHash(); - BOOST_CHECK(CreateBlock(pblocktemplate)); + BOOST_CHECK(CreateBlock(pblocktemplate, insert_nonces[block_counter++])); blocks_to_invalidate.push_back(pblocktemplate->block.hashPrevBlock); delete pblocktemplate; @@ -668,7 +733,7 @@ BOOST_AUTO_TEST_CASE(ncctrie_insert_update_claim) BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey)); BOOST_CHECK(pblocktemplate->block.vtx.size() == 2); pblocktemplate->block.hashPrevBlock = chainActive.Tip()->GetBlockHash(); - BOOST_CHECK(CreateBlock(pblocktemplate)); + BOOST_CHECK(CreateBlock(pblocktemplate, insert_nonces[block_counter++])); blocks_to_invalidate.push_back(pblocktemplate->block.hashPrevBlock); delete pblocktemplate; @@ -689,7 +754,7 @@ BOOST_AUTO_TEST_CASE(ncctrie_insert_update_claim) pblocktemplate->block.hashPrevBlock = chainActive.Tip()->GetBlockHash(); for (unsigned int i = 1; i < 100; ++i) { - BOOST_CHECK(CreateBlock(pblocktemplate, true)); + BOOST_CHECK(CreateBlock(pblocktemplate, insert_nonces[block_counter++])); } delete pblocktemplate; @@ -698,7 +763,7 @@ BOOST_AUTO_TEST_CASE(ncctrie_insert_update_claim) BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey)); pblocktemplate->block.hashPrevBlock = chainActive.Tip()->GetBlockHash(); - BOOST_CHECK(CreateBlock(pblocktemplate)); + BOOST_CHECK(CreateBlock(pblocktemplate, insert_nonces[block_counter++])); delete pblocktemplate; BOOST_CHECK(!pnccTrie->empty()); @@ -720,7 +785,7 @@ BOOST_AUTO_TEST_CASE(ncctrie_insert_update_claim) pblocktemplate->block.hashPrevBlock = chainActive.Tip()->GetBlockHash(); for (unsigned int i = 0; i < 50; ++i) { - BOOST_CHECK(CreateBlock(pblocktemplate)); + BOOST_CHECK(CreateBlock(pblocktemplate, insert_nonces[block_counter++])); } delete pblocktemplate; @@ -729,7 +794,7 @@ BOOST_AUTO_TEST_CASE(ncctrie_insert_update_claim) BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey)); pblocktemplate->block.hashPrevBlock = chainActive.Tip()->GetBlockHash(); - BOOST_CHECK(CreateBlock(pblocktemplate)); + BOOST_CHECK(CreateBlock(pblocktemplate, insert_nonces[block_counter++])); delete pblocktemplate; BOOST_CHECK(!pnccTrie->empty()); @@ -744,7 +809,7 @@ BOOST_AUTO_TEST_CASE(ncctrie_insert_update_claim) BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey)); BOOST_CHECK(pblocktemplate->block.vtx.size() == 2); pblocktemplate->block.hashPrevBlock = chainActive.Tip()->GetBlockHash(); - BOOST_CHECK(CreateBlock(pblocktemplate)); + BOOST_CHECK(CreateBlock(pblocktemplate, insert_nonces[block_counter++])); delete pblocktemplate; BOOST_CHECK(!pnccTrie->empty()); @@ -759,7 +824,7 @@ BOOST_AUTO_TEST_CASE(ncctrie_insert_update_claim) BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey)); BOOST_CHECK(pblocktemplate->block.vtx.size() == 2); pblocktemplate->block.hashPrevBlock = chainActive.Tip()->GetBlockHash(); - BOOST_CHECK(CreateBlock(pblocktemplate)); + BOOST_CHECK(CreateBlock(pblocktemplate, insert_nonces[block_counter++])); blocks_to_invalidate.push_back(pblocktemplate->block.hashPrevBlock); delete pblocktemplate; @@ -783,6 +848,279 @@ BOOST_AUTO_TEST_CASE(ncctrie_insert_update_claim) BOOST_CHECK(pnccTrie->queueEmpty()); } +BOOST_AUTO_TEST_CASE(ncctrie_claim_expiration) +{ + int block_counter = 0; + BOOST_CHECK(pnccTrie->nCurrentHeight == chainActive.Height() + 1); + + CBlockTemplate *pblocktemplate; + LOCK(cs_main); + + CScript scriptPubKey = CScript() << OP_TRUE; + + std::string sName("atest"); + std::string sValue("testa"); + + std::vector vchName(sName.begin(), sName.end()); + std::vector vchValue(sValue.begin(), sValue.end()); + + BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey)); + pblocktemplate->block.hashPrevBlock = chainActive.Tip()->GetBlockHash(); + std::vector coinbases; + for (unsigned int i = 0; i < 102; ++i) + { + BOOST_CHECK(CreateBlock(pblocktemplate, expire_nonces[block_counter++])); + if (coinbases.size() < 2) + coinbases.push_back(CTransaction(pblocktemplate->block.vtx[0])); + } + + delete pblocktemplate; + + CMutableTransaction tx1 = BuildTransaction(coinbases[0]); + tx1.vout[0].scriptPubKey = CScript() << OP_CLAIM_NAME << vchName << vchValue << OP_2DROP << OP_DROP << OP_TRUE; + CMutableTransaction tx2 = BuildTransaction(tx1); + tx2.vout[0].scriptPubKey = CScript() << OP_TRUE; + + std::vector blocks_to_invalidate; + // set expiration time to 100 blocks after the block becomes valid. + + pnccTrie->setExpirationTime(200); + + // create a claim. verify no expiration event has been scheduled. + + AddToMempool(tx1); + + BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey)); + BOOST_CHECK(pblocktemplate->block.vtx.size() == 2); + pblocktemplate->block.hashPrevBlock = chainActive.Tip()->GetBlockHash(); + BOOST_CHECK(CreateBlock(pblocktemplate, expire_nonces[block_counter++])); + blocks_to_invalidate.push_back(pblocktemplate->block.hashPrevBlock); + delete pblocktemplate; + + BOOST_CHECK(pnccTrie->empty()); + BOOST_CHECK(!pnccTrie->queueEmpty()); + BOOST_CHECK(!pnccTrie->expirationQueueEmpty()); + + // advance until the claim is valid. verify the expiration event is scheduled. + + BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey)); + pblocktemplate->block.hashPrevBlock = chainActive.Tip()->GetBlockHash(); + for (unsigned int i = 1; i < 100; ++i) + { + BOOST_CHECK(CreateBlock(pblocktemplate, expire_nonces[block_counter++])); + } + delete pblocktemplate; + + BOOST_CHECK(pnccTrie->empty()); + BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey)); + pblocktemplate->block.hashPrevBlock = chainActive.Tip()->GetBlockHash(); + BOOST_CHECK(CreateBlock(pblocktemplate, expire_nonces[block_counter++])); + blocks_to_invalidate.push_back(pblocktemplate->block.hashPrevBlock); + delete pblocktemplate; + + BOOST_CHECK(!pnccTrie->empty()); + BOOST_CHECK(pnccTrie->queueEmpty()); + BOOST_CHECK(!pnccTrie->expirationQueueEmpty()); + + // advance until the expiration event occurs. verify the expiration event occurs on time. + + BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey)); + pblocktemplate->block.hashPrevBlock = chainActive.Tip()->GetBlockHash(); + for (unsigned int i = 1; i < 100; ++i) + { + BOOST_CHECK(CreateBlock(pblocktemplate, expire_nonces[block_counter++])); + } + delete pblocktemplate; + + BOOST_CHECK(!pnccTrie->empty()); + BOOST_CHECK(pnccTrie->queueEmpty()); + BOOST_CHECK(!pnccTrie->expirationQueueEmpty()); + + BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey)); + pblocktemplate->block.hashPrevBlock = chainActive.Tip()->GetBlockHash(); + BOOST_CHECK(CreateBlock(pblocktemplate, expire_nonces[block_counter++])); + blocks_to_invalidate.push_back(pblocktemplate->block.hashPrevBlock); + delete pblocktemplate; + + BOOST_CHECK(pnccTrie->empty()); + BOOST_CHECK(pnccTrie->queueEmpty()); + BOOST_CHECK(pnccTrie->expirationQueueEmpty()); + + // roll forward a bit and then roll back to before the expiration event. verify the claim is reinserted. verify the expiration event is scheduled again. + + BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey)); + pblocktemplate->block.hashPrevBlock = chainActive.Tip()->GetBlockHash(); + for (unsigned int i = 0; i < 100; ++i) + { + BOOST_CHECK(CreateBlock(pblocktemplate, expire_nonces[block_counter++])); + } + delete pblocktemplate; + + BOOST_CHECK(pnccTrie->empty()); + BOOST_CHECK(pnccTrie->queueEmpty()); + BOOST_CHECK(pnccTrie->expirationQueueEmpty()); + + BOOST_CHECK(RemoveBlock(blocks_to_invalidate.back())); + blocks_to_invalidate.pop_back(); + BOOST_CHECK(!pnccTrie->empty()); + BOOST_CHECK(pnccTrie->queueEmpty()); + BOOST_CHECK(!pnccTrie->expirationQueueEmpty()); + + // advance until the expiration event occurs. verify the expiration event occurs on time. + + BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey)); + pblocktemplate->block.hashPrevBlock = chainActive.Tip()->GetBlockHash(); + BOOST_CHECK(CreateBlock(pblocktemplate, expire_nonces[block_counter++])); + blocks_to_invalidate.push_back(pblocktemplate->block.hashPrevBlock); + delete pblocktemplate; + + BOOST_CHECK(pnccTrie->empty()); + BOOST_CHECK(pnccTrie->queueEmpty()); + BOOST_CHECK(pnccTrie->expirationQueueEmpty()); + + // roll back to before the expiration event. verify the claim is reinserted. verify the expiration event is scheduled again. + + BOOST_CHECK(RemoveBlock(blocks_to_invalidate.back())); + blocks_to_invalidate.pop_back(); + BOOST_CHECK(!pnccTrie->empty()); + BOOST_CHECK(pnccTrie->queueEmpty()); + BOOST_CHECK(!pnccTrie->expirationQueueEmpty()); + + // roll back to before the claim is valid. verify the claim is removed but the expiration event still exists. + + BOOST_CHECK(RemoveBlock(blocks_to_invalidate.back())); + blocks_to_invalidate.pop_back(); + BOOST_CHECK(pnccTrie->empty()); + BOOST_CHECK(!pnccTrie->queueEmpty()); + BOOST_CHECK(!pnccTrie->expirationQueueEmpty()); + + // advance until the claim is valid again. verify the expiration event is scheduled. + + BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey)); + pblocktemplate->block.hashPrevBlock = chainActive.Tip()->GetBlockHash(); + BOOST_CHECK(CreateBlock(pblocktemplate, expire_nonces[block_counter++])); + blocks_to_invalidate.push_back(pblocktemplate->block.hashPrevBlock); + delete pblocktemplate; + + BOOST_CHECK(!pnccTrie->empty()); + BOOST_CHECK(pnccTrie->queueEmpty()); + BOOST_CHECK(!pnccTrie->expirationQueueEmpty()); + + // advance until the expiration event occurs. verify the expiration event occurs on time. + + BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey)); + pblocktemplate->block.hashPrevBlock = chainActive.Tip()->GetBlockHash(); + for (unsigned int i = 1; i < 100; ++i) + { + BOOST_CHECK(CreateBlock(pblocktemplate, expire_nonces[block_counter++])); + if (i == 50) + blocks_to_invalidate.push_back(pblocktemplate->block.hashPrevBlock); + } + delete pblocktemplate; + + BOOST_CHECK(!pnccTrie->empty()); + BOOST_CHECK(pnccTrie->queueEmpty()); + BOOST_CHECK(!pnccTrie->expirationQueueEmpty()); + + BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey)); + pblocktemplate->block.hashPrevBlock = chainActive.Tip()->GetBlockHash(); + BOOST_CHECK(CreateBlock(pblocktemplate, expire_nonces[block_counter++])); + blocks_to_invalidate.push_back(pblocktemplate->block.hashPrevBlock); + delete pblocktemplate; + + BOOST_CHECK(pnccTrie->empty()); + BOOST_CHECK(pnccTrie->queueEmpty()); + BOOST_CHECK(pnccTrie->expirationQueueEmpty()); + + // roll back to before the expiration event. verify the expiration event is scheduled. + + BOOST_CHECK(RemoveBlock(blocks_to_invalidate.back())); + blocks_to_invalidate.pop_back(); + BOOST_CHECK(!pnccTrie->empty()); + BOOST_CHECK(pnccTrie->queueEmpty()); + BOOST_CHECK(!pnccTrie->expirationQueueEmpty()); + + // roll back some. + + BOOST_CHECK(RemoveBlock(blocks_to_invalidate.back())); + blocks_to_invalidate.pop_back(); + BOOST_CHECK(!pnccTrie->empty()); + BOOST_CHECK(pnccTrie->queueEmpty()); + BOOST_CHECK(!pnccTrie->expirationQueueEmpty()); + + // spend the claim. verify the expiration event is removed. + + AddToMempool(tx2); + + BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey)); + BOOST_CHECK(pblocktemplate->block.vtx.size() == 2); + pblocktemplate->block.hashPrevBlock = chainActive.Tip()->GetBlockHash(); + BOOST_CHECK(CreateBlock(pblocktemplate, expire_nonces[block_counter++])); + blocks_to_invalidate.push_back(pblocktemplate->block.hashPrevBlock); + delete pblocktemplate; + + BOOST_CHECK(pnccTrie->empty()); + BOOST_CHECK(pnccTrie->queueEmpty()); + BOOST_CHECK(pnccTrie->expirationQueueEmpty()); + + // roll back the spend. verify the expiration event is returned. + + BOOST_CHECK(RemoveBlock(blocks_to_invalidate.back())); + blocks_to_invalidate.pop_back(); + BOOST_CHECK(!pnccTrie->empty()); + BOOST_CHECK(pnccTrie->queueEmpty()); + BOOST_CHECK(!pnccTrie->expirationQueueEmpty()); + + // advance until the expiration event occurs. verify the event occurs on time. + + BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey)); + pblocktemplate->block.hashPrevBlock = chainActive.Tip()->GetBlockHash(); + for (unsigned int i = 50; i < 100; ++i) + { + BOOST_CHECK(CreateBlock(pblocktemplate, expire_nonces[block_counter++])); + } + delete pblocktemplate; + + BOOST_CHECK(!pnccTrie->empty()); + BOOST_CHECK(pnccTrie->queueEmpty()); + BOOST_CHECK(!pnccTrie->expirationQueueEmpty()); + + BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey)); + pblocktemplate->block.hashPrevBlock = chainActive.Tip()->GetBlockHash(); + BOOST_CHECK(CreateBlock(pblocktemplate, expire_nonces[block_counter++])); + blocks_to_invalidate.push_back(pblocktemplate->block.hashPrevBlock); + delete pblocktemplate; + + BOOST_CHECK(pnccTrie->empty()); + BOOST_CHECK(pnccTrie->queueEmpty()); + BOOST_CHECK(pnccTrie->expirationQueueEmpty()); + + // roll back to before the expiration event. verify the claim is reinserted. verify the expiration event is scheduled again. + + BOOST_CHECK(RemoveBlock(blocks_to_invalidate.back())); + blocks_to_invalidate.pop_back(); + BOOST_CHECK(!pnccTrie->empty()); + BOOST_CHECK(pnccTrie->queueEmpty()); + BOOST_CHECK(!pnccTrie->expirationQueueEmpty()); + + // roll back to before the claim is valid. verify the claim is removed but the expiration event is not. + + BOOST_CHECK(RemoveBlock(blocks_to_invalidate.back())); + blocks_to_invalidate.pop_back(); + BOOST_CHECK(pnccTrie->empty()); + BOOST_CHECK(!pnccTrie->queueEmpty()); + BOOST_CHECK(!pnccTrie->expirationQueueEmpty()); + + // roll all the way back + + BOOST_CHECK(RemoveBlock(blocks_to_invalidate.back())); + blocks_to_invalidate.pop_back(); + BOOST_CHECK(pnccTrie->empty()); + BOOST_CHECK(pnccTrie->queueEmpty()); + BOOST_CHECK(pnccTrie->expirationQueueEmpty()); + BOOST_CHECK(blocks_to_invalidate.empty()); +} + BOOST_AUTO_TEST_CASE(ncctrienode_serialize_unserialize) { CDataStream ss(SER_DISK, 0); diff --git a/src/undo.h b/src/undo.h index c8324537d..884107645 100644 --- a/src/undo.h +++ b/src/undo.h @@ -80,14 +80,16 @@ class CBlockUndo { public: std::vector vtxundo; // for all but the coinbase - CNCCTrieQueueUndo queueUndo; // any claims that went from the queue to the trie + CNCCTrieQueueUndo insertUndo; // any claims that went from the queue to the trie + CNCCTrieQueueUndo expireUndo; // any claims that expired ADD_SERIALIZE_METHODS; template inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { READWRITE(vtxundo); - READWRITE(queueUndo); + READWRITE(insertUndo); + READWRITE(expireUndo); } }; diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 80b1f7aca..d0c3ba139 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -665,7 +665,19 @@ void ListNameClaims(const CWalletTx& wtx, const string& strAccount, int nMinDept { CBlockIndex* pindex = it->second; if (pindex) + { entry.push_back(Pair("height", pindex->nHeight)); + entry.push_back(Pair("expiration height", pindex->nHeight + pnccTrie->nExpirationTime)); + if (pindex->nHeight + pnccTrie->nExpirationTime > chainActive.Height()) + { + entry.push_back(Pair("expired", false)); + entry.push_back(Pair("blocks to expiration", pindex->nHeight + pnccTrie->nExpirationTime - chainActive.Height())); + } + else + { + entry.push_back(Pair("expired", true)); + } + } } entry.push_back(Pair("confirmations", wtx.GetDepthInMainChain())); entry.push_back(Pair("is spent", pwalletMain->IsSpent(wtx.GetHash(), s.vout)));