diff --git a/src/validation.cpp b/src/validation.cpp --- a/src/validation.cpp +++ b/src/validation.cpp @@ -2796,66 +2796,75 @@ SyncWithValidationInterfaceQueue(); } - const CBlockIndex *pindexFork; - bool fInitialDownload; { LOCK(cs_main); + 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). + + // Destructed before cs_main is unlocked + ConnectTrace connectTrace(g_mempool); + + if (pindexMostWork == nullptr) { + pindexMostWork = FindMostWorkChain(); + } - // Destructed before cs_main is unlocked. - ConnectTrace connectTrace(g_mempool); - - CBlockIndex *pindexOldTip = chainActive.Tip(); - if (pindexMostWork == nullptr) { - pindexMostWork = FindMostWorkChain(); - } - - // Whether we have anything to do at all. - if (pindexMostWork == nullptr || - pindexMostWork == chainActive.Tip()) { - return true; - } + // Whether we have anything to do at all. + if (pindexMostWork == nullptr || + pindexMostWork == chainActive.Tip()) { + break; + } - bool fInvalidFound = false; - std::shared_ptr nullBlockPtr; - if (!ActivateBestChainStep( - config, state, pindexMostWork, - pblock && - pblock->GetHash() == pindexMostWork->GetBlockHash() - ? pblock - : nullBlockPtr, - fInvalidFound, connectTrace)) { - return false; - } + bool fInvalidFound = false; + std::shared_ptr nullBlockPtr; + if (!ActivateBestChainStep( + config, state, pindexMostWork, + pblock && pblock->GetHash() == + pindexMostWork->GetBlockHash() + ? pblock + : nullBlockPtr, + fInvalidFound, connectTrace)) { + return false; + } + blocks_connected = true; - if (fInvalidFound) { - // Wipe cache, we may need another branch now. - pindexMostWork = nullptr; - } + if (fInvalidFound) { + // Wipe cache, we may need another branch now. + pindexMostWork = nullptr; + } - pindexNewTip = chainActive.Tip(); - pindexFork = chainActive.FindFork(pindexOldTip); - fInitialDownload = IsInitialBlockDownload(); + pindexNewTip = chainActive.Tip(); + 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))); - for (const PerBlockConnectTrace &trace : - connectTrace.GetBlocksConnected()) { - assert(trace.pblock && trace.pindex); - GetMainSignals().BlockConnected(trace.pblock, trace.pindex, - trace.conflictedTxs); + if (!blocks_connected) { + return true; } - } - // When we reach this point, we switched to a new tip (stored in - // pindexNewTip). + const CBlockIndex *pindexFork = chainActive.FindFork(starting_tip); + bool fInitialDownload = IsInitialBlockDownload(); - // Notifications/callbacks that can run without cs_main + // 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 + if (pindexFork != pindexNewTip) { + // Notify ValidationInterface subscribers + GetMainSignals().UpdatedBlockTip(pindexNewTip, pindexFork, + fInitialDownload); - // Notify external listeners about the new tip. - GetMainSignals().UpdatedBlockTip(pindexNewTip, pindexFork, - fInitialDownload); - - // Always notify the UI if a new block tip was connected - if (pindexFork != pindexNewTip) { - uiInterface.NotifyBlockTip(fInitialDownload, pindexNewTip); + // Always notify the UI if a new block tip was connected + uiInterface.NotifyBlockTip(fInitialDownload, pindexNewTip); + } } if (nStopAtHeight && pindexNewTip && diff --git a/src/validationinterface.h b/src/validationinterface.h --- a/src/validationinterface.h +++ b/src/validationinterface.h @@ -77,7 +77,11 @@ */ ~CValidationInterface() = default; /** - * 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. */