diff options
author | Pieter Wuille <pieter.wuille@gmail.com> | 2013-01-03 15:29:07 +0100 |
---|---|---|
committer | Pieter Wuille <pieter.wuille@gmail.com> | 2013-01-04 14:58:47 +0100 |
commit | 1f355b66cd90f1bd96a862604a7216e8ba56f853 (patch) | |
tree | 05998e9ab46b5fe669211a5c0675fa8a8d73ac23 /src | |
parent | 8539361e66eace839060f77f39b770d045e7ce28 (diff) |
New database check routine
-checklevel gets a new meaning:
0: verify blocks can be read from disk (like before)
1: verify (contextless) block validity (like before)
2: verify undo files can be read and have good checksums
3: verify coin database is consistent with the last few blocks
(close to level 6 before)
4: verify all validity rules of the last few blocks
Level 3 is the new default, as it's reasonably fast. As level 3 and
4 are implemented using an in-memory rollback of the database, they
are limited to as many blocks as possible without exceeding the
limits set by -dbcache. The default of -dbcache=25 allows for some
150-200 blocks to be rolled back.
In case an error is found, the application quits with a message
instructing the user to restart with -reindex. Better instructions,
and automatic recovery (when possible) or automatic reindexing are
left as future work.
Diffstat (limited to 'src')
-rw-r--r-- | src/init.cpp | 7 | ||||
-rw-r--r-- | src/main.cpp | 65 | ||||
-rw-r--r-- | src/main.h | 2 |
3 files changed, 60 insertions, 14 deletions
diff --git a/src/init.cpp b/src/init.cpp index 557c23fc92..79e9b2fb10 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -300,7 +300,7 @@ std::string HelpMessage() " -rescan " + _("Rescan the block chain for missing wallet transactions") + "\n" + " -salvagewallet " + _("Attempt to recover private keys from a corrupt wallet.dat") + "\n" + " -checkblocks=<n> " + _("How many blocks to check at startup (default: 2500, 0 = all)") + "\n" + - " -checklevel=<n> " + _("How thorough the block verification is (0-6, default: 1)") + "\n" + + " -checklevel=<n> " + _("How thorough the block verification is (0-4, default: 3)") + "\n" + " -loadblock=<file> " + _("Imports blocks from external blk000??.dat file") + "\n" + " -reindex " + _("Rebuild blockchain index from current blk000??.dat files") + "\n" + @@ -752,7 +752,10 @@ bool AppInit2() pblocktree->WriteReindexing(true); if (!LoadBlockIndex()) - return InitError(_("Error loading blkindex.dat")); + return InitError(_("Error loading block/coin databases")); + + if (!VerifyDB()) + return InitError(_("Corrupted database detected. Please restart the client with -reindex.")); // as LoadBlockIndex can take several minutes, it's possible the user // requested to kill bitcoin-qt during the last operation. If so, exit. diff --git a/src/main.cpp b/src/main.cpp index 2619062a6b..3fd85131ab 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2388,36 +2388,77 @@ bool static LoadBlockIndexDB() BlockHashStr(hashBestChain).c_str(), nBestHeight, DateTimeStrFormat("%Y-%m-%d %H:%M:%S", pindexBest->GetBlockTime()).c_str()); + return true; +} + +bool VerifyDB() { + if (pindexBest == NULL || pindexBest->pprev == NULL) + return true; + // Verify blocks in the best chain - int nCheckLevel = GetArg("-checklevel", 1); + int nCheckLevel = GetArg("-checklevel", 3); int nCheckDepth = GetArg( "-checkblocks", 2500); if (nCheckDepth == 0) nCheckDepth = 1000000000; // suffices until the year 19000 if (nCheckDepth > nBestHeight) nCheckDepth = nBestHeight; + nCheckLevel = std::max(0, std::min(4, nCheckLevel)); printf("Verifying last %i blocks at level %i\n", nCheckDepth, nCheckLevel); - CBlockIndex* pindexFork = NULL; + CCoinsViewCache coins(*pcoinsTip, true); + CBlockIndex* pindexState = pindexBest; + CBlockIndex* pindexFailure = NULL; + int nGoodTransactions = 0; for (CBlockIndex* pindex = pindexBest; pindex && pindex->pprev; pindex = pindex->pprev) { if (fRequestShutdown || pindex->nHeight < nBestHeight-nCheckDepth) break; CBlock block; + // check level 0: read from disk if (!block.ReadFromDisk(pindex)) - return error("LoadBlockIndex() : block.ReadFromDisk failed"); + return error("VerifyDB() : *** block.ReadFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString().c_str()); // 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; + if (nCheckLevel >= 1 && !block.CheckBlock()) + return error("VerifyDB() : *** found bad block at %d, hash=%s\n", pindex->nHeight, pindex->GetBlockHash().ToString().c_str()); + // check level 2: verify undo validity + if (nCheckLevel >= 2 && pindex) { + CBlockUndo undo; + CDiskBlockPos pos = pindex->GetUndoPos(); + if (!pos.IsNull()) { + if (!undo.ReadFromDisk(pos, pindex->pprev->GetBlockHash())) + return error("VerifyDB() : *** found bad undo data at %d, hash=%s\n", pindex->nHeight, pindex->GetBlockHash().ToString().c_str()); + } + } + // check level 3: check for inconsistencies during memory-only disconnect of tip blocks + if (nCheckLevel >= 3 && pindex == pindexState && (coins.GetCacheSize() + pcoinsTip->GetCacheSize()) <= 2*nCoinCacheSize + 32000) { + bool fClean = true; + if (!block.DisconnectBlock(pindex, coins, &fClean)) + return error("VerifyDB() : *** irrecoverable inconsistency in block data at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString().c_str()); + pindexState = pindex->pprev; + if (!fClean) { + nGoodTransactions = 0; + pindexFailure = pindex; + } else + nGoodTransactions += block.vtx.size(); } - // TODO: stronger verifications } - if (pindexFork && !fRequestShutdown) - { - // TODO: reorg back - return error("LoadBlockIndex(): chain database corrupted"); + if (pindexFailure) + return error("VerifyDB() : *** coin database inconsistencies found (last %i blocks, %i good transactions before that)\n", pindexBest->nHeight - pindexFailure->nHeight + 1, nGoodTransactions); + + // check level 4: try reconnecting blocks + if (nCheckLevel >= 4) { + CBlockIndex *pindex = pindexState; + while (pindex != pindexBest && !fRequestShutdown) { + pindex = pindex->pnext; + CBlock block; + if (!block.ReadFromDisk(pindex)) + return error("VerifyDB() : *** block.ReadFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString().c_str()); + if (!block.ConnectBlock(pindex, coins)) + return error("VerifyDB() : *** found unconnectable block at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString().c_str()); + } } + printf("No coin database inconsistencies in last %i blocks (%i transactions)\n", pindexBest->nHeight - pindexState->nHeight, nGoodTransactions); + return true; } diff --git a/src/main.h b/src/main.h index 557df8bc5f..2e3e1c5efe 100644 --- a/src/main.h +++ b/src/main.h @@ -126,6 +126,8 @@ FILE* OpenUndoFile(const CDiskBlockPos &pos, bool fReadOnly = false); bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp = NULL); /** Load the block tree and coins database from disk */ bool LoadBlockIndex(); +/** Verify consistency of the block and coin databases */ +bool VerifyDB(); /** Print the loaded block tree */ void PrintBlockTree(); /** Find a block by height in the currently-connected chain */ |