Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

0.18: Granular invalidateblock and RewindBlockIndex #15552

Merged
merged 11 commits into from Mar 7, 2019

Release cs_main during RewindBlockIndex operation

  • Loading branch information...
sipa committed Feb 13, 2019
commit 436f7d735f1c37e77d42ff59d4cbb1bd76d5fcfb
@@ -4208,77 +4208,89 @@ 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));
return false;
}
}

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;
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.