From: Pieter Wuille Date: Wed, 21 Mar 2012 22:08:13 +0000 (+0100) Subject: Add -checklevel and improve -checkblocks X-Git-Tag: v0.4.0-unstable~129^2~137^2 X-Git-Url: https://git.novaco.in/?p=novacoin.git;a=commitdiff_plain;h=4538e45c46d3829a21aed7685e6fbac7e8aafad3 Add -checklevel and improve -checkblocks -checkblocks now takes a numeric argument: the number of blocks that must be verified at the end of the chain. Default is 2500, and 0 means all blocks. -checklevel specifies how thorough the verification must be: 0: only check whether the block exists on disk 1: verify block validity (default) 2: verify transaction index validity 3: check transaction hashes 4: check whether spent txouts were spent within the main chain 5: check whether all prevouts are marked spent 6: check whether spent txouts were spent by a valid transaction that consumes them --- diff --git a/src/db.cpp b/src/db.cpp index 0344c79..0ee5185 100644 --- a/src/db.cpp +++ b/src/db.cpp @@ -580,19 +580,114 @@ bool CTxDB::LoadBlockIndex() ReadBestInvalidWork(bnBestInvalidWork); // Verify blocks in the best chain + int nCheckLevel = GetArg("-checklevel", 1); + int nCheckDepth = GetArg( "-checkblocks", 2500); + if (nCheckDepth == 0) + nCheckDepth = 1000000000; // suffices until the year 19000 + if (nCheckDepth > nBestHeight) + nCheckDepth = nBestHeight; + printf("Verifying last %i blocks at level %i\n", nCheckDepth, nCheckLevel); CBlockIndex* pindexFork = NULL; + map, CBlockIndex*> mapBlockPos; for (CBlockIndex* pindex = pindexBest; pindex && pindex->pprev; pindex = pindex->pprev) { - if (pindex->nHeight < nBestHeight-2500 && !mapArgs.count("-checkblocks")) + if (pindex->nHeight < nBestHeight-nCheckDepth) break; CBlock block; if (!block.ReadFromDisk(pindex)) return error("LoadBlockIndex() : block.ReadFromDisk failed"); - if (!block.CheckBlock()) + // check level 1: verify block validity + if (nCheckLevel>0 && !block.CheckBlock()) { printf("LoadBlockIndex() : *** found bad block at %d, hash=%s\n", pindex->nHeight, pindex->GetBlockHash().ToString().c_str()); pindexFork = pindex->pprev; } + // check level 2: verify transaction index validity + if (nCheckLevel>1) + { + pair pos = make_pair(pindex->nFile, pindex->nBlockPos); + mapBlockPos[pos] = pindex; + BOOST_FOREACH(const CTransaction &tx, block.vtx) + { + uint256 hashTx = tx.GetHash(); + CTxIndex txindex; + if (ReadTxIndex(hashTx, txindex)) + { + // check level 3: checker transaction hashes + if (nCheckLevel>2 || pindex->nFile != txindex.pos.nFile || pindex->nBlockPos != txindex.pos.nBlockPos) + { + // either an error or a duplicate transaction + CTransaction txFound; + if (!txFound.ReadFromDisk(txindex.pos)) + { + printf("LoadBlockIndex() : *** cannot read mislocated transaction %s\n", hashTx.ToString().c_str()); + pindexFork = pindex->pprev; + } + else + if (txFound.GetHash() != hashTx) // not a duplicate tx + { + printf("LoadBlockIndex(): *** invalid tx position for %s\n", hashTx.ToString().c_str()); + pindexFork = pindex->pprev; + } + } + // check level 4: check whether spent txouts were spent within the main chain + int nOutput = 0; + if (nCheckLevel>3) + BOOST_FOREACH(const CDiskTxPos &txpos, txindex.vSpent) + { + if (!txpos.IsNull()) + { + pair posFind = make_pair(txpos.nFile, txpos.nBlockPos); + if (!mapBlockPos.count(posFind)) + { + printf("LoadBlockIndex(): *** found bad spend at %d, hashBlock=%s, hashTx=%s\n", pindex->nHeight, pindex->GetBlockHash().ToString().c_str(), hashTx.ToString().c_str()); + pindexFork = pindex->pprev; + } + // check level 6: check whether spent txouts were spent by a valid transaction that consume them + if (nCheckLevel>5) + { + CTransaction txSpend; + if (!txSpend.ReadFromDisk(txpos)) + { + printf("LoadBlockIndex(): *** cannot read spending transaction of %s:%i from disk\n", hashTx.ToString().c_str(), nOutput); + pindexFork = pindex->pprev; + } + else if (!txSpend.CheckTransaction()) + { + printf("LoadBlockIndex(): *** spending transaction of %s:%i is invalid\n", hashTx.ToString().c_str(), nOutput); + pindexFork = pindex->pprev; + } + else + { + bool fFound = false; + BOOST_FOREACH(const CTxIn &txin, txSpend.vin) + if (txin.prevout.hash == hashTx && txin.prevout.n == nOutput) + fFound = true; + if (!fFound) + { + printf("LoadBlockIndex(): *** spending transaction of %s:%i does not spend it\n", hashTx.ToString().c_str(), nOutput); + pindexFork = pindex->pprev; + } + } + } + } + nOutput++; + } + } + // check level 5: check whether all prevouts are marked spent + if (nCheckLevel>4) + BOOST_FOREACH(const CTxIn &txin, tx.vin) + { + CTxIndex txindex; + if (ReadTxIndex(txin.prevout.hash, txindex)) + if (txindex.vSpent.size()-1 < txin.prevout.n || txindex.vSpent[txin.prevout.n].IsNull()) + { + printf("LoadBlockIndex(): *** found unspent prevout %s:%i in %s\n", txin.prevout.hash.ToString().c_str(), txin.prevout.n, hashTx.ToString().c_str()); + pindexFork = pindex->pprev; + } + } + } + } } if (pindexFork) { diff --git a/src/init.cpp b/src/init.cpp index a5db20b..647bf44 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -222,7 +222,9 @@ bool AppInit2(int argc, char* argv[]) " -rpcconnect= \t " + _("Send commands to node running on (default: 127.0.0.1)") + "\n" + " -blocknotify= " + _("Execute command when the best block changes (%s in cmd is replaced by block hash)") + "\n" + " -keypool= \t " + _("Set key pool size to (default: 100)") + "\n" + - " -rescan \t " + _("Rescan the block chain for missing wallet transactions") + "\n"; + " -rescan \t " + _("Rescan the block chain for missing wallet transactions") + "\n" + + " -checkblocks= \t\t " + _("How many blocks to check at startup (default: 2500, 0 = all)") + "\n" + + " -checklevel= \t\t " + _("How thorough the block verification is (0-6, default: 1)") + "\n"; #ifdef USE_SSL strUsage += string() +