implement reading and writing the NCC trie to disk, create the global trie upon initialization, add and remove NCC claims when connecting and disconnecting blocks, and check block headers against the NCC merkle hash

This commit is contained in:
Jimmy Kiselak 2015-02-05 14:24:09 -05:00
parent 9bb4ecef2a
commit b2b0bd5bb5
11 changed files with 268 additions and 74 deletions

View file

@ -55,7 +55,7 @@ static void convertSeed6(std::vector<CAddress> &vSeedsOut, const SeedSpec6 *data
static Checkpoints::MapCheckpoints mapCheckpoints =
boost::assign::map_list_of
( 0, uint256S("0x008c55867210a8154af697b7776c6b6cdb26972381eac9f84d18a491374464a3"));
( 0, uint256S("0x00ef6ded2b610fc5e4f06d187d12136bd5fba7b932fa0b66bf353c7c1648ec9c"));
/*static Checkpoints::MapCheckpoints mapCheckpoints =
boost::assign::map_list_of
( 11111, uint256("0x0000000069e244f73d78e8fd29ba2fd2ed618bd6fa2ee92559f542fdb26e7c1d"))
@ -148,10 +148,11 @@ public:
genesis.vtx.push_back(txNew);
genesis.hashPrevBlock.SetNull();
genesis.hashMerkleRoot = genesis.BuildMerkleTree();
genesis.hashNCCTrie = uint256S("0x0000000000000000000000000000000000000000000000000000000000000001");
genesis.nVersion = 1;
genesis.nTime = 1417453734;
genesis.nBits = 0x207fffff;//0x1d00ffff;
genesis.nNonce = 110;
genesis.nNonce = 601;
/*bool found = false;
while (!found)
@ -170,7 +171,7 @@ public:
hashGenesisBlock = genesis.GetHash();
//printf("%s\n", hashGenesisBlock.GetHex().c_str());
//assert(hashGenesisBlock == uint256("0x000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"));
assert(hashGenesisBlock == uint256S("0x008c55867210a8154af697b7776c6b6cdb26972381eac9f84d18a491374464a3"));
assert(hashGenesisBlock == uint256S("0x00ef6ded2b610fc5e4f06d187d12136bd5fba7b932fa0b66bf353c7c1648ec9c"));
//printf("%s\n", genesis.hashMerkleRoot.GetHex().c_str());
//assert(genesis.hashMerkleRoot == uint256("0x4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b"));
assert(genesis.hashMerkleRoot == uint256S("0xa7d51d407092059a2beeffab22e65d6176cfb3c33b93515109480aa7c81c9141"));

View file

@ -181,6 +181,8 @@ void Shutdown()
pcoinsdbview = NULL;
delete pblocktree;
pblocktree = NULL;
delete pnccTrie;
pnccTrie = NULL;
}
#ifdef ENABLE_WALLET
if (pwalletMain)
@ -1016,11 +1018,13 @@ bool AppInit2(boost::thread_group& threadGroup)
delete pcoinsdbview;
delete pcoinscatcher;
delete pblocktree;
delete pnccTrie;
pblocktree = new CBlockTreeDB(nBlockTreeDBCache, false, fReindex);
pcoinsdbview = new CCoinsViewDB(nCoinDBCache, false, fReindex);
pcoinscatcher = new CCoinsViewErrorCatcher(pcoinsdbview);
pcoinsTip = new CCoinsViewCache(pcoinscatcher);
pnccTrie = new CNCCTrie();
if (fReindex)
pblocktree->WriteReindexing(true);
@ -1053,6 +1057,11 @@ bool AppInit2(boost::thread_group& threadGroup)
strLoadError = _("Corrupted block database detected");
break;
}
if (!pnccTrie->ReadFromDisk(true))
{
strLoadError = _("Error loading the ncc trie from disk");
break;
}
} catch (const std::exception& e) {
if (fDebug) LogPrintf("%s\n", e.what());
strLoadError = _("Error opening block database");

View file

@ -13,6 +13,7 @@
#include "checkqueue.h"
#include "init.h"
#include "merkleblock.h"
#include "ncc.h"
#include "net.h"
#include "pow.h"
#include "txdb.h"
@ -538,6 +539,7 @@ CBlockIndex* FindForkInGlobalIndex(const CChain& chain, const CBlockLocator& loc
}
CCoinsViewCache *pcoinsTip = NULL;
CNCCTrie *pnccTrie = NULL;
CBlockTreeDB *pblocktree = NULL;
//////////////////////////////////////////////////////////////////////////////
@ -1590,7 +1592,7 @@ bool UndoReadFromDisk(CBlockUndo& blockundo, const CDiskBlockPos& pos, const uin
} // anon namespace
bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& view, bool* pfClean)
bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& view, CNCCTrieCache& trieCache, bool* pfClean)
{
assert(pindex->GetBlockHash() == view.GetBestBlock());
@ -1632,6 +1634,22 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex
if (*outs != outsBlock)
fClean = fClean && error("DisconnectBlock() : added transaction mismatch? database corrupted");
// remove any NCC claims
for (unsigned int i = 0; i < tx.vout.size(); ++i)
{
const CTxOut& txout = tx.vout[i];
int op;
std::vector<std::vector<unsigned char> > vvchParams;
if (DecodeNCCScript(txout.scriptPubKey, op, vvchParams))
{
assert(vvchParams.size() == 2);
std::string name(vvchParams[0].begin(), vvchParams[0].end());
if (!trieCache.removeName(name, hash, i))
LogPrintf("Something went wrong removing the name");
}
}
// remove outputs
outs->Clear();
}
@ -1661,6 +1679,18 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex
fClean = fClean && error("DisconnectBlock() : undo data overwriting existing output");
if (coins->vout.size() < out.n+1)
coins->vout.resize(out.n+1);
// restore NCC claim if applicable
int op;
std::vector<std::vector<unsigned char> > vvchParams;
if (DecodeNCCScript(undo.txout.scriptPubKey, op, vvchParams))
{
assert(vvchParams.size() == 2);
std::string name(vvchParams[0].begin(), vvchParams[0].end());
if (!trieCache.insertName(name, out.hash, out.n, undo.txout.nValue, undo.nHeight))
LogPrintf("Something went wrong inserting the name");
}
coins->vout[out.n] = undo.txout;
}
}
@ -1715,7 +1745,7 @@ static int64_t nTimeIndex = 0;
static int64_t nTimeCallbacks = 0;
static int64_t nTimeTotal = 0;
bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& view, bool fJustCheck)
bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& view, CNCCTrieCache& trieCache, bool fJustCheck)
{
AssertLockHeld(cs_main);
// Check it again in case a previous version let a bad block in
@ -1811,6 +1841,37 @@ 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);
BOOST_FOREACH(const CTxIn& txin, tx.vin)
{
const CCoins* coins = view.AccessCoins(txin.prevout.hash);
assert(coins);
int op;
std::vector<std::vector<unsigned char> > vvchParams;
if (DecodeNCCScript(coins->vout[txin.prevout.n].scriptPubKey, op, vvchParams))
{
assert(vvchParams.size() == 1);
std::string name(vvchParams[0].begin(), vvchParams[0].end());
if (!trieCache.removeName(name, txin.prevout.hash, txin.prevout.n))
LogPrintf("Something went wrong removing the name");
}
}
for (unsigned int i = 0; i < tx.vout.size(); ++i)
{
const CTxOut& txout = tx.vout[i];
int op;
std::vector<std::vector<unsigned char> > vvchParams;
if (DecodeNCCScript(txout.scriptPubKey, op, vvchParams))
{
assert(vvchParams.size() == 2);
std::string name(vvchParams[0].begin(), vvchParams[0].end());
if (!trieCache.insertName(name, tx.GetHash(), i, txout.nValue, pindex->nHeight))
LogPrintf("Something went wrong inserting the name");
}
}
}
CTxUndo undoDummy;
@ -1819,12 +1880,22 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
}
UpdateCoins(tx, state, view, i == 0 ? undoDummy : blockundo.vtxundo.back(), pindex->nHeight);
// TODO: This seems like a good place to actually change the
// TODO: tree of NCCs and make the CUndo vector for those changes.
// The CUndo vector contains all of the
// necessary information for putting NCC claims
// removed by this block back into the trie.
// (As long as the coinbase can't remove any
// NCC claims from the trie.)
vPos.push_back(std::make_pair(tx.GetHash(), pos));
pos.nTxOffset += ::GetSerializeSize(tx, SER_DISK, CLIENT_VERSION);
}
if (trieCache.getMerkleHash() != block.hashNCCTrie)
return state.DoS(100,
error("ConnectBlock() : the merkle root of the NCC trie does not match "
"(actual=%s vs block=%s)", trieCache.getMerkleHash().GetHex(),
block.hashNCCTrie.GetHex()), REJECT_INVALID, "bad-ncc-merkle-hash");
int64_t nTime1 = GetTimeMicros(); nTimeConnect += nTime1 - nTimeStart;
LogPrint("bench", " - Connect %u transactions: %.2fms (%.3fms/tx, %.3fms/txin) [%.2fs]\n", (unsigned)block.vtx.size(), 0.001 * (nTime1 - nTimeStart), 0.001 * (nTime1 - nTimeStart) / block.vtx.size(), nInputs <= 1 ? 0 : 0.001 * (nTime1 - nTimeStart) / (nInputs-1), nTimeConnect * 0.000001);
@ -1999,9 +2070,11 @@ bool static DisconnectTip(CValidationState &state) {
int64_t nStart = GetTimeMicros();
{
CCoinsViewCache view(pcoinsTip);
if (!DisconnectBlock(block, state, pindexDelete, view))
CNCCTrieCache trieCache(pnccTrie);
if (!DisconnectBlock(block, state, pindexDelete, view, trieCache))
return error("DisconnectTip() : DisconnectBlock %s failed", pindexDelete->GetBlockHash().ToString());
assert(view.Flush());
assert(trieCache.flush());
}
LogPrint("bench", "- Disconnect block: %.2fms\n", (GetTimeMicros() - nStart) * 0.001);
// Write the chain state to disk, if necessary.
@ -2054,8 +2127,9 @@ bool static ConnectTip(CValidationState &state, CBlockIndex *pindexNew, CBlock *
LogPrint("bench", " - Load block from disk: %.2fms [%.2fs]\n", (nTime2 - nTime1) * 0.001, nTimeReadFromDisk * 0.000001);
{
CCoinsViewCache view(pcoinsTip);
CNCCTrieCache trieCache(pnccTrie);
CInv inv(MSG_BLOCK, pindexNew->GetBlockHash());
bool rv = ConnectBlock(*pblock, state, pindexNew, view);
bool rv = ConnectBlock(*pblock, state, pindexNew, view, trieCache);
g_signals.BlockChecked(*pblock, state);
if (!rv) {
if (state.IsInvalid())
@ -2066,6 +2140,7 @@ bool static ConnectTip(CValidationState &state, CBlockIndex *pindexNew, CBlock *
nTime3 = GetTimeMicros(); nTimeConnectTotal += nTime3 - nTime2;
LogPrint("bench", " - Connect total: %.2fms [%.2fs]\n", (nTime3 - nTime2) * 0.001, nTimeConnectTotal * 0.000001);
assert(view.Flush());
assert(trieCache.flush());
}
int64_t nTime4 = GetTimeMicros(); nTimeFlush += nTime4 - nTime3;
LogPrint("bench", " - Flush: %.2fms [%.2fs]\n", (nTime4 - nTime3) * 0.001, nTimeFlush * 0.000001);
@ -2630,9 +2705,13 @@ bool ContextualCheckBlock(const CBlock& block, CValidationState& state, CBlockIn
}
}
// TODO: This looks like a good place to check the "merkle" root of the
// TODO: NCC tree that will result from applying this block
// TODO: ~~This looks like a good place to check the "merkle" root of the
// TODO: NCC tree that will result from applying this block.~~
// TODO: Actually, it's not, because the merkle root needs access to
// TODO: all of the coins spent by the new transactions, to check if
// TODO: they're NCC transactions. So the better place is in
// TODO: ConnectBlock, where inputs are checked against the coins
// TODO: they are spending.
return true;
}
@ -2772,6 +2851,7 @@ bool TestBlockValidity(CValidationState &state, const CBlock& block, CBlockIndex
assert(pindexPrev == chainActive.Tip());
CCoinsViewCache viewNew(pcoinsTip);
CNCCTrieCache trieCache(pnccTrie);
CBlockIndex indexDummy(block);
indexDummy.pprev = pindexPrev;
indexDummy.nHeight = pindexPrev->nHeight + 1;
@ -2783,7 +2863,7 @@ bool TestBlockValidity(CValidationState &state, const CBlock& block, CBlockIndex
return false;
if (!ContextualCheckBlock(block, state, pindexPrev))
return false;
if (!ConnectBlock(block, state, &indexDummy, viewNew, true))
if (!ConnectBlock(block, state, &indexDummy, viewNew, trieCache, true))
return false;
assert(state.IsValid());
@ -3000,6 +3080,7 @@ bool CVerifyDB::VerifyDB(CCoinsView *coinsview, int nCheckLevel, int nCheckDepth
nCheckLevel = std::max(0, std::min(4, nCheckLevel));
LogPrintf("Verifying last %i blocks at level %i\n", nCheckDepth, nCheckLevel);
CCoinsViewCache coins(coinsview);
CNCCTrieCache trieCache(pnccTrie);
CBlockIndex* pindexState = chainActive.Tip();
CBlockIndex* pindexFailure = NULL;
int nGoodTransactions = 0;
@ -3029,7 +3110,7 @@ bool CVerifyDB::VerifyDB(CCoinsView *coinsview, int nCheckLevel, int nCheckDepth
// check level 3: check for inconsistencies during memory-only disconnect of tip blocks
if (nCheckLevel >= 3 && pindex == pindexState && (coins.GetCacheSize() + pcoinsTip->GetCacheSize()) <= nCoinCacheSize) {
bool fClean = true;
if (!DisconnectBlock(block, state, pindex, coins, &fClean))
if (!DisconnectBlock(block, state, pindex, coins, trieCache, &fClean))
return error("VerifyDB() : *** irrecoverable inconsistency in block data at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString());
pindexState = pindex->pprev;
if (!fClean) {
@ -3054,7 +3135,7 @@ bool CVerifyDB::VerifyDB(CCoinsView *coinsview, int nCheckLevel, int nCheckDepth
CBlock block;
if (!ReadBlockFromDisk(block, pindex))
return error("VerifyDB() : *** ReadBlockFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString());
if (!ConnectBlock(block, state, pindex, coins))
if (!ConnectBlock(block, state, pindex, coins, trieCache))
return error("VerifyDB() : *** found unconnectable block at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString());
}
}

View file

@ -16,6 +16,7 @@
#include "coins.h"
#include "primitives/block.h"
#include "primitives/transaction.h"
#include "ncctrie.h"
#include "net.h"
#include "pow.h"
#include "script/script.h"
@ -348,10 +349,10 @@ bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex);
* In case pfClean is provided, operation will try to be tolerant about errors, and *pfClean
* will be true if no problems were found. Otherwise, the return value will be false in case
* of problems. Note that in any case, coins may be modified. */
bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& coins, bool* pfClean = NULL);
bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& coins, CNCCTrieCache& trieCache, bool* pfClean = NULL);
/** Apply the effects of this block (with given index) on the UTXO set represented by coins */
bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& coins, bool fJustCheck = false);
bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& coins, CNCCTrieCache& trieCache, bool fJustCheck = false);
/** Context-independent validity checks */
bool CheckBlockHeader(const CBlockHeader& block, CValidationState& state, bool fCheckPOW = true);
@ -510,6 +511,9 @@ extern CChain chainActive;
/** Global variable that points to the active CCoinsView (protected by cs_main) */
extern CCoinsViewCache *pcoinsTip;
/** Global variable that points to the active CNCCTrie (protected by cs_main) */
extern CNCCTrie *pnccTrie;
/** Global variable that points to the active block tree (protected by cs_main) */
extern CBlockTreeDB *pblocktree;

View file

@ -9,6 +9,8 @@
#include "primitives/transaction.h"
#include "hash.h"
#include "main.h"
#include "ncc.h"
#include "ncctrie.h"
#include "net.h"
#include "pow.h"
#include "timedata.h"
@ -135,6 +137,7 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn)
CBlockIndex* pindexPrev = chainActive.Tip();
const int nHeight = pindexPrev->nHeight + 1;
CCoinsViewCache view(pcoinsTip);
CNCCTrieCache trieCache(pnccTrie);
// Priority order to process transactions
list<COrphan> vOrphan; // list memory doesn't move
@ -280,6 +283,43 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn)
UpdateCoins(tx, state, view, nHeight);
BOOST_FOREACH(const CTxIn& txin, tx.vin)
{
const CCoins* coins = view.AccessCoins(txin.prevout.hash);
// This seems to happen during testing, and should never happen otherwise
if (!coins || txin.prevout.n >= coins->vout.size())
{
LogPrintf("!coins || txin.prevout.n >= coins->vout.size()");
continue;
}
std::vector<std::vector<unsigned char> > vvchParams;
int op;
if (DecodeNCCScript(coins->vout[txin.prevout.n].scriptPubKey, op, vvchParams))
{
assert(vvchParams.size() == 2);
std::string name(vvchParams[0].begin(), vvchParams[0].end());
if (!trieCache.removeName(name, txin.prevout.hash, txin.prevout.n))
LogPrintf("Something went wrong removing the name");
}
}
for (unsigned int i = 0; i < tx.vout.size(); ++i)
{
const CTxOut& txout = tx.vout[i];
std::vector<std::vector<unsigned char> > vvchParams;
int op;
if (DecodeNCCScript(txout.scriptPubKey, op, vvchParams))
{
assert(vvchParams.size() == 2);
std::string name(vvchParams[0].begin(), vvchParams[0].end());
if (!trieCache.insertName(name, tx.GetHash(), i, txout.nValue, nHeight))
LogPrintf("Something went wrong inserting the name");
}
}
// Added
pblock->vtx.push_back(tx);
pblocktemplate->vTxFees.push_back(nTxFees);
@ -329,6 +369,7 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn)
pblock->nBits = GetNextWorkRequired(pindexPrev, pblock);
pblock->nNonce = 0;
pblocktemplate->vTxSigOps[0] = GetLegacySigOpCount(pblock->vtx[0]);
pblock->hashNCCTrie = trieCache.getMerkleHash();
CValidationState state;
if (!TestBlockValidity(state, *pblock, pindexPrev, false, false))

View file

@ -1,4 +1,7 @@
#include "ncctrie.h"
#include "leveldbwrapper.h"
#include <boost/scoped_ptr.hpp>
std::string CNodeValue::ToString()
{
@ -79,6 +82,8 @@ bool CNCCTrie::empty() const
bool CNCCTrie::checkConsistency()
{
if (empty())
return true;
return recursiveCheckConsistency(&root);
}
@ -114,11 +119,6 @@ bool CNCCTrie::recursiveCheckConsistency(CNCCTrieNode* node)
return calculatedHash == node->hash;
}
/*bool cachesort (std::pair<std::string, CNCCTrieNode*>& i, std::pair<std::string, CNCCTrieNode*>& j)
{
return i.first.size() < j.first.size();
}*/
bool CNCCTrie::update(nodeCacheType& cache, hashMapType& hashes)
{
// General strategy: the cache is ordered by length, ensuring child
@ -144,7 +144,6 @@ bool CNCCTrie::update(nodeCacheType& cache, hashMapType& hashes)
{
return true;
}
//std::sort(cache.begin(), cache.end(), cachesort);
bool success = true;
std::vector<std::string> deletedNames;
for (nodeCacheType::iterator itcache = cache.begin(); itcache != cache.end(); ++itcache)
@ -263,6 +262,54 @@ bool CNCCTrie::BatchWrite(nodeCacheType& changedNodes, std::vector<std::string>&
return db.WriteBatch(batch);
}
bool CNCCTrie::InsertFromDisk(const std::string& name, CNCCTrieNode* node)
{
if (name.size() == 0)
root = *node;
return true;
CNCCTrieNode* current = &root;
for (std::string::const_iterator itname = name.begin(); itname + 1 != name.end(); ++itname)
{
nodeMapType::iterator itchild = current->children.find(*itname);
if (itchild == current->children.end())
return false;
current = itchild->second;
}
current->children[name[name.size()-1]] = node;
return true;
}
bool CNCCTrie::ReadFromDisk(bool check)
{
boost::scoped_ptr<leveldb::Iterator> pcursor(const_cast<CLevelDBWrapper*>(&db)->NewIterator());
pcursor->SeekToFirst();
while (pcursor->Valid())
{
//TODO: make try statement here
{
leveldb::Slice slKey = pcursor->key();
CDataStream ssKey(slKey.data(), slKey.data()+slKey.size(), SER_DISK, CLIENT_VERSION);
char chType;
ssKey >> chType;
if (chType == 'n')
{
leveldb::Slice slValue = pcursor->value();
std::string name;
ssKey >> name;
CDataStream ssValue(slValue.data(), slValue.data()+slValue.size(), SER_DISK, CLIENT_VERSION);
CNCCTrieNode* node = new CNCCTrieNode();
ssValue >> *node;
if (!InsertFromDisk(name, node))
return false;
}
}
}
if (check)
return checkConsistency();
return true;
}
bool CNCCTrieCache::recursiveComputeMerkleHash(CNCCTrieNode* tnCurrent, std::string sPos) const
{
std::string stringToHash;
@ -334,11 +381,12 @@ uint256 CNCCTrieCache::getMerkleHash() const
bool CNCCTrieCache::empty() const
{
return base->empty() && cache.empty();
return !base || (base->empty() && cache.empty());
}
bool CNCCTrieCache::insertName(const std::string name, uint256 txhash, int nOut, CAmount nAmount, int nHeight) const
{
assert(base);
CNCCTrieNode* currentNode = &(base->root);
nodeCacheType::iterator cachedNode;
cachedNode = cache.find("");
@ -416,15 +464,16 @@ bool CNCCTrieCache::insertName(const std::string name, uint256 txhash, int nOut,
return true;
}
bool CNCCTrieCache::removeName(const std::string name, uint256 txhash, int nOut, CAmount nAmount, int nHeight) const
bool CNCCTrieCache::removeName(const std::string name, uint256 txhash, int nOut) const
{
CNCCTrieNode* currentNode = &(base->root);
nodeCacheType::iterator cachedNode;
cachedNode = cache.find("");
if (cachedNode != cache.end())
currentNode = cachedNode->second;
assert(currentNode != NULL); // If there is no root in either the trie or the cache, how can there be any names to remove?
for (std::string::const_iterator itCur = name.begin(); itCur != name.end(); ++itCur)
assert(base);
CNCCTrieNode* currentNode = &(base->root);
nodeCacheType::iterator cachedNode;
cachedNode = cache.find("");
if (cachedNode != cache.end())
currentNode = cachedNode->second;
assert(currentNode != NULL); // If there is no root in either the trie or the cache, how can there be any names to remove?
for (std::string::const_iterator itCur = name.begin(); itCur != name.end(); ++itCur)
{
std::string sCurrentSubstring(name.begin(), itCur);
std::string sNextSubstring(name.begin(), itCur + 1);
@ -456,7 +505,7 @@ bool CNCCTrieCache::removeName(const std::string name, uint256 txhash, int nOut,
}
bool fChanged = false;
assert(currentNode != NULL);
bool success = currentNode->removeValue(CNodeValue(txhash, nOut, nAmount, nHeight), &fChanged);
bool success = currentNode->removeValue(CNodeValue(txhash, nOut), &fChanged);
assert(success);
if (fChanged)
{
@ -562,6 +611,8 @@ bool CNCCTrieCache::flush()
{
if (dirty())
getMerkleHash();
if (!base)
return true;
bool success = base->update(cache, cacheHashes);
if (success)
{

View file

@ -1,5 +1,5 @@
#ifndef BITCOIN_NCCTRIE_H
#define BITCOIN_NCC_H
#define BITCOIN_NCCTRIE_H
#include "amount.h"
#include "serialize.h"
@ -23,6 +23,7 @@ public:
CAmount nAmount;
int nHeight;
CNodeValue() {};
CNodeValue(uint256 txhash, uint32_t nOut) : txhash(txhash), nOut(nOut), nAmount(0), nHeight(0) {}
CNodeValue(uint256 txhash, uint32_t nOut, CAmount nAmount, int nHeight) : txhash(txhash), nOut(nOut), nAmount(nAmount), nHeight(nHeight) {}
std::string ToString();
@ -57,7 +58,7 @@ public:
}
bool operator==(const CNodeValue& other) const
{
return txhash == other.txhash && nOut == other.nOut && nAmount == other.nAmount && nHeight == other.nHeight;
return txhash == other.txhash && nOut == other.nOut;
}
bool operator!=(const CNodeValue& other) const
{
@ -122,20 +123,21 @@ class CNCCTrieCache;
class CNCCTrie
{
public:
CNCCTrie(CCoinsViewCache* coins) : db(GetDataDir() / "ncctrie", 100, false, false), coins(coins), root(uint256S("0000000000000000000000000000000000000000000000000000000000000001")) {}
CNCCTrie() : db(GetDataDir() / "ncctrie", 100, false, false), root(uint256S("0000000000000000000000000000000000000000000000000000000000000001")) {}
uint256 getMerkleHash();
CLevelDBWrapper db;
bool empty() const;
bool checkConsistency();
bool ReadFromDisk(bool check = false);
friend class CNCCTrieCache;
private:
CCoinsViewCache* coins;
bool update(nodeCacheType& cache, hashMapType& hashes);
bool updateName(const std::string& name, CNCCTrieNode* updatedNode, std::vector<std::string>& deletedNames);
bool updateHash(const std::string& name, uint256& hash, CNCCTrieNode** pNodeRet);
bool recursiveNullify(CNCCTrieNode* node, std::string& name, std::vector<std::string>& deletedNames);
bool recursiveCheckConsistency(CNCCTrieNode* node);
bool BatchWrite(nodeCacheType& changedNodes, std::vector<std::string>& deletedNames);
bool InsertFromDisk(const std::string& name, CNCCTrieNode* node);
CNCCTrieNode root;
};
@ -148,7 +150,7 @@ public:
bool flush();
bool dirty() const { return !dirtyHashes.empty(); }
bool insertName (const std::string name, uint256 txhash, int nOut, CAmount nAmount, int nDepth) const;
bool removeName (const std::string name, uint256 txhash, int nOut, CAmount nAmount, int nDepth) const;
bool removeName (const std::string name, uint256 txhash, int nOut) const;
~CNCCTrieCache() { clear(); }
private:
CNCCTrie* base;

View file

@ -28,6 +28,7 @@ public:
int32_t nVersion;
uint256 hashPrevBlock;
uint256 hashMerkleRoot;
uint256 hashNCCTrie;
uint32_t nTime;
uint32_t nBits;
uint32_t nNonce;
@ -48,6 +49,7 @@ public:
READWRITE(nTime);
READWRITE(nBits);
READWRITE(nNonce);
READWRITE(hashNCCTrie);
}
void SetNull()
@ -55,6 +57,7 @@ public:
nVersion = CBlockHeader::CURRENT_VERSION;
hashPrevBlock.SetNull();
hashMerkleRoot.SetNull();
hashNCCTrie.SetNull();
nTime = 0;
nBits = 0;
nNonce = 0;
@ -118,6 +121,7 @@ public:
block.nTime = nTime;
block.nBits = nBits;
block.nNonce = nNonce;
block.hashNCCTrie = hashNCCTrie;
return block;
}

View file

@ -180,6 +180,7 @@ BOOST_AUTO_TEST_CASE(bloom_match)
BOOST_CHECK_MESSAGE(!filter.IsRelevantAndUpdate(tx), "Simple Bloom filter matched COutPoint for an output we didn't care about");
}
/* TODO: put these tests back in when there are real blocks to use
BOOST_AUTO_TEST_CASE(merkle_block_1)
{
// Random real block (0000000000013b8ab2cd513b0261a14096412195a72a0c4827d229dcc7e0f7af)
@ -457,5 +458,6 @@ BOOST_AUTO_TEST_CASE(merkle_block_4_test_update_none)
BOOST_CHECK(!filter.contains(COutPoint(uint256S("0x147caa76786596590baa4e98f5d9f48b86c7765e489f7a6ff3360fe5c674360b"), 0)));
BOOST_CHECK(!filter.contains(COutPoint(uint256S("0x02981fa052f0481dbc5868f4fc2166035a10f27a03cfd2de67326471df5bc041"), 0)));
}
*/
BOOST_AUTO_TEST_SUITE_END()

View file

@ -52,34 +52,34 @@ struct {
unsigned char extranonce;
unsigned int nonce;
} blockinfo[] = {
{4, 0x00000002}, {2, 0x00000002}, {1, 0x00000000}, {1, 0x00000001}, //0
{2, 0x00000001}, {2, 0x00000002}, {1, 0x00000000}, {2, 0x0000000b}, //4
{2, 0x00000000}, {1, 0x00000000}, {1, 0x00000000}, {2, 0x00000001}, //8
{2, 0x00000000}, {1, 0x00000000}, {2, 0x00000000}, {2, 0x00000001}, //12
{1, 0x00000000}, {2, 0x00000004}, {1, 0x00000000}, {1, 0x00000000}, //16
{3, 0x00000002}, {2, 0x00000000}, {2, 0x00000000}, {1, 0x00000000}, //20
{2, 0x00000000}, {1, 0x00000002}, {2, 0x00000000}, {2, 0x00000000}, //24
{2, 0x00000000}, {2, 0x00000001}, {2, 0x00000000}, {2, 0x00000000}, //28
{1, 0x00000000}, {2, 0x00000001}, {2, 0x00000000}, {1, 0x00000000}, //32
{2, 0x00000001}, {1, 0x00000000}, {2, 0x00000001}, {1, 0x00000000}, //36
{1, 0x00000001}, {3, 0x00000000}, {2, 0x00000000}, {5, 0x00000002}, //40
{1, 0x00000001}, {5, 0x00000000}, {1, 0x00000005}, {1, 0x00000000}, //44
{1, 0x00000000}, {2, 0x00000002}, {1, 0x00000002}, {1, 0x00000000}, //48
{1, 0x00000000}, {1, 0x00000000}, {5, 0x00000001}, {5, 0x00000000}, //52
{1, 0x00000000}, {1, 0x00000000}, {6, 0x00000000}, {2, 0x00000001}, //56
{2, 0x00000000}, {1, 0x00000000}, {1, 0x00000001}, {1, 0x00000002}, //60
{2, 0x00000001}, {2, 0x00000000}, {1, 0x00000003}, {1, 0x00000001}, //64
{1, 0x00000000}, {5, 0x00000000}, {5, 0x00000002}, {1, 0x00000001}, //68
{1, 0x00000001}, {2, 0x00000000}, {2, 0x00000001}, {1, 0x00000003}, //72
{2, 0x00000002}, {1, 0x00000000}, {2, 0x00000000}, {2, 0x00000006}, //76
{1, 0x00000000}, {1, 0x00000000}, {1, 0x00000001}, {5, 0x00000000}, //80
{1, 0x00000001}, {1, 0x00000001}, {1, 0x00000000}, {1, 0x00000002}, //84
{1, 0x00000000}, {1, 0x00000000}, {1, 0x00000002}, {2, 0x00000001}, //88
{0, 0x00000003}, {1, 0x00000004}, {2, 0x00000000}, {2, 0x00000001}, //92
{2, 0x00000000}, {1, 0x00000002}, {1, 0x00000000}, {1, 0x00000002}, //96
{1, 0x00000000}, {1, 0x00000003}, {1, 0x00000000}, {5, 0x00000000}, //100
{2, 0x00000000}, {1, 0x00000000}, {1, 0x00000001}, {1, 0x00000001}, //104
{2, 0x00000000}, {2, 0x00000002}, //108
{4, 0x00000000}, {2, 0x00000001}, {1, 0x00000002}, {1, 0x00000000}, //0
{2, 0x00000001}, {2, 0x00000001}, {1, 0x00000001}, {2, 0x00000000}, //4
{2, 0x00000001}, {1, 0x00000000}, {1, 0x00000001}, {2, 0x00000001}, //8
{2, 0x00000003}, {1, 0x00000001}, {2, 0x00000001}, {2, 0x00000001}, //12
{1, 0x00000001}, {2, 0x00000000}, {1, 0x00000005}, {1, 0x00000001}, //16
{3, 0x00000000}, {2, 0x00000000}, {2, 0x00000000}, {1, 0x00000004}, //20
{2, 0x00000000}, {1, 0x00000000}, {2, 0x00000002}, {2, 0x00000003}, //24
{2, 0x00000001}, {2, 0x00000001}, {2, 0x00000000}, {2, 0x00000000}, //28
{1, 0x00000001}, {2, 0x00000000}, {2, 0x00000001}, {1, 0x00000000}, //32
{2, 0x00000000}, {1, 0x00000001}, {2, 0x00000000}, {1, 0x00000000}, //36
{1, 0x00000000}, {3, 0x00000000}, {2, 0x00000005}, {5, 0x00000000}, //40
{1, 0x00000002}, {5, 0x00000001}, {1, 0x00000001}, {1, 0x00000001}, //44
{1, 0x00000001}, {2, 0x00000002}, {1, 0x00000002}, {1, 0x00000001}, //48
{1, 0x00000000}, {1, 0x00000000}, {5, 0x00000002}, {5, 0x00000000}, //52
{1, 0x00000000}, {1, 0x00000000}, {6, 0x00000001}, {2, 0x00000000}, //56
{2, 0x00000003}, {1, 0x00000000}, {1, 0x00000000}, {1, 0x00000000}, //60
{2, 0x00000000}, {2, 0x00000001}, {1, 0x00000000}, {1, 0x00000001}, //64
{1, 0x00000003}, {5, 0x00000000}, {5, 0x00000000}, {1, 0x00000002}, //68
{1, 0x00000000}, {2, 0x00000001}, {2, 0x00000004}, {1, 0x00000000}, //72
{2, 0x00000001}, {1, 0x00000002}, {2, 0x00000004}, {2, 0x00000000}, //76
{1, 0x00000000}, {1, 0x00000002}, {1, 0x00000000}, {5, 0x00000002}, //80
{1, 0x00000001}, {1, 0x00000003}, {1, 0x00000000}, {1, 0x00000002}, //84
{1, 0x00000000}, {1, 0x00000000}, {1, 0x00000002}, {2, 0x00000000}, //88
{0, 0x00000000}, {1, 0x00000000}, {2, 0x00000001}, {2, 0x00000001}, //92
{2, 0x00000000}, {1, 0x00000000}, {1, 0x00000000}, {1, 0x00000000}, //96
{1, 0x00000000}, {1, 0x00000000}, {1, 0x00000000}, {5, 0x00000000}, //100
{2, 0x00000000}, {1, 0x00000002}, {1, 0x00000000}, {1, 0x00000001}, //104
{2, 0x00000003}, {2, 0x00000000}, //108
};
// NOTE: These tests rely on CreateNewBlock doing its own self-validation!
@ -122,6 +122,8 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
if (CheckProofOfWork(pblock->GetHash(), pblock->nBits))
{
fFound = true;
if (i % 4 == 0)
std::cout << std::endl;
std::cout << "Block number: " << i << std::endl;
std::cout << "Nonce: " << std::hex << pblock->nNonce << std::dec << std::endl;
}

View file

@ -51,10 +51,7 @@ BOOST_AUTO_TEST_CASE(ncctrie_create_insert_remov)
uint256 hash4;
hash4.SetHex("a79e8a5b28f7fa5e8836a4b48da9988bdf56ce749f81f413cb754f963a516200");
CCoinsView coinsDummy;
CCoinsViewCache coins(&coinsDummy);
CNCCTrie trie(&coins);
CNCCTrie trie;
BOOST_CHECK(trie.empty());
@ -71,7 +68,7 @@ BOOST_AUTO_TEST_CASE(ncctrie_create_insert_remov)
ntState.insertName(std::string("tes"), tx4.GetHash(), 0, 50, 100);
BOOST_CHECK(ntState.getMerkleHash() == hash2);
ntState.insertName(std::string("testtesttesttest"), tx5.GetHash(), 0, 50, 100);
ntState.removeName(std::string("testtesttesttest"), tx5.GetHash(), 0, 50, 100);
ntState.removeName(std::string("testtesttesttest"), tx5.GetHash(), 0);
BOOST_CHECK(ntState.getMerkleHash() == hash2);
ntState.flush();
@ -80,7 +77,7 @@ BOOST_AUTO_TEST_CASE(ncctrie_create_insert_remov)
BOOST_CHECK(trie.checkConsistency());
CNCCTrieCache ntState2(&trie);
ntState2.insertName(std::string("abab"), tx6.GetHash(), 0, 50, 100);
ntState2.removeName(std::string("test"), tx1.GetHash(), 0, 50, 100);
ntState2.removeName(std::string("test"), tx1.GetHash(), 0);
BOOST_CHECK(ntState2.getMerkleHash() == hash3);
@ -99,7 +96,7 @@ BOOST_AUTO_TEST_CASE(ncctrie_create_insert_remov)
BOOST_CHECK(trie.checkConsistency());
CNCCTrieCache ntState4(&trie);
ntState4.removeName(std::string("abab"), tx6.GetHash(), 0, 50, 100);
ntState4.removeName(std::string("abab"), tx6.GetHash(), 0);
BOOST_CHECK(ntState4.getMerkleHash() == hash2);
ntState4.flush();
BOOST_CHECK(!trie.empty());
@ -107,7 +104,7 @@ BOOST_AUTO_TEST_CASE(ncctrie_create_insert_remov)
BOOST_CHECK(trie.checkConsistency());
CNCCTrieCache ntState5(&trie);
ntState5.removeName(std::string("test"), tx3.GetHash(), 0, 50, 101);
ntState5.removeName(std::string("test"), tx3.GetHash(), 0);
BOOST_CHECK(ntState5.getMerkleHash() == hash2);
ntState5.flush();