Rename CClaimTrieCache to CClaimTrieUpdateBuffer #156

Closed
kaykurokawa wants to merge 276 commits from cclaimtriecache_change 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; nRejectBlockOutdatedMajority = 950;
nToCheckBlockUpgradeMajority = 1000; nToCheckBlockUpgradeMajority = 1000;
nMinerThreads = 2; nMinerThreads = 2;
nTargetTimespan = 30 * 60 * 10;//14 * 24 * 60 * 60; // two weeks nTargetTimespan = 30 * 60 * 12;//14 * 24 * 60 * 60; // two weeks
nTargetSpacing = 30; nTargetSpacing = 30;
/** /**

View file

@ -1879,8 +1879,25 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
return false; return false;
control.Add(vChecks); control.Add(vChecks);
BOOST_FOREACH(const CTxIn& txin, tx.vin) // 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;
for (unsigned int i = 0; i < tx.vin.size(); ++i)
{ {
const CTxIn& txin = tx.vin[i];
const CCoins* coins = view.AccessCoins(txin.prevout.hash); const CCoins* coins = view.AccessCoins(txin.prevout.hash);
assert(coins); 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); 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)) if (!trieCache.spendClaim(name, txin.prevout.hash, txin.prevout.n, coins->nHeight, nValidAtHeight))
LogPrintf("%s: Something went wrong removing the name\n", __func__); LogPrintf("%s: Something went wrong removing the name\n", __func__);
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); assert(vvchParams.size() == 2);
std::string name(vvchParams[0].begin(), vvchParams[0].end()); std::string name(vvchParams[0].begin(), vvchParams[0].end());
LogPrintf("%s: Inserting %s into the ncc trie. Tx: %s, nOut: %d\n", __func__, name, tx.GetHash().GetHex(), i); LogPrintf("%s: Inserting %s into the ncc trie. Tx: %s, nOut: %d\n", __func__, name, tx.GetHash().GetHex(), i);
if (!trieCache.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__); 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)) if (!CheckInputs(tx, state, view, true, MANDATORY_SCRIPT_VERIFY_FLAGS, true))
continue; continue;
typedef std::map<std::string, std::pair<uint256, unsigned int> > spentClaimsType;
spentClaimsType spentClaims;
BOOST_FOREACH(const CTxIn& txin, tx.vin) BOOST_FOREACH(const CTxIn& txin, tx.vin)
{ {
const CCoins* coins = view.AccessCoins(txin.prevout.hash); const CCoins* coins = view.AccessCoins(txin.prevout.hash);
@ -303,6 +306,9 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn)
int throwaway; int throwaway;
if (!trieCache.spendClaim(name, txin.prevout.hash, txin.prevout.n, coins->nHeight, throwaway)) if (!trieCache.spendClaim(name, txin.prevout.hash, txin.prevout.n, coins->nHeight, throwaway))
LogPrintf("%s: Something went wrong removing the name\n", __func__); LogPrintf("%s: Something went wrong removing the name\n", __func__);
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); assert(vvchParams.size() == 2);
std::string name(vvchParams[0].begin(), vvchParams[0].end()); 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__); 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->nBits = GetNextWorkRequired(pindexPrev, pblock);
pblock->nNonce = 0; pblock->nNonce = 0;
pblocktemplate->vTxSigOps[0] = GetLegacySigOpCount(pblock->vtx[0]); pblocktemplate->vTxSigOps[0] = GetLegacySigOpCount(pblock->vtx[0]);
CNCCTrieQueueUndo dummyundo;
trieCache.incrementBlock(dummyundo);
pblock->hashNCCTrie = trieCache.getMerkleHash(); pblock->hashNCCTrie = trieCache.getMerkleHash();
CValidationState state; CValidationState state;

View file

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

View file

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

View file

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

View file

@ -4,6 +4,7 @@
#include "main.h" #include "main.h"
#include "primitives/transaction.h" #include "primitives/transaction.h"
#include "miner.h"
#include "ncctrie.h" #include "ncctrie.h"
#include "coins.h" #include "coins.h"
#include "streams.h" #include "streams.h"
@ -31,18 +32,69 @@ CMutableTransaction BuildTransaction(const uint256& prevhash)
return tx; return tx;
} }
void AttachBlock(CNCCTrieCache& cache, std::vector<CNCCTrieQueueUndo>& block_undos) CMutableTransaction BuildTransaction(const CMutableTransaction& prev)
{ {
CNCCTrieQueueUndo undo; CMutableTransaction tx;
cache.incrementBlock(undo); tx.nVersion = 1;
block_undos.push_back(undo); 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(); return BuildTransaction(CMutableTransaction(prev));
cache.decrementBlock(undo); }
block_undos.pop_back();
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) 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) BOOST_AUTO_TEST_CASE(ncctrie_insert_update_claim)
{ {
std::vector<CNCCTrieQueueUndo> block_undos; BOOST_CHECK(pnccTrie->nCurrentHeight == chainActive.Height() + 1);
int start_block = pnccTrie->nCurrentHeight;
int current_block = start_block; 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")); 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); BOOST_CHECK(pnccTrie->getMerkleHash() == hash0);
CNCCTrieCache ntState(pnccTrie); 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;
tx1_height = current_block;
ntState.addClaim(std::string("atest"), tx1.GetHash(), 0, 50, tx1_height);
tx3_height = current_block;
ntState.addClaim(std::string("btest"), tx3.GetHash(), 0, 50, tx3_height);
AttachBlock(ntState, block_undos);
current_block++;
ntState.flush();
for (; current_block < start_block + 100; ++current_block)
{
CNCCTrieCache s(pnccTrie);
AttachBlock(s, block_undos);
s.flush();
}
CNodeValue v;
BOOST_CHECK(!pnccTrie->getInfoForName(std::string("atest"), v));
BOOST_CHECK(!pnccTrie->getInfoForName(std::string("btest"), v));
CNCCTrieCache ntState1(pnccTrie);
AttachBlock(ntState1, block_undos);
ntState1.flush();
current_block++;
CNCCTrieCache ntState2(pnccTrie);
BOOST_CHECK(pnccTrie->getInfoForName(std::string("atest"), v));
BOOST_CHECK(pnccTrie->getInfoForName(std::string("btest"), v));
BOOST_CHECK(ntState2.spendClaim(std::string("atest"), tx1.GetHash(), 0, tx1_height, tx1_undo_height));
BOOST_CHECK(ntState2.spendClaim(std::string("btest"), tx3.GetHash(), 0, tx3_height, tx3_undo_height));
tx2_height = current_block;
ntState2.addClaim(std::string("atest"), tx2.GetHash(), 0, 50, tx2_height, tx1.GetHash(), 0);
tx4_height = current_block;
ntState2.addClaim(std::string("btest"), tx4.GetHash(), 0, 50, tx4_height);
AttachBlock(ntState2, block_undos);
current_block++;
ntState2.flush();
BOOST_CHECK(pnccTrie->getInfoForName(std::string("atest"), v));
BOOST_CHECK(!pnccTrie->getInfoForName(std::string("btest"), v));
BOOST_CHECK(v.txhash == tx2.GetHash());
CNCCTrieCache ntState3(pnccTrie);
DetachBlock(ntState3, block_undos);
current_block--;
BOOST_CHECK(ntState3.undoAddClaim(std::string("atest"), tx2.GetHash(), 0, tx2_height));
BOOST_CHECK(ntState3.undoAddClaim(std::string("btest"), tx4.GetHash(), 0, tx4_height));
ntState3.undoSpendClaim(std::string("atest"), tx1.GetHash(), 0, 50, tx1_height, tx1_undo_height);
ntState3.undoSpendClaim(std::string("btest"), tx3.GetHash(), 0, 50, tx3_height, tx3_undo_height);
ntState3.flush();
for (; current_block > start_block; --current_block)
{
CNCCTrieCache s(pnccTrie);
DetachBlock(s, block_undos);
s.flush();
}
CNCCTrieCache ntState4(pnccTrie);
BOOST_CHECK(ntState4.undoAddClaim(std::string("atest"), tx1.GetHash(), 0, tx1_height));
BOOST_CHECK(ntState4.undoAddClaim(std::string("btest"), tx3.GetHash(), 0, tx3_height));
ntState4.flush();
BOOST_CHECK(pnccTrie->empty());
BOOST_CHECK(pnccTrie->getMerkleHash() == hash0);
BOOST_CHECK(pnccTrie->queueEmpty());
}
BOOST_AUTO_TEST_CASE(ncctrie_undo)
{
std::vector<CNCCTrieQueueUndo> block_undos;
int start_block = pnccTrie->nCurrentHeight;
int current_block = start_block;
CNodeValue val; CNodeValue val;
uint256 hash0(uint256S("0000000000000000000000000000000000000000000000000000000000000001")); std::vector<uint256> blocks_to_invalidate;
CMutableTransaction tx1 = BuildTransaction(hash0);
CMutableTransaction tx2 = BuildTransaction(tx1.GetHash());
int tx1_height, tx2_height;
int tx1_undo_height, tx2_undo_height;
// 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->getMerkleHash() == hash0);
BOOST_CHECK(pnccTrie->queueEmpty()); 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.
/* Test undoing a claim before the claim gets into the trie */ AddToMempool(tx1);
// make claim at start_block BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey));
tx1_height = current_block; BOOST_CHECK(pblocktemplate->block.vtx.size() == 2);
ntState.addClaim(name, tx1.GetHash(), 0, 50, tx1_height); pblocktemplate->block.hashPrevBlock = chainActive.Tip()->GetBlockHash();
AttachBlock(ntState, block_undos); BOOST_CHECK(CreateBlock(pblocktemplate, true));
current_block++; blocks_to_invalidate.push_back(pblocktemplate->block.hashPrevBlock);
ntState.flush(); 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
// 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()); BOOST_CHECK(!pnccTrie->queueEmpty());
// undo block and remove claim BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey));
DetachBlock(ntState, block_undos); pblocktemplate->block.hashPrevBlock = chainActive.Tip()->GetBlockHash();
current_block--; for (unsigned int i = 1; i < 100; ++i)
ntState.undoAddClaim(name, tx1.GetHash(), 0, tx1_height); {
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());
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()); BOOST_CHECK(pnccTrie->queueEmpty());
/* Test undoing a claim that has gotten into the trie */ BOOST_CHECK(RemoveBlock(blocks_to_invalidate.back()));
blocks_to_invalidate.pop_back();
BOOST_CHECK(pnccTrie->empty());
BOOST_CHECK(pnccTrie->queueEmpty());
mempool.clear();
// make claim at start_block // Test undoing a spend which involves a claim inserted into the queue by a previous block
tx1_height = current_block;
ntState.addClaim(name, tx1.GetHash(), 0, 50, tx1_height); // Put tx2 into the chain, and then advance a few blocks but not far enough for it to get into the trie
AttachBlock(ntState, block_undos);
current_block++; AddToMempool(tx2);
ntState.flush();
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(!pnccTrie->queueEmpty());
// move to block start_block + 101 BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey));
for (; current_block < start_block + 101; ++current_block) pblocktemplate->block.hashPrevBlock = chainActive.Tip()->GetBlockHash();
for (unsigned int i = 1; i < 50; ++i)
{ {
AttachBlock(ntState, block_undos); BOOST_CHECK(CreateBlock(pblocktemplate));
ntState.flush();
} }
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->empty());
BOOST_CHECK(pnccTrie->queueEmpty()); BOOST_CHECK(pnccTrie->queueEmpty());
// undo block and remove claim // undo updating the original claim (tx3 spends tx1)
for (; current_block > start_block; --current_block)
{
DetachBlock(ntState, block_undos);
ntState.flush();
}
ntState.undoAddClaim(name, tx1.GetHash(), 0, tx1_height);
ntState.flush();
BOOST_CHECK(pnccTrie->empty());
BOOST_CHECK(pnccTrie->queueEmpty());
/* Test undoing a spend which involves a claim just inserted into the queue */ BOOST_CHECK(RemoveBlock(blocks_to_invalidate.back()));
blocks_to_invalidate.pop_back();
// make and spend claim at start_block
tx1_height = current_block;
ntState.addClaim(name, tx1.GetHash(), 0, 50, tx1_height);
ntState.spendClaim(name, tx1.GetHash(), 0, tx1_height, tx1_undo_height);
AttachBlock(ntState, block_undos);
current_block++;
ntState.flush();
BOOST_CHECK(pnccTrie->empty());
BOOST_CHECK(pnccTrie->queueEmpty());
// roll back the block
DetachBlock(ntState, block_undos);
current_block--;
ntState.undoSpendClaim(name, tx1.GetHash(), 0, 50, tx1_height, tx1_undo_height);
ntState.undoAddClaim(name, tx1.GetHash(), 0, tx1_height);
ntState.flush();
BOOST_CHECK(pnccTrie->empty());
BOOST_CHECK(pnccTrie->queueEmpty());
/* Test undoing a spend which involves a claim inserted into the queue by a previous block*/
// make claim at block start_block
tx1_height = current_block;
ntState.addClaim(name, tx1.GetHash(), 0, 50, tx1_height);
AttachBlock(ntState, block_undos);
current_block++;
ntState.flush();
// spend the claim in block start_block + 50
for (; current_block < start_block + 50; ++current_block)
{
AttachBlock(ntState, block_undos);
ntState.flush();
}
BOOST_CHECK(pnccTrie->empty()); BOOST_CHECK(pnccTrie->empty());
BOOST_CHECK(!pnccTrie->queueEmpty()); BOOST_CHECK(!pnccTrie->queueEmpty());
ntState.spendClaim(name, tx1.GetHash(), 0, tx1_height, tx1_undo_height); mempool.clear();
AttachBlock(ntState, block_undos);
++current_block;
ntState.flush();
BOOST_CHECK(pnccTrie->empty());
BOOST_CHECK(pnccTrie->queueEmpty());
// attach block start_block + 100 and make sure nothing is inserted // Test undoing an spent update which updated the best claim to a name
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 // move forward until the original claim is inserted into the trie
for (; current_block > start_block + 50; --current_block)
BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey));
pblocktemplate->block.hashPrevBlock = chainActive.Tip()->GetBlockHash();
for (unsigned int i = 0; i < 50; ++i)
{ {
DetachBlock(ntState, block_undos); BOOST_CHECK(CreateBlock(pblocktemplate));
ntState.flush();
} }
ntState.undoSpendClaim(name, tx1.GetHash(), 0, 50, tx1_height, tx1_undo_height); delete pblocktemplate;
ntState.flush();
BOOST_CHECK(pnccTrie->empty()); BOOST_CHECK(pnccTrie->empty());
BOOST_CHECK(!pnccTrie->queueEmpty()); BOOST_CHECK(!pnccTrie->queueEmpty());
// make sure it still gets inserted at block start_block + 100 BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey));
for (; current_block < start_block + 100; ++current_block) pblocktemplate->block.hashPrevBlock = chainActive.Tip()->GetBlockHash();
{ BOOST_CHECK(CreateBlock(pblocktemplate));
AttachBlock(ntState, block_undos); delete pblocktemplate;
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->empty());
BOOST_CHECK(pnccTrie->queueEmpty()); BOOST_CHECK(pnccTrie->queueEmpty());
BOOST_CHECK(pnccTrie->getInfoForName(sName1, val));
BOOST_CHECK(val.txhash == tx1.GetHash());
/* Test undoing a spend which involves a claim in the trie */ // 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;
// 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->empty());
BOOST_CHECK(pnccTrie->queueEmpty()); BOOST_CHECK(pnccTrie->queueEmpty());
ntState.spendClaim(name, tx1.GetHash(), 0, tx1_height, tx1_undo_height); BOOST_CHECK(pnccTrie->getInfoForName(sName1, val));
AttachBlock(ntState, block_undos); BOOST_CHECK(val.txhash == tx3.GetHash());
++current_block;
ntState.flush(); // 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->empty());
BOOST_CHECK(pnccTrie->queueEmpty()); BOOST_CHECK(pnccTrie->queueEmpty());
// roll back the block // undo spending the update (undo tx6 spending tx3)
DetachBlock(ntState, block_undos);
--current_block; BOOST_CHECK(RemoveBlock(blocks_to_invalidate.back()));
ntState.undoSpendClaim(name, tx1.GetHash(), 0, 50, tx1_height, tx1_undo_height); blocks_to_invalidate.pop_back();
ntState.flush();
BOOST_CHECK(!pnccTrie->empty()); BOOST_CHECK(!pnccTrie->empty());
BOOST_CHECK(pnccTrie->queueEmpty()); BOOST_CHECK(pnccTrie->queueEmpty());
BOOST_CHECK(pnccTrie->getInfoForName(sName1, val));
BOOST_CHECK(val.txhash == tx3.GetHash());
/* Test undoing an spent update which updated the best claim to a name*/ // roll all the way back
// update the claim at block start_block + 150 BOOST_CHECK(RemoveBlock(blocks_to_invalidate.back()));
tx2_height = current_block; blocks_to_invalidate.pop_back();
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->empty());
BOOST_CHECK(pnccTrie->queueEmpty()); BOOST_CHECK(pnccTrie->queueEmpty());
// undo the spend
DetachBlock(ntState, block_undos);
--current_block;
ntState.undoSpendClaim(name, tx2.GetHash(), 0, 75, tx2_height, tx2_undo_height);
ntState.flush();
BOOST_CHECK(!pnccTrie->empty());
BOOST_CHECK(pnccTrie->queueEmpty());
/* Test undoing a spent update which updated a claim still in the queue */
// roll everything back to block start_block + 50
for (; current_block > start_block + 151; --current_block)
{
DetachBlock(ntState, block_undos);
ntState.flush();
}
DetachBlock(ntState, block_undos);
--current_block;
ntState.undoAddClaim(name, tx2.GetHash(), 0, tx2_height);
ntState.undoSpendClaim(name, tx1.GetHash(), 0, 50, tx1_height, tx1_undo_height);
ntState.flush();
for (; current_block > start_block + 50; --current_block)
{
DetachBlock(ntState, block_undos);
ntState.flush();
}
BOOST_CHECK(pnccTrie->empty());
BOOST_CHECK(!pnccTrie->queueEmpty());
// update the claim at block start_block + 50
tx2_height = current_block;
ntState.spendClaim(name, tx1.GetHash(), 0, tx1_height, tx1_undo_height);
ntState.addClaim(name, tx2.GetHash(), 0, 75, tx2_height, tx1.GetHash(), 0);
AttachBlock(ntState, block_undos);
++current_block;
ntState.flush();
BOOST_CHECK(pnccTrie->empty());
BOOST_CHECK(!pnccTrie->queueEmpty());
// check that it gets inserted at block start_block + 150
for (; current_block < start_block + 150; ++current_block)
{
AttachBlock(ntState, block_undos);
ntState.flush();
}
BOOST_CHECK(pnccTrie->empty());
BOOST_CHECK(!pnccTrie->queueEmpty());
AttachBlock(ntState, block_undos);
++current_block;
ntState.flush();
BOOST_CHECK(!pnccTrie->empty());
BOOST_CHECK(pnccTrie->queueEmpty());
// roll back to start_block
for (; current_block > start_block + 50; --current_block)
{
DetachBlock(ntState, block_undos);
ntState.flush();
}
ntState.undoAddClaim(name, tx2.GetHash(), 0, tx2_height);
ntState.undoSpendClaim(name, tx1.GetHash(), 0, 75, tx1_height, tx1_undo_height);
ntState.flush();
for (; current_block > start_block; --current_block)
{
DetachBlock(ntState, block_undos);
ntState.flush();
}
ntState.undoAddClaim(name, tx1.GetHash(), 0, tx1_height);
ntState.flush();
BOOST_CHECK(pnccTrie->empty());
BOOST_CHECK(pnccTrie->queueEmpty());
BOOST_CHECK(pnccTrie->nCurrentHeight == start_block);
} }
BOOST_AUTO_TEST_CASE(ncctrienode_serialize_unserialize) BOOST_AUTO_TEST_CASE(ncctrienode_serialize_unserialize)