diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1597,15 +1597,15 @@ BlockHash hash(uint256S(strHash)); CValidationState state; + CBlockIndex *pblockindex; { LOCK(cs_main); - CBlockIndex *pblockindex = LookupBlockIndex(hash); + pblockindex = LookupBlockIndex(hash); if (!pblockindex) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); } - - FinalizeBlockAndInvalidate(config, state, pblockindex); } + FinalizeBlockAndInvalidate(config, state, pblockindex); if (state.IsValid()) { ActivateBestChain(config, state); diff --git a/src/validation.h b/src/validation.h --- a/src/validation.h +++ b/src/validation.h @@ -639,8 +639,7 @@ * A finalized block can not be reorged in any way. */ bool FinalizeBlockAndInvalidate(const Config &config, CValidationState &state, - CBlockIndex *pindex) - EXCLUSIVE_LOCKS_REQUIRED(cs_main); + CBlockIndex *pindex); /** Mark a block as invalid. */ bool InvalidateBlock(const Config &config, CValidationState &state, diff --git a/src/validation.cpp b/src/validation.cpp --- a/src/validation.cpp +++ b/src/validation.cpp @@ -2822,6 +2822,14 @@ } } +static void LimitValidationInterfaceQueue() { + AssertLockNotHeld(cs_main); + + if (GetMainSignals().CallbacksPending() > 10) { + SyncWithValidationInterfaceQueue(); + } +} + /** * Make the best chain active, in multiple steps. The result is either failure * or an activated best chain. pblock is either nullptr or a pointer to a block @@ -2856,15 +2864,13 @@ do { boost::this_thread::interruption_point(); - if (GetMainSignals().CallbacksPending() > 10) { - // Block until the validation queue drains. This should largely - // never happen in normal operation, however may happen during - // reindex, causing memory blowup if we run too far ahead. - // Note that if a validationinterface callback ends up calling - // ActivateBestChain this may lead to a deadlock! We should - // probably have a DEBUG_LOCKORDER test for this in the future. - SyncWithValidationInterfaceQueue(); - } + // Block until the validation queue drains. This should largely + // never happen in normal operation, however may happen during + // reindex, causing memory blowup if we run too far ahead. + // Note that if a validationinterface callback ends up calling + // ActivateBestChain this may lead to a deadlock! We should + // probably have a DEBUG_LOCKORDER test for this in the future. + LimitValidationInterfaceQueue(); { LOCK(cs_main); @@ -3028,6 +3034,9 @@ break; } + // Make sure the queue of validation callbacks doesn't grow unboundedly. + LimitValidationInterfaceQueue(); + LOCK(cs_main); if (!chainActive.Contains(pindex)) { @@ -3056,6 +3065,7 @@ return false; } + if (!ret) return false; assert(invalid_walk_tip->pprev == chainActive.Tip()); // We immediately mark the disconnected blocks as invalid. @@ -3065,21 +3075,35 @@ // nStatus" criteria for inclusion in setBlockIndexCandidates). invalid_walk_tip->nStatus = - invalidate ? invalid_walk_tip->nStatus.withFailedParent() - : invalid_walk_tip->nStatus.withParkedParent(); + invalidate ? invalid_walk_tip->nStatus.withFailed() + : invalid_walk_tip->nStatus.withParked(); setDirtyBlockIndex.insert(invalid_walk_tip); setBlockIndexCandidates.insert(invalid_walk_tip->pprev); - // If we abort invalidation after this iteration, make sure - // the last disconnected block gets marked failed (rather than - // just child of failed) + if (invalid_walk_tip->pprev == to_mark_failed_or_parked && + (invalidate ? to_mark_failed_or_parked->nStatus.hasFailed() + : to_mark_failed_or_parked->nStatus.isParked())) { + // We only want to mark the last disconnected block as + // BLOCK_FAILED_VALID; its children need to be BLOCK_FAILED_CHILD + // instead. + to_mark_failed_or_parked->nStatus = + (invalidate + ? to_mark_failed_or_parked->nStatus.withFailed(false) + .withFailedParent() + : to_mark_failed_or_parked->nStatus.withParked(false) + .withParkedParent()); + + setDirtyBlockIndex.insert(to_mark_failed_or_parked); + } + + // Track the last disconnected block, so we can correct its + // BLOCK_FAILED_CHILD status in future iterations, or, if it's the last + // one, call InvalidChainFound on it. to_mark_failed_or_parked = invalid_walk_tip; } { - // Mark pindex (or the last disconnected block) as invalid or parked, - // regardless of whether it was in the main chain or not. LOCK(cs_main); if (chainActive.Contains(to_mark_failed_or_parked)) { // If the to-be-marked invalid block is in the active chain, @@ -3087,6 +3111,8 @@ return false; } + // Mark pindex (or the last disconnected block) as invalid (or parked), + // even when it never was in the main chain. to_mark_failed_or_parked->nStatus = invalidate ? to_mark_failed_or_parked->nStatus.withFailed() : to_mark_failed_or_parked->nStatus.withParked(); @@ -3122,26 +3148,31 @@ bool FinalizeBlockAndInvalidate(const Config &config, CValidationState &state, CBlockIndex *pindex) { - AssertLockHeld(cs_main); - if (!FinalizeBlockInternal(config, state, pindex)) { - // state is set by FinalizeBlockInternal. - return false; - } + CBlockIndex *pindexToInvalidate; - // We have a valid candidate, make sure it is not parked. - if (pindex->nStatus.isOnParkedChain()) { - UnparkBlock(pindex); - } + { + LOCK(cs_main); + if (!FinalizeBlockInternal(config, state, pindex)) { + // state is set by FinalizeBlockInternal. + return false; + } + + // We have a valid candidate, make sure it is not parked. + if (pindex->nStatus.isOnParkedChain()) { + UnparkBlock(pindex); + } + + // If the finalized block is not on the active chain, we need to rewind. + if (AreOnTheSameFork(pindex, chainActive.Tip())) { + return true; + } - // If the finalized block is not on the active chain, we need to rewind. - if (!AreOnTheSameFork(pindex, chainActive.Tip())) { const CBlockIndex *pindexFork = chainActive.FindFork(pindex); - CBlockIndex *pindexToInvalidate = + pindexToInvalidate = chainActive.Tip()->GetAncestor(pindexFork->nHeight + 1); - return InvalidateBlock(config, state, pindexToInvalidate); } - return true; + return InvalidateBlock(config, state, pindexToInvalidate); } bool InvalidateBlock(const Config &config, CValidationState &state,