mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-02-22 23:07:59 +01:00
Release cs_main during RewindBlockIndex operation
This commit is contained in:
parent
1d342875c2
commit
436f7d735f
1 changed files with 59 additions and 47 deletions
|
@ -4208,64 +4208,73 @@ void CChainState::EraseBlockData(CBlockIndex* index)
|
||||||
|
|
||||||
bool CChainState::RewindBlockIndex(const CChainParams& params)
|
bool CChainState::RewindBlockIndex(const CChainParams& params)
|
||||||
{
|
{
|
||||||
LOCK(cs_main);
|
|
||||||
// Note that during -reindex-chainstate we are called with an empty chainActive!
|
// 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,
|
// First erase all post-segwit blocks without witness not in the main chain,
|
||||||
// as this can we done without costly DisconnectTip calls. Active
|
// as this can we done without costly DisconnectTip calls. Active
|
||||||
// blocks will be dealt with below.
|
// blocks will be dealt with below (releasing cs_main in between).
|
||||||
for (const auto& entry : mapBlockIndex) {
|
{
|
||||||
if (IsWitnessEnabled(entry.second->pprev, params.GetConsensus()) && !(entry.second->nStatus & BLOCK_OPT_WITNESS) && !chainActive.Contains(entry.second)) {
|
LOCK(cs_main);
|
||||||
EraseBlockData(entry.second);
|
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.
|
// Find what height we need to reorganize to.
|
||||||
|
CBlockIndex *tip;
|
||||||
int nHeight = 1;
|
int nHeight = 1;
|
||||||
while (nHeight <= chainActive.Height()) {
|
{
|
||||||
// Although SCRIPT_VERIFY_WITNESS is now generally enforced on all
|
LOCK(cs_main);
|
||||||
// blocks in ConnectBlock, we don't need to go back and
|
while (nHeight <= chainActive.Height()) {
|
||||||
// re-download/re-verify blocks from before segwit actually activated.
|
// Although SCRIPT_VERIFY_WITNESS is now generally enforced on all
|
||||||
if (IsWitnessEnabled(chainActive[nHeight - 1], params.GetConsensus()) && !(chainActive[nHeight]->nStatus & BLOCK_OPT_WITNESS)) {
|
// blocks in ConnectBlock, we don't need to go back and
|
||||||
break;
|
// 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
|
// nHeight is now the height of the first insufficiently-validated block, or tipheight + 1
|
||||||
|
|
||||||
CValidationState state;
|
CValidationState state;
|
||||||
CBlockIndex* tip = chainActive.Tip();
|
|
||||||
// Loop until the tip is below nHeight, or we reach a pruned block.
|
// Loop until the tip is below nHeight, or we reach a pruned block.
|
||||||
while (true) {
|
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());
|
LOCK(cs_main);
|
||||||
if (tip == nullptr || tip->nHeight < nHeight) break;
|
// Make sure nothing changed from under us (this won't happen because RewindBlockIndex runs before importing/network are active)
|
||||||
if (fPruneMode && !(tip->nStatus & BLOCK_HAVE_DATA)) {
|
assert(tip == chainActive.Tip());
|
||||||
// If pruning, don't try rewinding past the HAVE_DATA point;
|
if (tip == nullptr || tip->nHeight < nHeight) break;
|
||||||
// since older blocks can't be served anyway, there's
|
if (fPruneMode && !(tip->nStatus & BLOCK_HAVE_DATA)) {
|
||||||
// no need to walk further, and trying to DisconnectTip()
|
// If pruning, don't try rewinding past the HAVE_DATA point;
|
||||||
// will fail (and require a needless reindex/redownload
|
// since older blocks can't be served anyway, there's
|
||||||
// of the blockchain).
|
// no need to walk further, and trying to DisconnectTip()
|
||||||
break;
|
// 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));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Disconnect block
|
|
||||||
if (!DisconnectTip(state, params, nullptr)) {
|
|
||||||
return error("RewindBlockIndex: unable to disconnect block at height %i (%s)", tip->nHeight, FormatStateMessage(state));
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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.
|
// Occasionally flush state to disk.
|
||||||
if (!FlushStateToDisk(params, state, FlushStateMode::PERIODIC)) {
|
if (!FlushStateToDisk(params, state, FlushStateMode::PERIODIC)) {
|
||||||
LogPrintf("RewindBlockIndex: unable to flush state to disk (%s)\n", FormatStateMessage(state));
|
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
|
LOCK(cs_main);
|
||||||
// no tip due to chainActive being empty!
|
if (chainActive.Tip() != nullptr) {
|
||||||
PruneBlockIndexCandidates();
|
// 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;
|
return true;
|
||||||
|
|
Loading…
Add table
Reference in a new issue