Claim name returned is strange #172

Closed
mirgee wants to merge 286 commits from issue-119 into master
7 changed files with 582 additions and 332 deletions
Showing only changes of commit c8834c5551 - Show all commits

View file

@ -123,7 +123,7 @@ public:
nRejectBlockOutdatedMajority = 950;
nToCheckBlockUpgradeMajority = 1000;
nMinerThreads = 2;
nTargetTimespan = 30 * 60 * 10;//14 * 24 * 60 * 60; // two weeks
nTargetTimespan = 30 * 60 * 12;//14 * 24 * 60 * 60; // two weeks
nTargetSpacing = 30;
/**

View file

@ -1878,9 +1878,26 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
if (!CheckInputs(tx, state, view, fScriptChecks, flags, false, nScriptCheckThreads ? &vChecks : NULL))
return false;
control.Add(vChecks);
// To handle NCC updates, stick all NCC claims found in the inputs into a map of
// name: (txhash, nOut). When running through the outputs, if any NCC claim's
// name is found in the map, send the name's txhash and nOut to the trie cache,
// and then remove the name: (txhash, nOut) mapping from the map.
// If there are two or more NCC claims in the inputs with the same name, only
// use the first.
// TODO: before releasing, see if it's a better idea to make an explicit
// operation for updating, like OP_UPDATE_NAME, which directly references
// the claim to be updated. It would only be necessary to add a single extra
// parameter to the operation, the input number, to exactly specify which
// claim is being updated. Then here it would just need to be checked if that
// input number was already used by an earlier tx.vout in the transaction.
typedef std::map<std::string, std::pair<uint256, unsigned int> > spentClaimsType;
spentClaimsType spentClaims;
BOOST_FOREACH(const CTxIn& txin, tx.vin)
for (unsigned int i = 0; i < tx.vin.size(); ++i)
{
const CTxIn& txin = tx.vin[i];
const CCoins* coins = view.AccessCoins(txin.prevout.hash);
assert(coins);
@ -1894,6 +1911,10 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
LogPrintf("%s: Removing %s from the ncc 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))
LogPrintf("%s: Something went wrong removing the name\n", __func__);
mNCCUndoHeights[i] = nValidAtHeight;
std::pair<uint256, unsigned int> val(txin.prevout.hash, txin.prevout.n);
std::pair<std::string, std::pair<uint256, unsigned int> > entry(name, val);
spentClaims.insert(entry);
}
}
@ -1908,7 +1929,17 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
assert(vvchParams.size() == 2);
std::string name(vvchParams[0].begin(), vvchParams[0].end());
LogPrintf("%s: Inserting %s into the ncc trie. Tx: %s, nOut: %d\n", __func__, name, tx.GetHash().GetHex(), i);
if (!trieCache.addClaim(name, tx.GetHash(), i, txout.nValue, pindex->nHeight))
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__);
}
}

View file

@ -281,6 +281,9 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn)
if (!CheckInputs(tx, state, view, true, MANDATORY_SCRIPT_VERIFY_FLAGS, true))
continue;
typedef std::map<std::string, std::pair<uint256, unsigned int> > spentClaimsType;
spentClaimsType spentClaims;
BOOST_FOREACH(const CTxIn& txin, tx.vin)
{
const CCoins* coins = view.AccessCoins(txin.prevout.hash);
@ -303,6 +306,9 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn)
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<uint256, unsigned int> val(txin.prevout.hash, txin.prevout.n);
std::pair<std::string, std::pair<uint256, unsigned int> >entry(name, val);
spentClaims.insert(entry);
}
}
@ -318,7 +324,16 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn)
{
assert(vvchParams.size() == 2);
std::string name(vvchParams[0].begin(), vvchParams[0].end());
if (!trieCache.addClaim(name, tx.GetHash(), i, txout.nValue, nHeight))
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__);
}
}
@ -372,6 +387,8 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn)
pblock->nBits = GetNextWorkRequired(pindexPrev, pblock);
pblock->nNonce = 0;
pblocktemplate->vTxSigOps[0] = GetLegacySigOpCount(pblock->vtx[0]);
CNCCTrieQueueUndo dummyundo;
trieCache.incrementBlock(dummyundo);
pblock->hashNCCTrie = trieCache.getMerkleHash();
CValidationState state;

View file

@ -417,7 +417,8 @@ bool CNCCTrie::ReadFromDisk(bool check)
{
if (!db.Read('h', hashBlock))
LogPrintf("%s: Couldn't read the best block's hash\n", __func__);
if (!db.Read('t', nCurrentHeight))
LogPrintf("%s: Couldn't read the current height\n", __func__);
boost::scoped_ptr<leveldb::Iterator> pcursor(const_cast<CLevelDBWrapper*>(&db)->NewIterator());
pcursor->SeekToFirst();
@ -815,24 +816,30 @@ bool CNCCTrieCache::getInfoForName(const std::string name, CNodeValue& val) cons
bool CNCCTrieCache::addClaim(const std::string name, uint256 txhash, uint32_t nOut, CAmount nAmount, int nHeight) const
{
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);
}
bool CNCCTrieCache::addClaim(const std::string name, uint256 txhash, uint32_t nOut, CAmount nAmount, int nHeight, uint256 prevTxhash, uint32_t nPrevOut) const
{
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);
CNodeValue val;
if (getInfoForName(name, val))
{
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 addClaim(name, txhash, nOut, nAmount, nHeight);
}
bool CNCCTrieCache::undoSpendClaim(const std::string name, uint256 txhash, uint32_t nOut, CAmount nAmount, int nHeight, int nValidAtHeight) const
{
LogPrintf("%s: name: %s, txhash: %s, nOut: %d, nAmount: %d, nHeight: %d, nValidAtHeight: %d, nCurrentHeight: %d\n", __func__, name, txhash.GetHex(), nOut, nAmount, nHeight, nValidAtHeight, nCurrentHeight);
if (nValidAtHeight < nCurrentHeight)
{
CNodeValue val(txhash, nOut, nAmount, nHeight, nValidAtHeight);
@ -848,6 +855,7 @@ bool CNCCTrieCache::undoSpendClaim(const std::string name, uint256 txhash, uint3
bool CNCCTrieCache::addClaimToQueue(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);
@ -893,6 +901,7 @@ 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);
if (nHeight + DEFAULT_DELAY >= nCurrentHeight)
{
if (removeClaimFromQueue(name, txhash, nOut, nHeight + DEFAULT_DELAY, nValidAtHeight))
@ -907,6 +916,7 @@ bool CNCCTrieCache::removeClaim(const std::string name, uint256 txhash, uint32_t
bool CNCCTrieCache::incrementBlock(CNCCTrieQueueUndo& undo) const
{
LogPrintf("%s: nCurrentHeight (before increment): %d\n", __func__, nCurrentHeight);
valueQueueType::iterator itQueueRow = getQueueCacheRow(nCurrentHeight, false);
nCurrentHeight++;
if (itQueueRow == valueQueueCache.end())
@ -924,6 +934,7 @@ bool CNCCTrieCache::incrementBlock(CNCCTrieQueueUndo& undo) const
bool CNCCTrieCache::decrementBlock(CNCCTrieQueueUndo& undo) const
{
LogPrintf("%s: nCurrentHeight (before decrement): %d\n", __func__, nCurrentHeight);
nCurrentHeight--;
valueQueueType::iterator itQueueRow = getQueueCacheRow(nCurrentHeight, true);
for (CNCCTrieQueueUndo::iterator itUndo = undo.begin(); itUndo != undo.end(); ++itUndo)
@ -966,7 +977,7 @@ bool CNCCTrieCache::flush()
{
if (dirty())
getMerkleHash();
bool success = base->update(cache, cacheHashes, hashBlock, valueQueueCache, nCurrentHeight);
bool success = base->update(cache, cacheHashes, getBestBlock(), valueQueueCache, nCurrentHeight);
if (success)
{
success = clear();

View file

@ -149,7 +149,7 @@ class CNCCTrieCache;
class CNCCTrie
{
public:
CNCCTrie() : db(GetDataDir() / "ncctrie", 100, false, false), root(uint256S("0000000000000000000000000000000000000000000000000000000000000001")) {}
CNCCTrie() : db(GetDataDir() / "ncctrie", 100, false, false), nCurrentHeight(1), root(uint256S("0000000000000000000000000000000000000000000000000000000000000001")) {}
uint256 getMerkleHash();
CLevelDBWrapper db;
bool empty() const;
@ -207,7 +207,8 @@ private:
mutable std::set<std::string> dirtyHashes;
mutable hashMapType cacheHashes;
mutable valueQueueType valueQueueCache;
mutable int nCurrentHeight;
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 recursiveComputeMerkleHash(CNCCTrieNode* tnCurrent, std::string sPos) const;
bool recursivePruneName(CNCCTrieNode* tnCurrent, unsigned int nPos, std::string sName, bool* pfNullified = NULL) const;

View file

@ -246,17 +246,15 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
delete pblocktemplate;
mempool.clear();
/* TODO: fix this
// subsidy changing
int nHeight = chainActive.Height();
chainActive.Tip()->nHeight = 209999;
chainActive.Tip()->nHeight = 2099999;
BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey));
delete pblocktemplate;
chainActive.Tip()->nHeight = 210000;
chainActive.Tip()->nHeight = 2100000;
BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey));
delete pblocktemplate;
chainActive.Tip()->nHeight = nHeight;
*/
// non-final txs in mempool
SetMockTime(chainActive.Tip()->GetMedianTimePast()+1);

View file

@ -4,6 +4,7 @@
#include "main.h"
#include "primitives/transaction.h"
#include "miner.h"
#include "ncctrie.h"
#include "coins.h"
#include "streams.h"
@ -31,18 +32,69 @@ CMutableTransaction BuildTransaction(const uint256& prevhash)
return tx;
}
void AttachBlock(CNCCTrieCache& cache, std::vector<CNCCTrieQueueUndo>& block_undos)
CMutableTransaction BuildTransaction(const CMutableTransaction& prev)
{
CNCCTrieQueueUndo undo;
cache.incrementBlock(undo);
block_undos.push_back(undo);
CMutableTransaction tx;
tx.nVersion = 1;
tx.nLockTime = 0;
tx.vin.resize(1);
tx.vout.resize(1);
tx.vin[0].prevout.hash = prev.GetHash();
tx.vin[0].prevout.n = 0;
tx.vin[0].scriptSig = CScript();
tx.vin[0].nSequence = std::numeric_limits<unsigned int>::max();
tx.vout[0].scriptPubKey = CScript();
tx.vout[0].nValue = prev.vout[0].nValue;
return tx;
}
void DetachBlock(CNCCTrieCache& cache, std::vector<CNCCTrieQueueUndo>& block_undos)
CMutableTransaction BuildTransaction(const CTransaction& prev)
{
CNCCTrieQueueUndo& undo = block_undos.back();
cache.decrementBlock(undo);
block_undos.pop_back();
return BuildTransaction(CMutableTransaction(prev));
}
void AddToMempool(CMutableTransaction& tx)
{
mempool.addUnchecked(tx.GetHash(), CTxMemPoolEntry(tx, 0, GetTime(), 111.0, chainActive.Height()));
}
bool CreateBlock(CBlockTemplate* pblocktemplate, bool f = false)
{
static int unique_block_counter = 0;
CBlock* pblock = &pblocktemplate->block;
pblock->nVersion = 1;
pblock->nTime = chainActive.Tip()->GetMedianTimePast()+1;
CMutableTransaction txCoinbase(pblock->vtx[0]);
txCoinbase.vin[0].scriptSig = CScript() << CScriptNum(unique_block_counter++) << CScriptNum(chainActive.Height());
txCoinbase.vout[0].nValue = GetBlockValue(chainActive.Height(), 0);
pblock->vtx[0] = CTransaction(txCoinbase);
pblock->hashMerkleRoot = pblock->BuildMerkleTree();
for (int i = 0; ; ++i)
{
pblock->nNonce = i;
if (CheckProofOfWork(pblock->GetHash(), pblock->nBits))
break;
}
CValidationState state;
bool success = (ProcessNewBlock(state, NULL, pblock) && state.IsValid() && pblock->GetHash() == chainActive.Tip()->GetBlockHash());
pblock->hashPrevBlock = pblock->GetHash();
return success;
}
bool RemoveBlock(uint256& blockhash)
{
CValidationState state;
if (mapBlockIndex.count(blockhash) == 0)
return false;
CBlockIndex* pblockindex = mapBlockIndex[blockhash];
InvalidateBlock(state, pblockindex);
if (state.IsValid())
{
ActivateBestChain(state);
}
return state.IsValid();
}
BOOST_AUTO_TEST_CASE(ncctrie_merkle_hash)
@ -160,357 +212,497 @@ BOOST_AUTO_TEST_CASE(ncctrie_merkle_hash)
BOOST_AUTO_TEST_CASE(ncctrie_insert_update_claim)
{
std::vector<CNCCTrieQueueUndo> block_undos;
int start_block = pnccTrie->nCurrentHeight;
int current_block = start_block;
BOOST_CHECK(pnccTrie->nCurrentHeight == chainActive.Height() + 1);
CBlockTemplate *pblocktemplate;
LOCK(cs_main);
Checkpoints::fEnabled = false;
CScript scriptPubKey = CScript() << OP_TRUE;
std::string sName1("atest");
std::string sName2("btest");
std::string sValue1("testa");
std::string sValue2("testb");
std::vector<unsigned char> vchName1(sName1.begin(), sName1.end());
std::vector<unsigned char> vchName2(sName2.begin(), sName2.end());
std::vector<unsigned char> vchValue1(sValue1.begin(), sValue1.end());
std::vector<unsigned char> vchValue2(sValue2.begin(), sValue2.end());
BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey));
pblocktemplate->block.hashPrevBlock = chainActive.Tip()->GetBlockHash();
std::vector<CTransaction> coinbases;
for (unsigned int i = 0; i < 103; ++i)
{
BOOST_CHECK(CreateBlock(pblocktemplate));
if (coinbases.size() < 3)
coinbases.push_back(CTransaction(pblocktemplate->block.vtx[0]));
}
delete pblocktemplate;
uint256 hash0(uint256S("0000000000000000000000000000000000000000000000000000000000000001"));
CMutableTransaction tx1 = BuildTransaction(hash0);
CMutableTransaction tx2 = BuildTransaction(tx1.GetHash());
CMutableTransaction tx3 = BuildTransaction(tx2.GetHash());
CMutableTransaction tx4 = BuildTransaction(tx3.GetHash());
int tx1_height, tx2_height, tx3_height, tx4_height, tx1_undo_height, tx3_undo_height;
BOOST_CHECK(pnccTrie->getMerkleHash() == hash0);
CNCCTrieCache ntState(pnccTrie);
tx1_height = current_block;
ntState.addClaim(std::string("atest"), tx1.GetHash(), 0, 50, tx1_height);
tx3_height = current_block;
ntState.addClaim(std::string("btest"), tx3.GetHash(), 0, 50, tx3_height);
AttachBlock(ntState, block_undos);
current_block++;
ntState.flush();
for (; current_block < start_block + 100; ++current_block)
{
CNCCTrieCache s(pnccTrie);
AttachBlock(s, block_undos);
s.flush();
}
CNodeValue v;
BOOST_CHECK(!pnccTrie->getInfoForName(std::string("atest"), v));
BOOST_CHECK(!pnccTrie->getInfoForName(std::string("btest"), v));
CNCCTrieCache ntState1(pnccTrie);
AttachBlock(ntState1, block_undos);
ntState1.flush();
current_block++;
CMutableTransaction tx1 = BuildTransaction(coinbases[0]);
tx1.vout[0].scriptPubKey = CScript() << OP_CLAIM_NAME << vchName1 << vchValue1 << OP_2DROP << OP_DROP << OP_TRUE;
CMutableTransaction tx2 = BuildTransaction(coinbases[1]);
tx2.vout[0].scriptPubKey = CScript() << OP_CLAIM_NAME << vchName2 << vchValue2 << OP_2DROP << OP_DROP << OP_TRUE;
CMutableTransaction tx3 = BuildTransaction(tx1);
tx3.vout[0].scriptPubKey = CScript() << OP_CLAIM_NAME << vchName1 << vchValue1 << OP_2DROP << OP_DROP << OP_TRUE;
CMutableTransaction tx4 = BuildTransaction(tx2);
tx4.vout[0].scriptPubKey = CScript() << OP_TRUE;
CMutableTransaction tx5 = BuildTransaction(coinbases[2]);
tx5.vout[0].scriptPubKey = CScript() << OP_CLAIM_NAME << vchName2 << vchValue2 << OP_2DROP << OP_DROP << OP_TRUE;
CMutableTransaction tx6 = BuildTransaction(tx3);
tx6.vout[0].scriptPubKey = CScript() << OP_TRUE;
CNCCTrieCache ntState2(pnccTrie);
BOOST_CHECK(pnccTrie->getInfoForName(std::string("atest"), v));
BOOST_CHECK(pnccTrie->getInfoForName(std::string("btest"), v));
BOOST_CHECK(ntState2.spendClaim(std::string("atest"), tx1.GetHash(), 0, tx1_height, tx1_undo_height));
BOOST_CHECK(ntState2.spendClaim(std::string("btest"), tx3.GetHash(), 0, tx3_height, tx3_undo_height));
tx2_height = current_block;
ntState2.addClaim(std::string("atest"), tx2.GetHash(), 0, 50, tx2_height, tx1.GetHash(), 0);
tx4_height = current_block;
ntState2.addClaim(std::string("btest"), tx4.GetHash(), 0, 50, tx4_height);
AttachBlock(ntState2, block_undos);
current_block++;
ntState2.flush();
BOOST_CHECK(pnccTrie->getInfoForName(std::string("atest"), v));
BOOST_CHECK(!pnccTrie->getInfoForName(std::string("btest"), v));
BOOST_CHECK(v.txhash == tx2.GetHash());
CNCCTrieCache ntState3(pnccTrie);
DetachBlock(ntState3, block_undos);
current_block--;
BOOST_CHECK(ntState3.undoAddClaim(std::string("atest"), tx2.GetHash(), 0, tx2_height));
BOOST_CHECK(ntState3.undoAddClaim(std::string("btest"), tx4.GetHash(), 0, tx4_height));
ntState3.undoSpendClaim(std::string("atest"), tx1.GetHash(), 0, 50, tx1_height, tx1_undo_height);
ntState3.undoSpendClaim(std::string("btest"), tx3.GetHash(), 0, 50, tx3_height, tx3_undo_height);
ntState3.flush();
for (; current_block > start_block; --current_block)
{
CNCCTrieCache s(pnccTrie);
DetachBlock(s, block_undos);
s.flush();
}
CNCCTrieCache ntState4(pnccTrie);
BOOST_CHECK(ntState4.undoAddClaim(std::string("atest"), tx1.GetHash(), 0, tx1_height));
BOOST_CHECK(ntState4.undoAddClaim(std::string("btest"), tx3.GetHash(), 0, tx3_height));
ntState4.flush();
BOOST_CHECK(pnccTrie->empty());
BOOST_CHECK(pnccTrie->getMerkleHash() == hash0);
BOOST_CHECK(pnccTrie->queueEmpty());
}
BOOST_AUTO_TEST_CASE(ncctrie_undo)
{
std::vector<CNCCTrieQueueUndo> block_undos;
int start_block = pnccTrie->nCurrentHeight;
int current_block = start_block;
CNodeValue val;
uint256 hash0(uint256S("0000000000000000000000000000000000000000000000000000000000000001"));
CMutableTransaction tx1 = BuildTransaction(hash0);
CMutableTransaction tx2 = BuildTransaction(tx1.GetHash());
int tx1_height, tx2_height;
int tx1_undo_height, tx2_undo_height;
std::vector<uint256> blocks_to_invalidate;
// Verify updates to the best claim get inserted immediately, and others don't.
// Put tx1 and tx2 into the blockchain, and then advance 100 blocks to put them in the trie
AddToMempool(tx1);
AddToMempool(tx2);
BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey));
BOOST_CHECK(pblocktemplate->block.vtx.size() == 3);
pblocktemplate->block.hashPrevBlock = chainActive.Tip()->GetBlockHash();
BOOST_CHECK(CreateBlock(pblocktemplate));
blocks_to_invalidate.push_back(pblocktemplate->block.hashPrevBlock);
delete pblocktemplate;
BOOST_CHECK(pcoinsTip->HaveCoins(tx1.GetHash()));
BOOST_CHECK(pcoinsTip->HaveCoins(tx2.GetHash()));
BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey));
pblocktemplate->block.hashPrevBlock = chainActive.Tip()->GetBlockHash();
for (unsigned int i = 1; i < 100; ++i)
{
BOOST_CHECK(CreateBlock(pblocktemplate));
}
delete pblocktemplate;
BOOST_CHECK(!pnccTrie->getInfoForName(sName1, val));
BOOST_CHECK(!pnccTrie->getInfoForName(sName2, val));
BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey));
pblocktemplate->block.hashPrevBlock = chainActive.Tip()->GetBlockHash();
BOOST_CHECK(CreateBlock(pblocktemplate));
delete pblocktemplate;
// Verify tx1 and tx2 are in the trie
BOOST_CHECK(pnccTrie->getInfoForName(sName1, val));
BOOST_CHECK(pnccTrie->getInfoForName(sName2, val));
// Spend tx1 with tx3, tx2 with tx4, and put in tx5.
AddToMempool(tx3);
AddToMempool(tx4);
AddToMempool(tx5);
BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey));
BOOST_CHECK(pblocktemplate->block.vtx.size() == 4);
pblocktemplate->block.hashPrevBlock = chainActive.Tip()->GetBlockHash();
BOOST_CHECK(CreateBlock(pblocktemplate));
blocks_to_invalidate.push_back(pblocktemplate->block.hashPrevBlock);
delete pblocktemplate;
// Verify tx1, tx2, and tx5 are not in the trie, but tx3 is in the trie.
BOOST_CHECK(pnccTrie->getInfoForName(sName1, val));
BOOST_CHECK(val.txhash == tx3.GetHash());
BOOST_CHECK(!pnccTrie->getInfoForName(sName2, val));
// Roll back the last block, make sure tx1 and tx2 are put back in the trie
BOOST_CHECK(RemoveBlock(blocks_to_invalidate.back()));
blocks_to_invalidate.pop_back();
BOOST_CHECK(pnccTrie->getInfoForName(sName1, val));
BOOST_CHECK(val.txhash == tx1.GetHash());
BOOST_CHECK(pnccTrie->getInfoForName(sName2, val));
BOOST_CHECK(val.txhash == tx2.GetHash());
// Roll all the way back, make sure all txs are out of the trie
BOOST_CHECK(RemoveBlock(blocks_to_invalidate.back()));
blocks_to_invalidate.pop_back();
BOOST_CHECK(!pnccTrie->getInfoForName(sName1, val));
BOOST_CHECK(!pnccTrie->getInfoForName(sName2, val));
mempool.clear();
BOOST_CHECK(pnccTrie->empty());
BOOST_CHECK(pnccTrie->getMerkleHash() == hash0);
BOOST_CHECK(pnccTrie->queueEmpty());
CNCCTrieCache ntState(pnccTrie);
// Test undoing a claim before the claim gets into the trie
std::string name("a");
// Put tx1 in the chain, and then undo that block.
AddToMempool(tx1);
/* Test undoing a claim before the claim gets into the trie */
// make claim at start_block
tx1_height = current_block;
ntState.addClaim(name, tx1.GetHash(), 0, 50, tx1_height);
AttachBlock(ntState, block_undos);
current_block++;
ntState.flush();
BOOST_CHECK(!pnccTrie->queueEmpty());
// undo block and remove claim
DetachBlock(ntState, block_undos);
current_block--;
ntState.undoAddClaim(name, tx1.GetHash(), 0, tx1_height);
ntState.flush();
BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey));
BOOST_CHECK(pblocktemplate->block.vtx.size() == 2);
pblocktemplate->block.hashPrevBlock = chainActive.Tip()->GetBlockHash();
BOOST_CHECK(CreateBlock(pblocktemplate, true));
blocks_to_invalidate.push_back(pblocktemplate->block.hashPrevBlock);
delete pblocktemplate;
BOOST_CHECK(RemoveBlock(blocks_to_invalidate.back()));
blocks_to_invalidate.pop_back();
// Make sure it's not in the queue
BOOST_CHECK(pnccTrie->empty());
BOOST_CHECK(pnccTrie->queueEmpty());
mempool.clear();
/* Test undoing a claim that has gotten into the trie */
// Test undoing a claim that has gotten into the trie
// make claim at start_block
tx1_height = current_block;
ntState.addClaim(name, tx1.GetHash(), 0, 50, tx1_height);
AttachBlock(ntState, block_undos);
current_block++;
ntState.flush();
// Put tx1 in the chain, and then advance until it's in the trie
AddToMempool(tx1);
BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey));
BOOST_CHECK(pblocktemplate->block.vtx.size() == 2);
pblocktemplate->block.hashPrevBlock = chainActive.Tip()->GetBlockHash();
BOOST_CHECK(CreateBlock(pblocktemplate));
blocks_to_invalidate.push_back(pblocktemplate->block.hashPrevBlock);
delete pblocktemplate;
BOOST_CHECK(pnccTrie->empty());
BOOST_CHECK(!pnccTrie->queueEmpty());
// move to block start_block + 101
for (; current_block < start_block + 101; ++current_block)
BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey));
pblocktemplate->block.hashPrevBlock = chainActive.Tip()->GetBlockHash();
for (unsigned int i = 1; i < 100; ++i)
{
AttachBlock(ntState, block_undos);
ntState.flush();
BOOST_CHECK(CreateBlock(pblocktemplate));
}
delete pblocktemplate;
BOOST_CHECK(pnccTrie->empty());
BOOST_CHECK(!pnccTrie->queueEmpty());
BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey));
pblocktemplate->block.hashPrevBlock = chainActive.Tip()->GetBlockHash();
BOOST_CHECK(CreateBlock(pblocktemplate));
delete pblocktemplate;
BOOST_CHECK(!pnccTrie->empty());
BOOST_CHECK(pnccTrie->queueEmpty());
// undo block and remove claim
for (; current_block > start_block; --current_block)
{
DetachBlock(ntState, block_undos);
ntState.flush();
}
ntState.undoAddClaim(name, tx1.GetHash(), 0, tx1_height);
ntState.flush();
BOOST_CHECK(pnccTrie->getInfoForName(sName1, val));
BOOST_CHECK(val.txhash == tx1.GetHash());
// Remove it from the trie
BOOST_CHECK(RemoveBlock(blocks_to_invalidate.back()));
blocks_to_invalidate.pop_back();
BOOST_CHECK(pnccTrie->empty());
BOOST_CHECK(pnccTrie->queueEmpty());
mempool.clear();
// Test undoing a spend which involves a claim just inserted into the queue
// Immediately spend tx2 with tx4, verify nothing gets put in the trie
AddToMempool(tx2);
AddToMempool(tx4);
BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey));
BOOST_CHECK(pblocktemplate->block.vtx.size() == 3);
pblocktemplate->block.hashPrevBlock = chainActive.Tip()->GetBlockHash();
BOOST_CHECK(CreateBlock(pblocktemplate));
blocks_to_invalidate.push_back(pblocktemplate->block.hashPrevBlock);
delete pblocktemplate;
BOOST_CHECK(pnccTrie->empty());
BOOST_CHECK(pnccTrie->queueEmpty());
/* Test undoing a spend which involves a claim just inserted into the queue */
// make and spend claim at start_block
tx1_height = current_block;
ntState.addClaim(name, tx1.GetHash(), 0, 50, tx1_height);
ntState.spendClaim(name, tx1.GetHash(), 0, tx1_height, tx1_undo_height);
AttachBlock(ntState, block_undos);
current_block++;
ntState.flush();
BOOST_CHECK(RemoveBlock(blocks_to_invalidate.back()));
blocks_to_invalidate.pop_back();
BOOST_CHECK(pnccTrie->empty());
BOOST_CHECK(pnccTrie->queueEmpty());
mempool.clear();
// roll back the block
DetachBlock(ntState, block_undos);
current_block--;
ntState.undoSpendClaim(name, tx1.GetHash(), 0, 50, tx1_height, tx1_undo_height);
ntState.undoAddClaim(name, tx1.GetHash(), 0, tx1_height);
ntState.flush();
BOOST_CHECK(pnccTrie->empty());
BOOST_CHECK(pnccTrie->queueEmpty());
// Test undoing a spend which involves a claim inserted into the queue by a previous block
// Put tx2 into the chain, and then advance a few blocks but not far enough for it to get into the trie
/* Test undoing a spend which involves a claim inserted into the queue by a previous block*/
// make claim at block start_block
tx1_height = current_block;
ntState.addClaim(name, tx1.GetHash(), 0, 50, tx1_height);
AttachBlock(ntState, block_undos);
current_block++;
ntState.flush();
// spend the claim in block start_block + 50
for (; current_block < start_block + 50; ++current_block)
{
AttachBlock(ntState, block_undos);
ntState.flush();
}
AddToMempool(tx2);
BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey));
BOOST_CHECK(pblocktemplate->block.vtx.size() == 2);
pblocktemplate->block.hashPrevBlock = chainActive.Tip()->GetBlockHash();
BOOST_CHECK(CreateBlock(pblocktemplate));
blocks_to_invalidate.push_back(pblocktemplate->block.hashPrevBlock);
delete pblocktemplate;
BOOST_CHECK(pnccTrie->empty());
BOOST_CHECK(!pnccTrie->queueEmpty());
ntState.spendClaim(name, tx1.GetHash(), 0, tx1_height, tx1_undo_height);
AttachBlock(ntState, block_undos);
++current_block;
ntState.flush();
BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey));
pblocktemplate->block.hashPrevBlock = chainActive.Tip()->GetBlockHash();
for (unsigned int i = 1; i < 50; ++i)
{
BOOST_CHECK(CreateBlock(pblocktemplate));
}
delete pblocktemplate;
BOOST_CHECK(pnccTrie->empty());
BOOST_CHECK(!pnccTrie->queueEmpty());
// Spend tx2 with tx4, and then advance to where tx2 would be inserted into the trie and verify it hasn't happened
AddToMempool(tx4);
BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey));
BOOST_CHECK(pblocktemplate->block.vtx.size() == 2);
pblocktemplate->block.hashPrevBlock = chainActive.Tip()->GetBlockHash();
BOOST_CHECK(CreateBlock(pblocktemplate));
blocks_to_invalidate.push_back(pblocktemplate->block.hashPrevBlock);
delete pblocktemplate;
BOOST_CHECK(pnccTrie->empty());
BOOST_CHECK(pnccTrie->queueEmpty());
BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey));
pblocktemplate->block.hashPrevBlock = chainActive.Tip()->GetBlockHash();
for (unsigned int i = 1; i < 51; ++i)
{
BOOST_CHECK(CreateBlock(pblocktemplate));
}
delete pblocktemplate;
BOOST_CHECK(pnccTrie->empty());
BOOST_CHECK(pnccTrie->queueEmpty());
// Undo spending tx2 with tx4, and then advance and verify tx2 is inserted into the trie when it should be
BOOST_CHECK(RemoveBlock(blocks_to_invalidate.back()));
blocks_to_invalidate.pop_back();
BOOST_CHECK(pnccTrie->empty());
BOOST_CHECK(!pnccTrie->queueEmpty());
mempool.clear();
BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey));
pblocktemplate->block.hashPrevBlock = chainActive.Tip()->GetBlockHash();
for (unsigned int i = 0; i < 50; ++i)
{
BOOST_CHECK(CreateBlock(pblocktemplate));
}
delete pblocktemplate;
BOOST_CHECK(pnccTrie->empty());
BOOST_CHECK(!pnccTrie->queueEmpty());
BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey));
pblocktemplate->block.hashPrevBlock = chainActive.Tip()->GetBlockHash();
BOOST_CHECK(CreateBlock(pblocktemplate));
delete pblocktemplate;
BOOST_CHECK(!pnccTrie->empty());
BOOST_CHECK(pnccTrie->queueEmpty());
BOOST_CHECK(pnccTrie->getInfoForName(sName2, val));
BOOST_CHECK(val.txhash == tx2.GetHash());
// Test undoing a spend which involves a claim in the trie
// spend tx2, which is in the trie, with tx4
AddToMempool(tx4);
BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey));
BOOST_CHECK(pblocktemplate->block.vtx.size() == 2);
pblocktemplate->block.hashPrevBlock = chainActive.Tip()->GetBlockHash();
BOOST_CHECK(CreateBlock(pblocktemplate));
blocks_to_invalidate.push_back(pblocktemplate->block.hashPrevBlock);
delete pblocktemplate;
BOOST_CHECK(pnccTrie->empty());
BOOST_CHECK(pnccTrie->queueEmpty());
// undo spending tx2 with tx4, and verify tx2 is back in the trie
BOOST_CHECK(RemoveBlock(blocks_to_invalidate.back()));
blocks_to_invalidate.pop_back();
BOOST_CHECK(!pnccTrie->empty());
BOOST_CHECK(pnccTrie->queueEmpty());
mempool.clear();
// roll back to the beginning
BOOST_CHECK(RemoveBlock(blocks_to_invalidate.back()));
blocks_to_invalidate.pop_back();
BOOST_CHECK(pnccTrie->empty());
BOOST_CHECK(pnccTrie->queueEmpty());
mempool.clear();
// Test undoing a spent update which updated a claim still in the queue
// Create the original claim (tx1)
AddToMempool(tx1);
BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey));
BOOST_CHECK(pblocktemplate->block.vtx.size() == 2);
pblocktemplate->block.hashPrevBlock = chainActive.Tip()->GetBlockHash();
BOOST_CHECK(CreateBlock(pblocktemplate, true));
blocks_to_invalidate.push_back(pblocktemplate->block.hashPrevBlock);
delete pblocktemplate;
BOOST_CHECK(pnccTrie->empty());
BOOST_CHECK(!pnccTrie->queueEmpty());
// move forward some, but not far enough for the claim to get into the trie
BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey));
pblocktemplate->block.hashPrevBlock = chainActive.Tip()->GetBlockHash();
for (unsigned int i = 1; i < 50; ++i)
{
BOOST_CHECK(CreateBlock(pblocktemplate));
}
delete pblocktemplate;
BOOST_CHECK(pnccTrie->empty());
BOOST_CHECK(!pnccTrie->queueEmpty());
// update the original claim (tx3 spends tx1)
AddToMempool(tx3);
BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey));
BOOST_CHECK(pblocktemplate->block.vtx.size() == 2);
pblocktemplate->block.hashPrevBlock = chainActive.Tip()->GetBlockHash();
BOOST_CHECK(CreateBlock(pblocktemplate));
blocks_to_invalidate.push_back(pblocktemplate->block.hashPrevBlock);
delete pblocktemplate;
BOOST_CHECK(pnccTrie->empty());
BOOST_CHECK(!pnccTrie->queueEmpty());
// spend the update (tx6 spends tx3)
AddToMempool(tx6);
BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey));
BOOST_CHECK(pblocktemplate->block.vtx.size() == 2);
pblocktemplate->block.hashPrevBlock = chainActive.Tip()->GetBlockHash();
BOOST_CHECK(CreateBlock(pblocktemplate));
blocks_to_invalidate.push_back(pblocktemplate->block.hashPrevBlock);
delete pblocktemplate;
BOOST_CHECK(pnccTrie->empty());
BOOST_CHECK(pnccTrie->queueEmpty());
// undo spending the update (undo tx6 spending tx3)
BOOST_CHECK(RemoveBlock(blocks_to_invalidate.back()));
blocks_to_invalidate.pop_back();
BOOST_CHECK(pnccTrie->empty());
BOOST_CHECK(!pnccTrie->queueEmpty());
mempool.clear();
// make sure the update (tx3) still goes into effect in 100 blocks
BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey));
pblocktemplate->block.hashPrevBlock = chainActive.Tip()->GetBlockHash();
for (unsigned int i = 1; i < 100; ++i)
{
BOOST_CHECK(CreateBlock(pblocktemplate, true));
}
delete pblocktemplate;
BOOST_CHECK(pnccTrie->empty());
BOOST_CHECK(!pnccTrie->queueEmpty());
BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey));
pblocktemplate->block.hashPrevBlock = chainActive.Tip()->GetBlockHash();
BOOST_CHECK(CreateBlock(pblocktemplate));
delete pblocktemplate;
BOOST_CHECK(!pnccTrie->empty());
BOOST_CHECK(pnccTrie->queueEmpty());
// undo updating the original claim (tx3 spends tx1)
BOOST_CHECK(RemoveBlock(blocks_to_invalidate.back()));
blocks_to_invalidate.pop_back();
BOOST_CHECK(pnccTrie->empty());
BOOST_CHECK(!pnccTrie->queueEmpty());
mempool.clear();
// Test undoing an spent update which updated the best claim to a name
// move forward until the original claim is inserted into the trie
BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey));
pblocktemplate->block.hashPrevBlock = chainActive.Tip()->GetBlockHash();
for (unsigned int i = 0; i < 50; ++i)
{
BOOST_CHECK(CreateBlock(pblocktemplate));
}
delete pblocktemplate;
BOOST_CHECK(pnccTrie->empty());
BOOST_CHECK(!pnccTrie->queueEmpty());
BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey));
pblocktemplate->block.hashPrevBlock = chainActive.Tip()->GetBlockHash();
BOOST_CHECK(CreateBlock(pblocktemplate));
delete pblocktemplate;
BOOST_CHECK(!pnccTrie->empty());
BOOST_CHECK(pnccTrie->queueEmpty());
BOOST_CHECK(pnccTrie->getInfoForName(sName1, val));
BOOST_CHECK(val.txhash == tx1.GetHash());
// update the original claim (tx3 spends tx1)
AddToMempool(tx3);
BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey));
BOOST_CHECK(pblocktemplate->block.vtx.size() == 2);
pblocktemplate->block.hashPrevBlock = chainActive.Tip()->GetBlockHash();
BOOST_CHECK(CreateBlock(pblocktemplate));
delete pblocktemplate;
BOOST_CHECK(!pnccTrie->empty());
BOOST_CHECK(pnccTrie->queueEmpty());
BOOST_CHECK(pnccTrie->getInfoForName(sName1, val));
BOOST_CHECK(val.txhash == tx3.GetHash());
// spend the update (tx6 spends tx3)
AddToMempool(tx6);
BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey));
BOOST_CHECK(pblocktemplate->block.vtx.size() == 2);
pblocktemplate->block.hashPrevBlock = chainActive.Tip()->GetBlockHash();
BOOST_CHECK(CreateBlock(pblocktemplate));
blocks_to_invalidate.push_back(pblocktemplate->block.hashPrevBlock);
delete pblocktemplate;
BOOST_CHECK(pnccTrie->empty());
BOOST_CHECK(pnccTrie->queueEmpty());
// attach block start_block + 100 and make sure nothing is inserted
for (; current_block < start_block + 101; ++current_block)
{
AttachBlock(ntState, block_undos);
ntState.flush();
}
BOOST_CHECK(pnccTrie->empty());
BOOST_CHECK(pnccTrie->queueEmpty());
// roll back to block start_block + 50 and undo the spend
for (; current_block > start_block + 50; --current_block)
{
DetachBlock(ntState, block_undos);
ntState.flush();
}
ntState.undoSpendClaim(name, tx1.GetHash(), 0, 50, tx1_height, tx1_undo_height);
ntState.flush();
BOOST_CHECK(pnccTrie->empty());
BOOST_CHECK(!pnccTrie->queueEmpty());
// make sure it still gets inserted at block start_block + 100
for (; current_block < start_block + 100; ++current_block)
{
AttachBlock(ntState, block_undos);
ntState.flush();
}
BOOST_CHECK(pnccTrie->empty());
BOOST_CHECK(!pnccTrie->queueEmpty());
AttachBlock(ntState, block_undos);
++current_block;
ntState.flush();
// undo spending the update (undo tx6 spending tx3)
BOOST_CHECK(RemoveBlock(blocks_to_invalidate.back()));
blocks_to_invalidate.pop_back();
BOOST_CHECK(!pnccTrie->empty());
BOOST_CHECK(pnccTrie->queueEmpty());
BOOST_CHECK(pnccTrie->getInfoForName(sName1, val));
BOOST_CHECK(val.txhash == tx3.GetHash());
/* Test undoing a spend which involves a claim in the trie */
// roll all the way back
// spend the claim at block start_block + 150
for (; current_block < start_block + 150; ++current_block)
{
AttachBlock(ntState, block_undos);
ntState.flush();
}
BOOST_CHECK(!pnccTrie->empty());
BOOST_CHECK(pnccTrie->queueEmpty());
ntState.spendClaim(name, tx1.GetHash(), 0, tx1_height, tx1_undo_height);
AttachBlock(ntState, block_undos);
++current_block;
ntState.flush();
BOOST_CHECK(RemoveBlock(blocks_to_invalidate.back()));
blocks_to_invalidate.pop_back();
BOOST_CHECK(pnccTrie->empty());
BOOST_CHECK(pnccTrie->queueEmpty());
// roll back the block
DetachBlock(ntState, block_undos);
--current_block;
ntState.undoSpendClaim(name, tx1.GetHash(), 0, 50, tx1_height, tx1_undo_height);
ntState.flush();
BOOST_CHECK(!pnccTrie->empty());
BOOST_CHECK(pnccTrie->queueEmpty());
/* Test undoing an spent update which updated the best claim to a name*/
// update the claim at block start_block + 150
tx2_height = current_block;
ntState.spendClaim(name, tx1.GetHash(), 0, tx1_height, tx1_undo_height);
ntState.addClaim(name, tx2.GetHash(), 0, 75, tx2_height, tx1.GetHash(), 0);
AttachBlock(ntState, block_undos);
++current_block;
ntState.flush();
BOOST_CHECK(!pnccTrie->empty());
BOOST_CHECK(pnccTrie->queueEmpty());
// move forward a bit
for (; current_block < start_block + 200; ++current_block)
{
AttachBlock(ntState, block_undos);
ntState.flush();
}
// spend the update
ntState.spendClaim(name, tx2.GetHash(), 0, tx2_height, tx2_undo_height);
AttachBlock(ntState, block_undos);
++current_block;
ntState.flush();
BOOST_CHECK(pnccTrie->empty());
BOOST_CHECK(pnccTrie->queueEmpty());
// undo the spend
DetachBlock(ntState, block_undos);
--current_block;
ntState.undoSpendClaim(name, tx2.GetHash(), 0, 75, tx2_height, tx2_undo_height);
ntState.flush();
BOOST_CHECK(!pnccTrie->empty());
BOOST_CHECK(pnccTrie->queueEmpty());
/* Test undoing a spent update which updated a claim still in the queue */
// roll everything back to block start_block + 50
for (; current_block > start_block + 151; --current_block)
{
DetachBlock(ntState, block_undos);
ntState.flush();
}
DetachBlock(ntState, block_undos);
--current_block;
ntState.undoAddClaim(name, tx2.GetHash(), 0, tx2_height);
ntState.undoSpendClaim(name, tx1.GetHash(), 0, 50, tx1_height, tx1_undo_height);
ntState.flush();
for (; current_block > start_block + 50; --current_block)
{
DetachBlock(ntState, block_undos);
ntState.flush();
}
BOOST_CHECK(pnccTrie->empty());
BOOST_CHECK(!pnccTrie->queueEmpty());
// update the claim at block start_block + 50
tx2_height = current_block;
ntState.spendClaim(name, tx1.GetHash(), 0, tx1_height, tx1_undo_height);
ntState.addClaim(name, tx2.GetHash(), 0, 75, tx2_height, tx1.GetHash(), 0);
AttachBlock(ntState, block_undos);
++current_block;
ntState.flush();
BOOST_CHECK(pnccTrie->empty());
BOOST_CHECK(!pnccTrie->queueEmpty());
// check that it gets inserted at block start_block + 150
for (; current_block < start_block + 150; ++current_block)
{
AttachBlock(ntState, block_undos);
ntState.flush();
}
BOOST_CHECK(pnccTrie->empty());
BOOST_CHECK(!pnccTrie->queueEmpty());
AttachBlock(ntState, block_undos);
++current_block;
ntState.flush();
BOOST_CHECK(!pnccTrie->empty());
BOOST_CHECK(pnccTrie->queueEmpty());
// roll back to start_block
for (; current_block > start_block + 50; --current_block)
{
DetachBlock(ntState, block_undos);
ntState.flush();
}
ntState.undoAddClaim(name, tx2.GetHash(), 0, tx2_height);
ntState.undoSpendClaim(name, tx1.GetHash(), 0, 75, tx1_height, tx1_undo_height);
ntState.flush();
for (; current_block > start_block; --current_block)
{
DetachBlock(ntState, block_undos);
ntState.flush();
}
ntState.undoAddClaim(name, tx1.GetHash(), 0, tx1_height);
ntState.flush();
BOOST_CHECK(pnccTrie->empty());
BOOST_CHECK(pnccTrie->queueEmpty());
BOOST_CHECK(pnccTrie->nCurrentHeight == start_block);
}
BOOST_AUTO_TEST_CASE(ncctrienode_serialize_unserialize)