From 436f7d735f1c37e77d42ff59d4cbb1bd76d5fcfb Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Wed, 13 Feb 2019 15:32:34 -0800 Subject: Release cs_main during RewindBlockIndex operation --- src/validation.cpp | 102 ++++++++++++++++++++++++++++++----------------------- 1 file changed, 57 insertions(+), 45 deletions(-) (limited to 'src/validation.cpp') diff --git a/src/validation.cpp b/src/validation.cpp index 040994c7ce..da92a1d1ca 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -4208,64 +4208,73 @@ void CChainState::EraseBlockData(CBlockIndex* index) bool CChainState::RewindBlockIndex(const CChainParams& params) { - LOCK(cs_main); // Note that during -reindex-chainstate we are called with an empty chainActive! // First erase all post-segwit blocks without witness not in the main chain, // as this can we done without costly DisconnectTip calls. Active - // blocks will be dealt with below. - for (const auto& entry : mapBlockIndex) { - if (IsWitnessEnabled(entry.second->pprev, params.GetConsensus()) && !(entry.second->nStatus & BLOCK_OPT_WITNESS) && !chainActive.Contains(entry.second)) { - EraseBlockData(entry.second); + // blocks will be dealt with below (releasing cs_main in between). + { + LOCK(cs_main); + for (const auto& entry : mapBlockIndex) { + if (IsWitnessEnabled(entry.second->pprev, params.GetConsensus()) && !(entry.second->nStatus & BLOCK_OPT_WITNESS) && !chainActive.Contains(entry.second)) { + EraseBlockData(entry.second); + } } } // Find what height we need to reorganize to. + CBlockIndex *tip; int nHeight = 1; - while (nHeight <= chainActive.Height()) { - // Although SCRIPT_VERIFY_WITNESS is now generally enforced on all - // blocks in ConnectBlock, we don't need to go back and - // re-download/re-verify blocks from before segwit actually activated. - if (IsWitnessEnabled(chainActive[nHeight - 1], params.GetConsensus()) && !(chainActive[nHeight]->nStatus & BLOCK_OPT_WITNESS)) { - break; + { + LOCK(cs_main); + while (nHeight <= chainActive.Height()) { + // Although SCRIPT_VERIFY_WITNESS is now generally enforced on all + // blocks in ConnectBlock, we don't need to go back and + // re-download/re-verify blocks from before segwit actually activated. + if (IsWitnessEnabled(chainActive[nHeight - 1], params.GetConsensus()) && !(chainActive[nHeight]->nStatus & BLOCK_OPT_WITNESS)) { + break; + } + nHeight++; } - nHeight++; - } + tip = chainActive.Tip(); + } // nHeight is now the height of the first insufficiently-validated block, or tipheight + 1 + CValidationState state; - CBlockIndex* tip = chainActive.Tip(); // Loop until the tip is below nHeight, or we reach a pruned block. while (true) { - // Make sure nothing changed from under us (this won't happen because RewindBlockIndex runs before importing/network are active) - assert(tip == chainActive.Tip()); - if (tip == nullptr || tip->nHeight < nHeight) break; - if (fPruneMode && !(tip->nStatus & BLOCK_HAVE_DATA)) { - // If pruning, don't try rewinding past the HAVE_DATA point; - // since older blocks can't be served anyway, there's - // no need to walk further, and trying to DisconnectTip() - // will fail (and require a needless reindex/redownload - // of the blockchain). - break; - } - - // Disconnect block - if (!DisconnectTip(state, params, nullptr)) { - return error("RewindBlockIndex: unable to disconnect block at height %i (%s)", tip->nHeight, FormatStateMessage(state)); - } + { + LOCK(cs_main); + // Make sure nothing changed from under us (this won't happen because RewindBlockIndex runs before importing/network are active) + assert(tip == chainActive.Tip()); + if (tip == nullptr || tip->nHeight < nHeight) break; + if (fPruneMode && !(tip->nStatus & BLOCK_HAVE_DATA)) { + // If pruning, don't try rewinding past the HAVE_DATA point; + // since older blocks can't be served anyway, there's + // no need to walk further, and trying to DisconnectTip() + // will fail (and require a needless reindex/redownload + // of the blockchain). + break; + } - // Reduce validity flag and have-data flags. - // We do this after actual disconnecting, otherwise we'll end up writing the lack of data - // to disk before writing the chainstate, resulting in a failure to continue if interrupted. - // Note: If we encounter an insufficiently validated block that - // is on chainActive, it must be because we are a pruning node, and - // this block or some successor doesn't HAVE_DATA, so we were unable to - // rewind all the way. Blocks remaining on chainActive at this point - // must not have their validity reduced. - EraseBlockData(tip); + // Disconnect block + if (!DisconnectTip(state, params, nullptr)) { + return error("RewindBlockIndex: unable to disconnect block at height %i (%s)", tip->nHeight, FormatStateMessage(state)); + } - tip = tip->pprev; + // Reduce validity flag and have-data flags. + // We do this after actual disconnecting, otherwise we'll end up writing the lack of data + // to disk before writing the chainstate, resulting in a failure to continue if interrupted. + // Note: If we encounter an insufficiently validated block that + // is on chainActive, it must be because we are a pruning node, and + // this block or some successor doesn't HAVE_DATA, so we were unable to + // rewind all the way. Blocks remaining on chainActive at this point + // must not have their validity reduced. + EraseBlockData(tip); + tip = tip->pprev; + } // Occasionally flush state to disk. if (!FlushStateToDisk(params, state, FlushStateMode::PERIODIC)) { LogPrintf("RewindBlockIndex: unable to flush state to disk (%s)\n", FormatStateMessage(state)); @@ -4273,12 +4282,15 @@ bool CChainState::RewindBlockIndex(const CChainParams& params) } } - if (chainActive.Tip() != nullptr) { - // We can't prune block index candidates based on our tip if we have - // no tip due to chainActive being empty! - PruneBlockIndexCandidates(); + { + LOCK(cs_main); + if (chainActive.Tip() != nullptr) { + // We can't prune block index candidates based on our tip if we have + // no tip due to chainActive being empty! + PruneBlockIndexCandidates(); - CheckBlockIndex(params.GetConsensus()); + CheckBlockIndex(params.GetConsensus()); + } } return true; -- cgit v1.2.3