diff --git a/src/init.cpp b/src/init.cpp index a4ad2771cc..4e61e030f7 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -1770,7 +1770,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) node.background_init_thread = std::thread(&util::TraceThread, "initload", [=, &chainman, &args, &node] { ScheduleBatchPriority(); - // Import blocks + // Import blocks and ActivateBestChain() ImportBlocks(chainman, vImportFiles); if (args.GetBoolArg("-stopafterblockimport", DEFAULT_STOPAFTERBLOCKIMPORT)) { LogPrintf("Stopping after block import\n"); @@ -1793,8 +1793,18 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) } }); - // Wait for genesis block to be processed - if (WITH_LOCK(chainman.GetMutex(), return chainman.ActiveTip() == nullptr)) { + /* + * Wait for genesis block to be processed. Typically kernel_notifications.m_tip_block + * has already been set by a call to LoadChainTip() in CompleteChainstateInitialization(). + * But this is skipped if the chainstate doesn't exist yet or is being wiped: + * + * 1. first startup with an empty datadir + * 2. reindex + * 3. reindex-chainstate + * + * In these case it's connected by a call to ActivateBestChain() in the initload thread. + */ + { WAIT_LOCK(kernel_notifications.m_tip_block_mutex, lock); kernel_notifications.m_tip_block_cv.wait(lock, [&]() EXCLUSIVE_LOCKS_REQUIRED(kernel_notifications.m_tip_block_mutex) { return !kernel_notifications.m_tip_block.IsNull() || ShutdownRequested(node); diff --git a/src/interfaces/mining.h b/src/interfaces/mining.h index c77f3c30a2..6f23bf3bb5 100644 --- a/src/interfaces/mining.h +++ b/src/interfaces/mining.h @@ -75,8 +75,8 @@ public: virtual std::optional getTip() = 0; /** - * Waits for the connected tip to change. If the tip was not connected on - * startup, this will wait. + * Waits for the connected tip to change. During node initialization, this will + * wait until the tip is connected. * * @param[in] current_tip block hash of the current chain tip. Function waits * for the chain tip to differ from this. diff --git a/src/node/blockstorage.h b/src/node/blockstorage.h index 03bc5f4600..ac6db0558d 100644 --- a/src/node/blockstorage.h +++ b/src/node/blockstorage.h @@ -430,6 +430,7 @@ public: void CleanupBlockRevFiles() const; }; +// Calls ActivateBestChain() even if no blocks are imported. void ImportBlocks(ChainstateManager& chainman, std::span import_paths); } // namespace node diff --git a/src/node/kernel_notifications.h b/src/node/kernel_notifications.h index 296b9c426d..35070b5285 100644 --- a/src/node/kernel_notifications.h +++ b/src/node/kernel_notifications.h @@ -56,9 +56,9 @@ public: Mutex m_tip_block_mutex; std::condition_variable m_tip_block_cv GUARDED_BY(m_tip_block_mutex); - //! The block for which the last blockTip notification was received for. - //! The initial ZERO means that no block has been connected yet, which may - //! be true even long after startup, until shutdown. + //! The block for which the last blockTip notification was received. + //! It's first set when the tip is connected during node initialization. + //! Might be unset during an early shutdown. uint256 m_tip_block GUARDED_BY(m_tip_block_mutex){uint256::ZERO}; private: diff --git a/src/validation.cpp b/src/validation.cpp index 0227ccec2f..aff8fe7024 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -4721,6 +4721,13 @@ bool Chainstate::LoadChainTip() m_chain.Height(), FormatISO8601DateTime(tip->GetBlockTime()), GuessVerificationProgress(m_chainman.GetParams().TxData(), tip)); + + // Ensure KernelNotifications m_tip_block is set even if no new block arrives. + if (this->GetRole() != ChainstateRole::BACKGROUND) { + // Ignoring return value for now. + (void)m_chainman.GetNotifications().blockTip(GetSynchronizationState(/*init=*/true, m_chainman.m_blockman.m_blockfiles_indexed), *pindex); + } + return true; }