Skip to content

Commit

Permalink
Do not unlock cs_main in ABC unless we've actually made progress.
Browse files Browse the repository at this point in the history
Technically, some internal datastructures may be in an inconsistent
state if we do this, though there are no known bugs there. Still,
for future safety, its much better to only unlock cs_main if we've
made progress (not just tried a reorg which may make progress).
  • Loading branch information
TheBlueMatt authored and furszy committed Apr 3, 2021
1 parent 8d15cf5 commit 198f435
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 29 deletions.
64 changes: 36 additions & 28 deletions src/validation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2238,45 +2238,53 @@ bool ActivateBestChain(CValidationState& state, std::shared_ptr<const CBlock> pb
SyncWithValidationInterfaceQueue();
}

const CBlockIndex *pindexFork;
bool fInitialDownload;
{
LOCK(cs_main);
ConnectTrace connectTrace(mempool); // Destructed before cs_main is unlocked
CBlockIndex* starting_tip = chainActive.Tip();
bool blocks_connected = false;
do {
// We absolutely may not unlock cs_main until we've made forward progress
// (with the exception of shutdown due to hardware issues, low disk space, etc).
ConnectTrace connectTrace(mempool); // Destructed before cs_main is unlocked

if (pindexMostWork == nullptr) {
pindexMostWork = FindMostWorkChain();
}

CBlockIndex *pindexOldTip = chainActive.Tip();
if (pindexMostWork == nullptr) {
pindexMostWork = FindMostWorkChain();
}
// Whether we have anything to do at all.
if (pindexMostWork == nullptr || pindexMostWork == chainActive.Tip()) {
break;
}

// Whether we have anything to do at all.
if (pindexMostWork == nullptr || pindexMostWork == chainActive.Tip())
return true;
bool fInvalidFound = false;
std::shared_ptr<const CBlock> nullBlockPtr;
if (!ActivateBestChainStep(state, pindexMostWork, pblock && pblock->GetHash() == pindexMostWork->GetBlockHash() ? pblock : nullBlockPtr, fAlreadyChecked, fInvalidFound, connectTrace))
return false;
blocks_connected = true;

bool fInvalidFound = false;
std::shared_ptr<const CBlock> nullBlockPtr;
if (!ActivateBestChainStep(state, pindexMostWork, pblock && pblock->GetHash() == pindexMostWork->GetBlockHash() ? pblock : nullBlockPtr, fAlreadyChecked, fInvalidFound, connectTrace))
return false;
if (fInvalidFound) {
// Wipe cache, we may need another branch now.
pindexMostWork = nullptr;
}
pindexNewTip = chainActive.Tip();

if (fInvalidFound) {
// Wipe cache, we may need another branch now.
pindexMostWork = nullptr;
}
pindexNewTip = chainActive.Tip();
pindexFork = chainActive.FindFork(pindexOldTip);
fInitialDownload = IsInitialBlockDownload();
for (const PerBlockConnectTrace& trace : connectTrace.GetBlocksConnected()) {
assert(trace.pblock && trace.pindex);
GetMainSignals().BlockConnected(trace.pblock, trace.pindex, trace.conflictedTxs);
}
} while (!chainActive.Tip() || (starting_tip && CBlockIndexWorkComparator()(chainActive.Tip(), starting_tip)));
if (!blocks_connected) return true;

for (const PerBlockConnectTrace& trace : connectTrace.GetBlocksConnected()) {
assert(trace.pblock && trace.pindex);
GetMainSignals().BlockConnected(trace.pblock, trace.pindex, trace.conflictedTxs);
}
const CBlockIndex* pindexFork = chainActive.FindFork(starting_tip);
bool fInitialDownload = IsInitialBlockDownload();

// Notify external listeners about the new tip.
// Enqueue while holding cs_main to ensure that UpdatedBlockTip is called in the order in which blocks are connected
GetMainSignals().UpdatedBlockTip(pindexNewTip, pindexFork, fInitialDownload);

// Always notify the UI if a new block tip was connected
if (pindexFork != pindexNewTip) {
// Notify ValidationInterface subscribers
GetMainSignals().UpdatedBlockTip(pindexNewTip, pindexFork, fInitialDownload);

// Always notify the UI if a new block tip was connected
uiInterface.NotifyBlockTip(fInitialDownload, pindexNewTip);
}
}
Expand Down
6 changes: 5 additions & 1 deletion src/validationinterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,11 @@ class CValidationInterface {
virtual ~CValidationInterface() = default;
protected:
/**
* Notifies listeners of updated block chain tip
* Notifies listeners when the block chain tip advances.
*
* When multiple blocks are connected at once, UpdatedBlockTip will be called on the final tip
* but may not be called on every intermediate tip. If the latter behavior is desired,
* subscribe to BlockConnected() instead.
*
* Called on a background thread.
*/
Expand Down

0 comments on commit 198f435

Please sign in to comment.