diff --git a/src/interfaces/mining.h b/src/interfaces/mining.h index ebda49bcc95..421c5ba762f 100644 --- a/src/interfaces/mining.h +++ b/src/interfaces/mining.h @@ -12,6 +12,7 @@ #include // for CTransactionRef #include // for int64_t #include // for uint256 +#include // for MillisecondsDouble #include // for unique_ptr, shared_ptr #include // for optional @@ -60,6 +61,17 @@ public: virtual std::optional getTip() = 0; /** + * Waits for the tip to change + * + * @param[in] current_tip block hash of the current chain tip. Function waits + * for the chain tip to change if this matches, otherwise + * it returns right away. + * @param[in] timeout how long to wait for a new tip + * @returns Hash and height of the current chain tip after this call. + */ + virtual BlockRef waitTipChanged(uint256 current_tip, MillisecondsDouble timeout = MillisecondsDouble::max()) = 0; + + /** * Construct a new block template * * @param[in] script_pub_key the coinbase output diff --git a/src/node/interfaces.cpp b/src/node/interfaces.cpp index 0b7685aa873..8d32ff5c2c2 100644 --- a/src/node/interfaces.cpp +++ b/src/node/interfaces.cpp @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -935,6 +936,27 @@ public: return BlockRef{tip->GetBlockHash(), tip->nHeight}; } + BlockRef waitTipChanged(uint256 current_tip, MillisecondsDouble timeout) override + { + // Interrupt check interval + const MillisecondsDouble tick{1000}; + auto now{std::chrono::steady_clock::now()}; + auto deadline = now + timeout; + // std::chrono does not check against overflow + if (deadline < now) deadline = std::chrono::steady_clock::time_point::max(); + { + WAIT_LOCK(notifications().m_tip_block_mutex, lock); + while ((notifications().m_tip_block == uint256() || notifications().m_tip_block == current_tip) && !chainman().m_interrupt) { + now = std::chrono::steady_clock::now(); + if (now >= deadline) break; + notifications().m_tip_block_cv.wait_until(lock, std::min(deadline, now + tick)); + } + } + // Must release m_tip_block_mutex before locking cs_main, to avoid deadlocks. + LOCK(::cs_main); + return BlockRef{chainman().ActiveChain().Tip()->GetBlockHash(), chainman().ActiveChain().Tip()->nHeight}; + } + bool processNewBlock(const std::shared_ptr& block, bool* new_block) override { return chainman().ProcessNewBlock(block, /*force_processing=*/true, /*min_pow_checked=*/true, /*new_block=*/new_block); @@ -967,6 +989,7 @@ public: NodeContext* context() override { return &m_node; } ChainstateManager& chainman() { return *Assert(m_node.chainman); } + KernelNotifications& notifications() { return *Assert(m_node.notifications); } NodeContext& m_node; }; } // namespace