diff --git a/src/interfaces/chain.h b/src/interfaces/chain.h index e6847b9b1613..20369fd2e262 100644 --- a/src/interfaces/chain.h +++ b/src/interfaces/chain.h @@ -326,12 +326,18 @@ class Chain }; //! Register handler for notifications. + //! Some notifications are asynchronous and may still execute after the handler is disconnected. + //! Use waitForNotifications() after the handler is disconnected to ensure all pending notifications + //! have been processed. virtual std::unique_ptr handleNotifications(std::shared_ptr notifications) = 0; //! Wait for pending notifications to be processed unless block hash points to the current //! chain tip. virtual void waitForNotificationsIfTipChanged(const uint256& old_tip) = 0; + //! Wait for all pending notifications up to this point to be processed + virtual void waitForNotifications() = 0; + //! Register handler for RPC. Command is not copied, so reference //! needs to remain valid until Handler is disconnected. virtual std::unique_ptr handleRpc(const CRPCCommand& command) = 0; diff --git a/src/node/interfaces.cpp b/src/node/interfaces.cpp index 6c61b2105d9b..84fd5bb79474 100644 --- a/src/node/interfaces.cpp +++ b/src/node/interfaces.cpp @@ -785,6 +785,10 @@ class ChainImpl : public Chain if (!old_tip.IsNull() && old_tip == WITH_LOCK(::cs_main, return chainman().ActiveChain().Tip()->GetBlockHash())) return; validation_signals().SyncWithValidationInterfaceQueue(); } + void waitForNotifications() override + { + validation_signals().SyncWithValidationInterfaceQueue(); + } std::unique_ptr handleRpc(const CRPCCommand& command) override { return std::make_unique(command); diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 63dab29972da..1e7ad3fc6b82 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -169,7 +169,7 @@ bool RemoveWallet(WalletContext& context, const std::shared_ptr& wallet WITH_LOCK(wallet->cs_wallet, wallet->WriteBestBlock()); // Unregister with the validation interface which also drops shared pointers. - wallet->m_chain_notifications_handler.reset(); + wallet->DisconnectChainNotifications(); { LOCK(context.wallets_mutex); std::vector>::iterator i = std::find(context.wallets.begin(), context.wallets.end(), wallet); @@ -3117,7 +3117,7 @@ std::shared_ptr CWallet::CreateNew(WalletContext& context, const std::s walletInstance->TopUpKeyPool(); if (chain && !AttachChain(walletInstance, *chain, /*rescan_required=*/false, error, warnings)) { - walletInstance->m_chain_notifications_handler.reset(); // Reset this pointer so that the wallet will actually be unloaded + walletInstance->DisconnectChainNotifications(); return nullptr; } @@ -3158,7 +3158,7 @@ std::shared_ptr CWallet::LoadExisting(WalletContext& context, const std walletInstance->TopUpKeyPool(); if (chain && !AttachChain(walletInstance, *chain, rescan_required, error, warnings)) { - walletInstance->m_chain_notifications_handler.reset(); // Reset this pointer so that the wallet will actually be unloaded + walletInstance->DisconnectChainNotifications(); return nullptr; } @@ -4577,4 +4577,14 @@ std::optional CWallet::GetTXO(const COutPoint& outpoint) const } return it->second; } + +void CWallet::DisconnectChainNotifications() +{ + if (m_chain_notifications_handler) { + m_chain_notifications_handler->disconnect(); + chain().waitForNotifications(); + m_chain_notifications_handler.reset(); + } +} + } // namespace wallet diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index e3d362360c48..fd4b368d7819 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -1072,6 +1072,9 @@ class CWallet final : public WalletStorage, public interfaces::Chain::Notificati //! Find the private key for the given key id from the wallet's descriptors, if available //! Returns nullopt when no descriptor has the key or if the wallet is locked. std::optional GetKey(const CKeyID& keyid) const; + + //! Disconnect chain notifications and wait for all notifications to be processed + void DisconnectChainNotifications(); }; /**