fix a few bugs and add some assertions

ncctrietests.cpp:
add test to make sure emptying the ncc trie cache results in a
default hash ("00...01")
ncctrie.cpp:
fix the ncc trie cache so that emptying it causes the root hash
to be computed as the default hash ("00...01")
fix two bugs in reading the ncc trie from disk:
all insertions were returning immediately due to parenthesis
misplacement, and the loop reading nodes from disk wasn't
terminating due to a lack of an incrementing operation
miner.cpp
fix a bug in the miner where input coins in the cache were being
spent before their txouts could be checked for NCC commands
main.cpp
add some assertions when disconnecting blocks from the active
chain, ensuring the merkle root of the ncc trie after undoing the
block is equal to the hashNCCTrie in the previous block
fix an erroneous assertion in ConnectBlock to assert the correct
expected value
change some log statements to use __func__
init.cpp
change the order of some init operations so the NCC trie is
restored from disk before the DB is verified
This commit is contained in:
Jimmy Kiselak 2015-02-10 11:19:41 -05:00
parent df59145787
commit 389a2a963e
5 changed files with 60 additions and 21 deletions

View file

@ -1073,17 +1073,18 @@ bool AppInit2(boost::thread_group& threadGroup)
break;
}
if (!pnccTrie->ReadFromDisk(true))
{
strLoadError = _("Error loading the ncc trie from disk");
break;
}
uiInterface.InitMessage(_("Verifying blocks..."));
if (!CVerifyDB().VerifyDB(pcoinsdbview, GetArg("-checklevel", 3),
GetArg("-checkblocks", 288))) {
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

@ -1628,8 +1628,9 @@ static bool ApplyTxInUndo(const CTxInUndo& undo, CCoinsViewCache& view, CNCCTrie
{
assert(vvchParams.size() == 2);
std::string name(vvchParams[0].begin(), vvchParams[0].end());
LogPrintf("%s: Restoring %s to the NCC trie due to a block being disconnected\n", __func__, name.c_str());
if (!trieCache.insertName(name, out.hash, out.n, undo.txout.nValue, undo.nHeight))
LogPrintf("Something went wrong inserting the name");
LogPrintf("%s: Something went wrong inserting the name\n", __func__);
}
coins->vout[out.n] = undo.txout;
@ -1687,8 +1688,9 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex
{
assert(vvchParams.size() == 2);
std::string name(vvchParams[0].begin(), vvchParams[0].end());
LogPrintf("%s: Removing %s from the ncc trie due to its block being disconnected\n", __func__, name.c_str());
if (!trieCache.removeName(name, hash, i))
LogPrintf("Something went wrong removing the name");
LogPrintf("%s: Something went wrong removing the name %s in hash %s\n", __func__, name.c_str(), hash.GetHex());
}
}
@ -1712,6 +1714,7 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex
// move best block pointer to prevout block
view.SetBestBlock(pindex->pprev->GetBlockHash());
assert(trieCache.getMerkleHash() == pindex->pprev->hashNCCTrie);
if (pfClean) {
*pfClean = fClean;
@ -1870,10 +1873,10 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
std::vector<std::vector<unsigned char> > vvchParams;
if (DecodeNCCScript(coins->vout[txin.prevout.n].scriptPubKey, op, vvchParams))
{
assert(vvchParams.size() == 1);
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");
LogPrintf("%s: Something went wrong removing the name\n", __func__);
}
}
@ -1888,7 +1891,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
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");
LogPrintf("%s: Something went wrong inserting the name\n", __func__);
}
}
}
@ -2094,6 +2097,7 @@ bool static DisconnectTip(CValidationState &state) {
return error("DisconnectTip(): DisconnectBlock %s failed", pindexDelete->GetBlockHash().ToString());
assert(view.Flush());
assert(trieCache.flush());
assert(pindexDelete->pprev->hashNCCTrie == trieCache.getMerkleHash());
}
LogPrint("bench", "- Disconnect block: %.2fms\n", (GetTimeMicros() - nStart) * 0.001);
// Write the chain state to disk, if necessary.

View file

@ -281,15 +281,15 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn)
if (!CheckInputs(tx, state, view, true, MANDATORY_SCRIPT_VERIFY_FLAGS, true))
continue;
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()");
LogPrintf("%s: !coins || txin.prevout.n >= coins->vout.size(). txin.prevout.hash = %s\n", __func__, txin.prevout.hash.GetHex());
if (coins)
LogPrintf("coins is not null. txin.prevout.n = %d, coins->vout.size() = %d\n", txin.prevout.n, coins->vout.size());
continue;
}
@ -301,10 +301,12 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn)
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");
LogPrintf("%s: Something went wrong removing the name\n", __func__);
}
}
UpdateCoins(tx, state, view, nHeight);
for (unsigned int i = 0; i < tx.vout.size(); ++i)
{
const CTxOut& txout = tx.vout[i];
@ -316,7 +318,7 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn)
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");
LogPrintf("%s: Something went wrong inserting the name\n", __func__);
}
}

View file

@ -109,7 +109,7 @@ json_spirit::Array CNCCTrie::dumpToJSON() const
{
json_spirit::Array ret;
if (!recursiveDumpToJSON("", &root, ret))
LogPrintf("Something went wrong dumping to JSON");
LogPrintf("%s: Something went wrong dumping to JSON", __func__);
return ret;
}
@ -321,8 +321,10 @@ bool CNCCTrie::BatchWrite(nodeCacheType& changedNodes, std::vector<std::string>&
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)
{
@ -342,7 +344,7 @@ bool CNCCTrie::ReadFromDisk(bool check)
while (pcursor->Valid())
{
//TODO: make try statement here
try
{
leveldb::Slice slKey = pcursor->key();
CDataStream ssKey(slKey.data(), slKey.data()+slKey.size(), SER_DISK, CLIENT_VERSION);
@ -359,15 +361,35 @@ bool CNCCTrie::ReadFromDisk(bool check)
if (!InsertFromDisk(name, node))
return false;
}
pcursor->Next();
}
catch (const std::exception& e)
{
return error("%s: Deserialize or I/O error - %s", __func__, e.what());
}
}
if (check)
return checkConsistency();
{
LogPrintf("Checking NCC trie consistency...");
if( checkConsistency())
{
LogPrintf("consistent\n");
return true;
}
LogPrintf("inconsistent!\n");
return false;
}
return true;
}
bool CNCCTrieCache::recursiveComputeMerkleHash(CNCCTrieNode* tnCurrent, std::string sPos) const
{
if (sPos == "" && tnCurrent->empty())
{
cacheHashes[""] = uint256S("0000000000000000000000000000000000000000000000000000000000000001");
return true;
}
std::string stringToHash;
CNodeValue val;
@ -547,7 +569,7 @@ bool CNCCTrieCache::removeName(const std::string name, uint256 txhash, int nOut)
continue;
}
// The name doesn't exist in either the trie or the cache, so how can we remove it?
LogPrintf("%s: The name %s does not exist in the trie\n", __func__, name.c_str());
return false;
}

View file

@ -30,9 +30,10 @@ CMutableTransaction BuildTransaction(const uint256& prevhash)
return tx;
}
BOOST_AUTO_TEST_CASE(ncctrie_create_insert_remov)
BOOST_AUTO_TEST_CASE(ncctrie_create_insert_remove)
{
CMutableTransaction tx1 = BuildTransaction(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());
@ -75,6 +76,15 @@ BOOST_AUTO_TEST_CASE(ncctrie_create_insert_remov)
BOOST_CHECK(!trie.empty());
BOOST_CHECK(trie.getMerkleHash() == hash2);
BOOST_CHECK(trie.checkConsistency());
CNCCTrieCache ntState1(&trie);
ntState1.removeName(std::string("test"), tx1.GetHash(), 0);
ntState1.removeName(std::string("test2"), tx2.GetHash(), 0);
ntState1.removeName(std::string("test"), tx3.GetHash(), 0);
ntState1.removeName(std::string("tes"), tx4.GetHash(), 0);
BOOST_CHECK(ntState1.getMerkleHash() == hash0);
CNCCTrieCache ntState2(&trie);
ntState2.insertName(std::string("abab"), tx6.GetHash(), 0, 50, 100);
ntState2.removeName(std::string("test"), tx1.GetHash(), 0);