diff --git a/src/interfaces/chain.h b/src/interfaces/chain.h --- a/src/interfaces/chain.h +++ b/src/interfaces/chain.h @@ -28,6 +28,7 @@ namespace interfaces { +class Handler; class Wallet; //! Interface giving clients (wallet processes, maybe other analysis tools in @@ -43,6 +44,12 @@ //! 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). @@ -215,6 +222,29 @@ //! Send wallet load notification to the GUI. virtual void loadWallet(std::unique_ptr wallet) = 0; + + //! Chain notifications. + class Notifications { + public: + virtual ~Notifications() {} + virtual void TransactionAddedToMempool(const CTransactionRef &tx) {} + virtual void TransactionRemovedFromMempool(const CTransactionRef &ptx) { + } + virtual void + BlockConnected(const CBlock &block, + const std::vector &tx_conflicted) {} + virtual void BlockDisconnected(const CBlock &block) {} + virtual void ChainStateFlushed(const CBlockLocator &locator) {} + virtual void ResendWalletTransactions(Lock &locked_chain, + int64_t best_block_time) {} + }; + + //! Register handler for notifications. + virtual std::unique_ptr + handleNotifications(Notifications ¬ifications) = 0; + + //! Wait for pending notifications to be handled. + virtual void waitForNotifications() = 0; }; //! Interface to let node manage chain clients (wallets, or maybe tools for diff --git a/src/interfaces/chain.cpp b/src/interfaces/chain.cpp --- a/src/interfaces/chain.cpp +++ b/src/interfaces/chain.cpp @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -20,6 +21,7 @@ #include #include #include +#include #include #include @@ -163,6 +165,54 @@ using UniqueLock::UniqueLock; }; + class NotificationsHandlerImpl : public Handler, CValidationInterface { + public: + explicit NotificationsHandlerImpl(Chain &chain, + Chain::Notifications ¬ifications) + : m_chain(chain), m_notifications(¬ifications) { + RegisterValidationInterface(this); + } + ~NotificationsHandlerImpl() override { disconnect(); } + void disconnect() override { + if (m_notifications) { + m_notifications = nullptr; + UnregisterValidationInterface(this); + } + } + void TransactionAddedToMempool(const CTransactionRef &tx) override { + m_notifications->TransactionAddedToMempool(tx); + } + void TransactionRemovedFromMempool(const CTransactionRef &tx) override { + m_notifications->TransactionRemovedFromMempool(tx); + } + void BlockConnected( + const std::shared_ptr &block, + const CBlockIndex *index, + const std::vector &tx_conflicted) override { + m_notifications->BlockConnected(*block, tx_conflicted); + } + void + BlockDisconnected(const std::shared_ptr &block) override { + m_notifications->BlockDisconnected(*block); + } + void ChainStateFlushed(const CBlockLocator &locator) override { + m_notifications->ChainStateFlushed(locator); + } + void ResendWalletTransactions(int64_t best_block_time, + CConnman *) override { + // `cs_main` is always held when this method is called, so it is + // safe to call `assumeLocked`. This is awkward, and the + // `assumeLocked` method should be able to be removed entirely if + // `ResendWalletTransactions` is replaced by a wallet timer as + // suggested in https://github.com/bitcoin/bitcoin/issues/15619 + auto locked_chain = m_chain.assumeLocked(); + m_notifications->ResendWalletTransactions(*locked_chain, + best_block_time); + } + Chain &m_chain; + Chain::Notifications *m_notifications; + }; + class ChainImpl : public Chain { public: std::unique_ptr lock(bool try_lock) override { @@ -259,6 +309,14 @@ void loadWallet(std::unique_ptr wallet) override { ::uiInterface.LoadWallet(wallet); } + std::unique_ptr + handleNotifications(Notifications ¬ifications) override { + return std::make_unique(*this, + notifications); + } + void waitForNotifications() override { + SyncWithValidationInterfaceQueue(); + } }; } // namespace diff --git a/src/wallet/test/wallet_test_fixture.h b/src/wallet/test/wallet_test_fixture.h --- a/src/wallet/test/wallet_test_fixture.h +++ b/src/wallet/test/wallet_test_fixture.h @@ -19,7 +19,6 @@ struct WalletTestingSetup : public TestingSetup { explicit WalletTestingSetup( const std::string &chainName = CBaseChainParams::MAIN); - ~WalletTestingSetup(); std::unique_ptr m_chain = interfaces::MakeChain(); CWallet m_wallet; diff --git a/src/wallet/test/wallet_test_fixture.cpp b/src/wallet/test/wallet_test_fixture.cpp --- a/src/wallet/test/wallet_test_fixture.cpp +++ b/src/wallet/test/wallet_test_fixture.cpp @@ -16,12 +16,9 @@ WalletDatabase::CreateMock()) { bool fFirstRun; m_wallet.LoadWallet(fFirstRun); - RegisterValidationInterface(&m_wallet); + m_wallet.m_chain_notifications_handler = + m_chain->handleNotifications(m_wallet); RegisterWalletRPCCommands(tableRPC); RegisterDumpRPCCommands(tableRPC); } - -WalletTestingSetup::~WalletTestingSetup() { - UnregisterValidationInterface(&m_wallet); -} diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -9,6 +9,7 @@ #include #include +#include #include #include #include