mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-01-21 07:39:08 +01:00
Merge bitcoin/bitcoin#25574: validation: Improve error handling when VerifyDB dosn't finish successfully
0af16e7134doc: add release note for #25574 (Martin Zumsande)57ef2a4812validation: report if pruning prevents completion of verification (Martin Zumsande)0c7785bb25init, validation: Improve handling if VerifyDB() fails due to insufficient dbcache (Martin Zumsande)d6f781f1cfvalidation: return VerifyDBResult::INTERRUPTED if verification was interrupted (Martin Zumsande)6360b5302dvalidation: Change return value of VerifyDB to enum type (Martin Zumsande) Pull request description: `VerifyDB()` can fail to complete due to insufficient dbcache at the level 3 checks. This PR improves the error handling in this case in the following ways: - The rpc `-verifychain` now returns false if the check can't be completed due to insufficient cache - During init, we only log a warning if the default values for `-checkblocks` and `-checklevel` are taken and the check doesn't complete. However, if the user actively specifies one of these args, we return with an InitError if we can't complete the check. This PR also changes `-verifychain` RPC to return `false` if the verification didn't finish due to missing block data (pruning) or due to being interrupted by the node being shutdown. Previously, this PR also included a fix for a possible assert during verification - this was done in #27009 (now merged). ACKs for top commit: achow101: ACK0af16e7134ryanofsky: Code review ACK0af16e7134. Only small suggested changes since the last review, like renaming some of the enum values. I did leave more suggestions, but they are not very important and could be followups john-moffett: ACK0af16e7134MarcoFalke: lgtm re-ACK0af16e7134🎚 Tree-SHA512: 84b4f767cf9bfbafef362312757c9bf765b41ae3977f4ece840e40c52a2266b1457832df0cdf70440be0aac2168d9b58fc817238630b0b6812f3836ca950bc0e
This commit is contained in:
@@ -4060,7 +4060,7 @@ CVerifyDB::~CVerifyDB()
|
||||
uiInterface.ShowProgress("", 100, false);
|
||||
}
|
||||
|
||||
bool CVerifyDB::VerifyDB(
|
||||
VerifyDBResult CVerifyDB::VerifyDB(
|
||||
Chainstate& chainstate,
|
||||
const Consensus::Params& consensus_params,
|
||||
CCoinsView& coinsview,
|
||||
@@ -4069,7 +4069,7 @@ bool CVerifyDB::VerifyDB(
|
||||
AssertLockHeld(cs_main);
|
||||
|
||||
if (chainstate.m_chain.Tip() == nullptr || chainstate.m_chain.Tip()->pprev == nullptr) {
|
||||
return true;
|
||||
return VerifyDBResult::SUCCESS;
|
||||
}
|
||||
|
||||
// Verify blocks in the best chain
|
||||
@@ -4084,6 +4084,7 @@ bool CVerifyDB::VerifyDB(
|
||||
int nGoodTransactions = 0;
|
||||
BlockValidationState state;
|
||||
int reportDone = 0;
|
||||
bool skipped_no_block_data{false};
|
||||
bool skipped_l3_checks{false};
|
||||
LogPrintf("Verification progress: 0%%\n");
|
||||
|
||||
@@ -4103,25 +4104,29 @@ bool CVerifyDB::VerifyDB(
|
||||
if ((chainstate.m_blockman.IsPruneMode() || is_snapshot_cs) && !(pindex->nStatus & BLOCK_HAVE_DATA)) {
|
||||
// If pruning or running under an assumeutxo snapshot, only go
|
||||
// back as far as we have data.
|
||||
LogPrintf("VerifyDB(): block verification stopping at height %d (pruning, no data)\n", pindex->nHeight);
|
||||
LogPrintf("VerifyDB(): block verification stopping at height %d (no data). This could be due to pruning or use of an assumeutxo snapshot.\n", pindex->nHeight);
|
||||
skipped_no_block_data = true;
|
||||
break;
|
||||
}
|
||||
CBlock block;
|
||||
// check level 0: read from disk
|
||||
if (!ReadBlockFromDisk(block, pindex, consensus_params)) {
|
||||
return error("VerifyDB(): *** ReadBlockFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString());
|
||||
LogPrintf("Verification error: ReadBlockFromDisk failed at %d, hash=%s\n", pindex->nHeight, pindex->GetBlockHash().ToString());
|
||||
return VerifyDBResult::CORRUPTED_BLOCK_DB;
|
||||
}
|
||||
// check level 1: verify block validity
|
||||
if (nCheckLevel >= 1 && !CheckBlock(block, state, consensus_params)) {
|
||||
return error("%s: *** found bad block at %d, hash=%s (%s)\n", __func__,
|
||||
pindex->nHeight, pindex->GetBlockHash().ToString(), state.ToString());
|
||||
LogPrintf("Verification error: found bad block at %d, hash=%s (%s)\n",
|
||||
pindex->nHeight, pindex->GetBlockHash().ToString(), state.ToString());
|
||||
return VerifyDBResult::CORRUPTED_BLOCK_DB;
|
||||
}
|
||||
// check level 2: verify undo validity
|
||||
if (nCheckLevel >= 2 && pindex) {
|
||||
CBlockUndo undo;
|
||||
if (!pindex->GetUndoPos().IsNull()) {
|
||||
if (!UndoReadFromDisk(undo, pindex)) {
|
||||
return error("VerifyDB(): *** found bad undo data at %d, hash=%s\n", pindex->nHeight, pindex->GetBlockHash().ToString());
|
||||
LogPrintf("Verification error: found bad undo data at %d, hash=%s\n", pindex->nHeight, pindex->GetBlockHash().ToString());
|
||||
return VerifyDBResult::CORRUPTED_BLOCK_DB;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4133,7 +4138,8 @@ bool CVerifyDB::VerifyDB(
|
||||
assert(coins.GetBestBlock() == pindex->GetBlockHash());
|
||||
DisconnectResult res = chainstate.DisconnectBlock(block, pindex, coins);
|
||||
if (res == DISCONNECT_FAILED) {
|
||||
return error("VerifyDB(): *** irrecoverable inconsistency in block data at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString());
|
||||
LogPrintf("Verification error: irrecoverable inconsistency in block data at %d, hash=%s\n", pindex->nHeight, pindex->GetBlockHash().ToString());
|
||||
return VerifyDBResult::CORRUPTED_BLOCK_DB;
|
||||
}
|
||||
if (res == DISCONNECT_UNCLEAN) {
|
||||
nGoodTransactions = 0;
|
||||
@@ -4145,14 +4151,16 @@ bool CVerifyDB::VerifyDB(
|
||||
skipped_l3_checks = true;
|
||||
}
|
||||
}
|
||||
if (ShutdownRequested()) return true;
|
||||
if (ShutdownRequested()) return VerifyDBResult::INTERRUPTED;
|
||||
}
|
||||
if (pindexFailure) {
|
||||
return error("VerifyDB(): *** coin database inconsistencies found (last %i blocks, %i good transactions before that)\n", chainstate.m_chain.Height() - pindexFailure->nHeight + 1, nGoodTransactions);
|
||||
LogPrintf("Verification error: coin database inconsistencies found (last %i blocks, %i good transactions before that)\n", chainstate.m_chain.Height() - pindexFailure->nHeight + 1, nGoodTransactions);
|
||||
return VerifyDBResult::CORRUPTED_BLOCK_DB;
|
||||
}
|
||||
if (skipped_l3_checks) {
|
||||
LogPrintf("Skipped verification of level >=3 (insufficient database cache size). Consider increasing -dbcache.\n");
|
||||
}
|
||||
|
||||
// store block count as we move pindex at check level >= 4
|
||||
int block_count = chainstate.m_chain.Height() - pindex->nHeight;
|
||||
|
||||
@@ -4168,18 +4176,27 @@ bool CVerifyDB::VerifyDB(
|
||||
uiInterface.ShowProgress(_("Verifying blocks…").translated, percentageDone, false);
|
||||
pindex = chainstate.m_chain.Next(pindex);
|
||||
CBlock block;
|
||||
if (!ReadBlockFromDisk(block, pindex, consensus_params))
|
||||
return error("VerifyDB(): *** ReadBlockFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString());
|
||||
if (!chainstate.ConnectBlock(block, state, pindex, coins)) {
|
||||
return error("VerifyDB(): *** found unconnectable block at %d, hash=%s (%s)", pindex->nHeight, pindex->GetBlockHash().ToString(), state.ToString());
|
||||
if (!ReadBlockFromDisk(block, pindex, consensus_params)) {
|
||||
LogPrintf("Verification error: ReadBlockFromDisk failed at %d, hash=%s\n", pindex->nHeight, pindex->GetBlockHash().ToString());
|
||||
return VerifyDBResult::CORRUPTED_BLOCK_DB;
|
||||
}
|
||||
if (ShutdownRequested()) return true;
|
||||
if (!chainstate.ConnectBlock(block, state, pindex, coins)) {
|
||||
LogPrintf("Verification error: found unconnectable block at %d, hash=%s (%s)\n", pindex->nHeight, pindex->GetBlockHash().ToString(), state.ToString());
|
||||
return VerifyDBResult::CORRUPTED_BLOCK_DB;
|
||||
}
|
||||
if (ShutdownRequested()) return VerifyDBResult::INTERRUPTED;
|
||||
}
|
||||
}
|
||||
|
||||
LogPrintf("Verification: No coin database inconsistencies in last %i blocks (%i transactions)\n", block_count, nGoodTransactions);
|
||||
|
||||
return true;
|
||||
if (skipped_l3_checks) {
|
||||
return VerifyDBResult::SKIPPED_L3_CHECKS;
|
||||
}
|
||||
if (skipped_no_block_data) {
|
||||
return VerifyDBResult::SKIPPED_MISSING_BLOCKS;
|
||||
}
|
||||
return VerifyDBResult::SUCCESS;
|
||||
}
|
||||
|
||||
/** Apply the effects of a block on the utxo cache, ignoring that it may already have been applied. */
|
||||
|
||||
Reference in New Issue
Block a user