// Copyright (c) 2024 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #ifndef BITCOIN_NODE_TXDOWNLOADMAN_H #define BITCOIN_NODE_TXDOWNLOADMAN_H #include #include #include #include #include class CBlock; class CRollingBloomFilter; class CTxMemPool; class GenTxid; class TxRequestTracker; namespace node { class TxDownloadManagerImpl; /** Maximum number of in-flight transaction requests from a peer. It is not a hard limit, but the threshold at which * point the OVERLOADED_PEER_TX_DELAY kicks in. */ static constexpr int32_t MAX_PEER_TX_REQUEST_IN_FLIGHT = 100; /** Maximum number of transactions to consider for requesting, per peer. It provides a reasonable DoS limit to * per-peer memory usage spent on announcements, while covering peers continuously sending INVs at the maximum * rate (by our own policy, see INVENTORY_BROADCAST_PER_SECOND) for several minutes, while not receiving * the actual transaction (from any peer) in response to requests for them. */ static constexpr int32_t MAX_PEER_TX_ANNOUNCEMENTS = 5000; /** How long to delay requesting transactions via txids, if we have wtxid-relaying peers */ static constexpr auto TXID_RELAY_DELAY{2s}; /** How long to delay requesting transactions from non-preferred peers */ static constexpr auto NONPREF_PEER_TX_DELAY{2s}; /** How long to delay requesting transactions from overloaded peers (see MAX_PEER_TX_REQUEST_IN_FLIGHT). */ static constexpr auto OVERLOADED_PEER_TX_DELAY{2s}; /** How long to wait before downloading a transaction from an additional peer */ static constexpr auto GETDATA_TX_INTERVAL{60s}; struct TxDownloadOptions { /** Read-only reference to mempool. */ const CTxMemPool& m_mempool; /** RNG provided by caller. */ FastRandomContext& m_rng; /** Maximum number of transactions allowed in orphanage. */ const uint32_t m_max_orphan_txs; /** Instantiate TxRequestTracker as deterministic (used for tests). */ bool m_deterministic_txrequest{false}; }; struct TxDownloadConnectionInfo { /** Whether this peer is preferred for transaction download. */ const bool m_preferred; /** Whether this peer has Relay permissions. */ const bool m_relay_permissions; /** Whether this peer supports wtxid relay. */ const bool m_wtxid_relay; }; struct PackageToValidate { Package m_txns; std::vector m_senders; /** Construct a 1-parent-1-child package. */ explicit PackageToValidate(const CTransactionRef& parent, const CTransactionRef& child, NodeId parent_sender, NodeId child_sender) : m_txns{parent, child}, m_senders{parent_sender, child_sender} {} // Move ctor PackageToValidate(PackageToValidate&& other) : m_txns{std::move(other.m_txns)}, m_senders{std::move(other.m_senders)} {} // Copy ctor PackageToValidate(const PackageToValidate& other) = default; // Move assignment PackageToValidate& operator=(PackageToValidate&& other) { this->m_txns = std::move(other.m_txns); this->m_senders = std::move(other.m_senders); return *this; } std::string ToString() const { Assume(m_txns.size() == 2); return strprintf("parent %s (wtxid=%s, sender=%d) + child %s (wtxid=%s, sender=%d)", m_txns.front()->GetHash().ToString(), m_txns.front()->GetWitnessHash().ToString(), m_senders.front(), m_txns.back()->GetHash().ToString(), m_txns.back()->GetWitnessHash().ToString(), m_senders.back()); } }; struct RejectedTxTodo { bool m_should_add_extra_compact_tx; std::vector m_unique_parents; std::optional m_package_to_validate; }; /** * Class responsible for deciding what transactions to request and, once * downloaded, whether and how to validate them. It is also responsible for * deciding what transaction packages to validate and how to resolve orphan * transactions. Its data structures include TxRequestTracker for scheduling * requests, rolling bloom filters for remembering transactions that have * already been {accepted, rejected, confirmed}, an orphanage, and a registry of * each peer's transaction relay-related information. * * Caller needs to interact with TxDownloadManager: * - ValidationInterface callbacks. * - When a potential transaction relay peer connects or disconnects. * - When a transaction or package is accepted or rejected from mempool * - When a inv, notfound, or tx message is received * - To get instructions for which getdata messages to send * * This class is not thread-safe. Access must be synchronized using an * external mutex. */ class TxDownloadManager { const std::unique_ptr m_impl; public: explicit TxDownloadManager(const TxDownloadOptions& options); ~TxDownloadManager(); // Responses to chain events. TxDownloadManager is not an actual client of ValidationInterface, these are called through PeerManager. void ActiveTipChange(); void BlockConnected(const std::shared_ptr& pblock); void BlockDisconnected(); /** Creates a new PeerInfo. Saves the connection info to calculate tx announcement delays later. */ void ConnectedPeer(NodeId nodeid, const TxDownloadConnectionInfo& info); /** Deletes all txrequest announcements and orphans for a given peer. */ void DisconnectedPeer(NodeId nodeid); /** Consider adding this tx hash to txrequest. Should be called whenever a new inv has been received. * Also called internally when a transaction is missing parents so that we can request them. * Returns true if this was a dropped inv (p2p_inv=true and we already have the tx), false otherwise. */ bool AddTxAnnouncement(NodeId peer, const GenTxid& gtxid, std::chrono::microseconds now); /** Get getdata requests to send. */ std::vector GetRequestsToSend(NodeId nodeid, std::chrono::microseconds current_time); /** Should be called when a notfound for a tx has been received. */ void ReceivedNotFound(NodeId nodeid, const std::vector& txhashes); /** Respond to successful transaction submission to mempool */ void MempoolAcceptedTx(const CTransactionRef& tx); /** Respond to transaction rejected from mempool */ RejectedTxTodo MempoolRejectedTx(const CTransactionRef& ptx, const TxValidationState& state, NodeId nodeid, bool first_time_failure); /** Respond to package rejected from mempool */ void MempoolRejectedPackage(const Package& package); /** Marks a tx as ReceivedResponse in txrequest and checks whether AlreadyHaveTx. * Return a bool indicating whether this tx should be validated. If false, optionally, a * PackageToValidate. */ std::pair> ReceivedTx(NodeId nodeid, const CTransactionRef& ptx); /** Whether there are any orphans to reconsider for this peer. */ bool HaveMoreWork(NodeId nodeid) const; /** Returns next orphan tx to consider, or nullptr if none exist. */ CTransactionRef GetTxToReconsider(NodeId nodeid); /** Check that all data structures are empty. */ void CheckIsEmpty() const; /** Check that all data structures that track per-peer information have nothing for this peer. */ void CheckIsEmpty(NodeId nodeid) const; /** Wrapper for TxOrphanage::GetOrphanTransactions */ std::vector GetOrphanTransactions() const; }; } // namespace node #endif // BITCOIN_NODE_TXDOWNLOADMAN_H