diff --git a/src/interfaces/chain.h b/src/interfaces/chain.h --- a/src/interfaces/chain.h +++ b/src/interfaces/chain.h @@ -46,12 +46,6 @@ //! asynchronously //! (https://github.com/bitcoin/bitcoin/pull/10973#issuecomment-380101269). //! -//! * The isPotentialTip() and waitForNotifications() methods are too low-level -//! and should be replaced with a higher level -//! waitForNotificationsUpTo(block_hash) method that the wallet can call -//! instead -//! (https://github.com/bitcoin/bitcoin/pull/10973#discussion_r266995234). -//! //! * The relayTransactions() and submitToMemoryPool() methods could be replaced //! with a higher-level broadcastTransaction method //! (https://github.com/bitcoin/bitcoin/pull/14978#issuecomment-459373984). @@ -129,11 +123,6 @@ virtual Optional findFork(const BlockHash &hash, Optional *height) = 0; - //! Return true if block hash points to the current chain tip, or to a - //! possible descendant of the current chain tip that isn't currently - //! connected. - virtual bool isPotentialTip(const BlockHash &hash) = 0; - //! Get locator for the current chain tip. virtual CBlockLocator getTipLocator() = 0; @@ -260,8 +249,11 @@ virtual std::unique_ptr handleNotifications(Notifications ¬ifications) = 0; - //! Wait for pending notifications to be handled. - virtual void waitForNotifications() = 0; + //! Wait for pending notifications to be processed unless block hash points + //! to the current chain tip, or to a possible descendant of the current + //! chain tip that isn't currently connected. + virtual void + waitForNotificationsIfNewBlocksConnected(const BlockHash &old_tip) = 0; //! Register handler for RPC. Command is not copied, so reference //! needs to remain valid until Handler is disconnected. diff --git a/src/interfaces/chain.cpp b/src/interfaces/chain.cpp --- a/src/interfaces/chain.cpp +++ b/src/interfaces/chain.cpp @@ -119,14 +119,6 @@ } return nullopt; } - bool isPotentialTip(const BlockHash &hash) override { - if (::ChainActive().Tip()->GetBlockHash() == hash) { - return true; - } - CBlockIndex *block = LookupBlockIndex(hash); - return block && block->GetAncestor(::ChainActive().Height()) == - ::ChainActive().Tip(); - } CBlockLocator getTipLocator() override { return ::ChainActive().GetLocator(); } @@ -358,9 +350,22 @@ return std::make_unique(*this, notifications); } - void waitForNotifications() override { + void waitForNotificationsIfNewBlocksConnected( + const BlockHash &old_tip) override { + if (!old_tip.IsNull()) { + LOCK(::cs_main); + if (old_tip == ::ChainActive().Tip()->GetBlockHash()) { + return; + } + CBlockIndex *block = LookupBlockIndex(old_tip); + if (block && block->GetAncestor(::ChainActive().Height()) == + ::ChainActive().Tip()) { + return; + } + } SyncWithValidationInterfaceQueue(); } + std::unique_ptr handleRpc(const CRPCCommand &command) override { return std::make_unique(command); diff --git a/src/sync.h b/src/sync.h --- a/src/sync.h +++ b/src/sync.h @@ -197,6 +197,20 @@ LeaveCritical(); \ } +//! Run code while locking a mutex. +//! +//! Examples: +//! +//! WITH_LOCK(cs, shared_val = shared_val + 1); +//! +//! int val = WITH_LOCK(cs, return shared_val); +//! +#define WITH_LOCK(cs, code) \ + [&] { \ + LOCK(cs); \ + code; \ + }() + class CSemaphore { private: std::condition_variable condition; diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -791,7 +791,7 @@ * to have seen all transactions in the chain, but is only used to track * live BlockConnected callbacks. */ - BlockHash m_last_block_processed; + BlockHash m_last_block_processed GUARDED_BY(cs_wallet); public: const CChainParams &chainParams; diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -1418,24 +1418,13 @@ void CWallet::BlockUntilSyncedToCurrentChain() { AssertLockNotHeld(cs_wallet); - - { - // Skip the queue-draining stuff if we know we're caught up with - // ::ChainActive().Tip()... - // We could also take cs_wallet here, and call m_last_block_processed - // protected by cs_wallet instead of cs_main, but as long as we need - // cs_main here anyway, it's easier to just call it cs_main-protected. - auto locked_chain = chain().lock(); - if (!m_last_block_processed.IsNull() && - locked_chain->isPotentialTip(m_last_block_processed)) { - return; - } - } - - // ...otherwise put a callback in the validation interface queue and wait - // for the queue to drain enough to execute it (indicating we are caught up - // at least with the time we entered this function). - chain().waitForNotifications(); + // Skip the queue-draining stuff if we know we're caught up with + // chainActive.Tip(), otherwise put a callback in the validation interface + // queue and wait for the queue to drain enough to execute it (indicating we + // are caught up at least with the time we entered this function). + const BlockHash last_block_hash = + WITH_LOCK(cs_wallet, return m_last_block_processed); + chain().waitForNotificationsIfNewBlocksConnected(last_block_hash); } isminetype CWallet::IsMine(const CTxIn &txin) const {