|
| 1 | +// Copyright (c) 2024 The Bitcoin Core developers |
| 2 | +// Distributed under the MIT software license, see the accompanying |
| 3 | +// file COPYING or http://www.opensource.org/licenses/mit-license.php. |
| 4 | + |
| 5 | +#ifndef BITCOIN_NODE_TXDOWNLOADMAN_H |
| 6 | +#define BITCOIN_NODE_TXDOWNLOADMAN_H |
| 7 | + |
| 8 | +#include <net.h> |
| 9 | +#include <policy/packages.h> |
| 10 | +#include <txorphanage.h> |
| 11 | + |
| 12 | +#include <cstdint> |
| 13 | +#include <memory> |
| 14 | + |
| 15 | +class CBlock; |
| 16 | +class CRollingBloomFilter; |
| 17 | +class CTxMemPool; |
| 18 | +class GenTxid; |
| 19 | +class TxRequestTracker; |
| 20 | +namespace node { |
| 21 | +class TxDownloadManagerImpl; |
| 22 | + |
| 23 | +/** Maximum number of in-flight transaction requests from a peer. It is not a hard limit, but the threshold at which |
| 24 | + * point the OVERLOADED_PEER_TX_DELAY kicks in. */ |
| 25 | +static constexpr int32_t MAX_PEER_TX_REQUEST_IN_FLIGHT = 100; |
| 26 | +/** Maximum number of transactions to consider for requesting, per peer. It provides a reasonable DoS limit to |
| 27 | + * per-peer memory usage spent on announcements, while covering peers continuously sending INVs at the maximum |
| 28 | + * rate (by our own policy, see INVENTORY_BROADCAST_PER_SECOND) for several minutes, while not receiving |
| 29 | + * the actual transaction (from any peer) in response to requests for them. */ |
| 30 | +static constexpr int32_t MAX_PEER_TX_ANNOUNCEMENTS = 5000; |
| 31 | +/** How long to delay requesting transactions via txids, if we have wtxid-relaying peers */ |
| 32 | +static constexpr auto TXID_RELAY_DELAY{2s}; |
| 33 | +/** How long to delay requesting transactions from non-preferred peers */ |
| 34 | +static constexpr auto NONPREF_PEER_TX_DELAY{2s}; |
| 35 | +/** How long to delay requesting transactions from overloaded peers (see MAX_PEER_TX_REQUEST_IN_FLIGHT). */ |
| 36 | +static constexpr auto OVERLOADED_PEER_TX_DELAY{2s}; |
| 37 | +/** How long to wait before downloading a transaction from an additional peer */ |
| 38 | +static constexpr auto GETDATA_TX_INTERVAL{60s}; |
| 39 | +struct TxDownloadOptions { |
| 40 | + /** Read-only reference to mempool. */ |
| 41 | + const CTxMemPool& m_mempool; |
| 42 | + /** RNG provided by caller. */ |
| 43 | + FastRandomContext& m_rng; |
| 44 | + /** Maximum number of transactions allowed in orphanage. */ |
| 45 | + const uint32_t m_max_orphan_txs; |
| 46 | + /** Instantiate TxRequestTracker as deterministic (used for tests). */ |
| 47 | + bool m_deterministic_txrequest{false}; |
| 48 | +}; |
| 49 | +struct TxDownloadConnectionInfo { |
| 50 | + /** Whether this peer is preferred for transaction download. */ |
| 51 | + const bool m_preferred; |
| 52 | + /** Whether this peer has Relay permissions. */ |
| 53 | + const bool m_relay_permissions; |
| 54 | + /** Whether this peer supports wtxid relay. */ |
| 55 | + const bool m_wtxid_relay; |
| 56 | +}; |
| 57 | +struct PackageToValidate { |
| 58 | + Package m_txns; |
| 59 | + std::vector<NodeId> m_senders; |
| 60 | + /** Construct a 1-parent-1-child package. */ |
| 61 | + explicit PackageToValidate(const CTransactionRef& parent, |
| 62 | + const CTransactionRef& child, |
| 63 | + NodeId parent_sender, |
| 64 | + NodeId child_sender) : |
| 65 | + m_txns{parent, child}, |
| 66 | + m_senders{parent_sender, child_sender} |
| 67 | + {} |
| 68 | + |
| 69 | + // Move ctor |
| 70 | + PackageToValidate(PackageToValidate&& other) : m_txns{std::move(other.m_txns)}, m_senders{std::move(other.m_senders)} {} |
| 71 | + // Copy ctor |
| 72 | + PackageToValidate(const PackageToValidate& other) = default; |
| 73 | + |
| 74 | + // Move assignment |
| 75 | + PackageToValidate& operator=(PackageToValidate&& other) { |
| 76 | + this->m_txns = std::move(other.m_txns); |
| 77 | + this->m_senders = std::move(other.m_senders); |
| 78 | + return *this; |
| 79 | + } |
| 80 | + |
| 81 | + std::string ToString() const { |
| 82 | + Assume(m_txns.size() == 2); |
| 83 | + return strprintf("parent %s (wtxid=%s, sender=%d) + child %s (wtxid=%s, sender=%d)", |
| 84 | + m_txns.front()->GetHash().ToString(), |
| 85 | + m_txns.front()->GetWitnessHash().ToString(), |
| 86 | + m_senders.front(), |
| 87 | + m_txns.back()->GetHash().ToString(), |
| 88 | + m_txns.back()->GetWitnessHash().ToString(), |
| 89 | + m_senders.back()); |
| 90 | + } |
| 91 | +}; |
| 92 | +struct RejectedTxTodo |
| 93 | +{ |
| 94 | + bool m_should_add_extra_compact_tx; |
| 95 | + std::vector<uint256> m_unique_parents; |
| 96 | + std::optional<PackageToValidate> m_package_to_validate; |
| 97 | +}; |
| 98 | + |
| 99 | + |
| 100 | +/** |
| 101 | + * Class responsible for deciding what transactions to request and, once |
| 102 | + * downloaded, whether and how to validate them. It is also responsible for |
| 103 | + * deciding what transaction packages to validate and how to resolve orphan |
| 104 | + * transactions. Its data structures include TxRequestTracker for scheduling |
| 105 | + * requests, rolling bloom filters for remembering transactions that have |
| 106 | + * already been {accepted, rejected, confirmed}, an orphanage, and a registry of |
| 107 | + * each peer's transaction relay-related information. |
| 108 | + * |
| 109 | + * Caller needs to interact with TxDownloadManager: |
| 110 | + * - ValidationInterface callbacks. |
| 111 | + * - When a potential transaction relay peer connects or disconnects. |
| 112 | + * - When a transaction or package is accepted or rejected from mempool |
| 113 | + * - When a inv, notfound, or tx message is received |
| 114 | + * - To get instructions for which getdata messages to send |
| 115 | + * |
| 116 | + * This class is not thread-safe. Access must be synchronized using an |
| 117 | + * external mutex. |
| 118 | + */ |
| 119 | +class TxDownloadManager { |
| 120 | + const std::unique_ptr<TxDownloadManagerImpl> m_impl; |
| 121 | + |
| 122 | +public: |
| 123 | + explicit TxDownloadManager(const TxDownloadOptions& options); |
| 124 | + ~TxDownloadManager(); |
| 125 | + |
| 126 | + // Responses to chain events. TxDownloadManager is not an actual client of ValidationInterface, these are called through PeerManager. |
| 127 | + void ActiveTipChange(); |
| 128 | + void BlockConnected(const std::shared_ptr<const CBlock>& pblock); |
| 129 | + void BlockDisconnected(); |
| 130 | + |
| 131 | + /** Creates a new PeerInfo. Saves the connection info to calculate tx announcement delays later. */ |
| 132 | + void ConnectedPeer(NodeId nodeid, const TxDownloadConnectionInfo& info); |
| 133 | + |
| 134 | + /** Deletes all txrequest announcements and orphans for a given peer. */ |
| 135 | + void DisconnectedPeer(NodeId nodeid); |
| 136 | + |
| 137 | + /** New inv has been received. May be added as a candidate to txrequest. |
| 138 | + * @param[in] p2p_inv When true, only add this announcement if we don't already have the tx. |
| 139 | + * Returns true if this was a dropped inv (p2p_inv=true and we already have the tx), false otherwise. */ |
| 140 | + bool AddTxAnnouncement(NodeId peer, const GenTxid& gtxid, std::chrono::microseconds now, bool p2p_inv); |
| 141 | + |
| 142 | + /** Get getdata requests to send. */ |
| 143 | + std::vector<GenTxid> GetRequestsToSend(NodeId nodeid, std::chrono::microseconds current_time); |
| 144 | + |
| 145 | + /** Should be called when a notfound for a tx has been received. */ |
| 146 | + void ReceivedNotFound(NodeId nodeid, const std::vector<uint256>& txhashes); |
| 147 | + |
| 148 | + /** Respond to successful transaction submission to mempool */ |
| 149 | + void MempoolAcceptedTx(const CTransactionRef& tx); |
| 150 | + |
| 151 | + /** Respond to transaction rejected from mempool */ |
| 152 | + RejectedTxTodo MempoolRejectedTx(const CTransactionRef& ptx, const TxValidationState& state, NodeId nodeid, bool first_time_failure); |
| 153 | + |
| 154 | + /** Respond to package rejected from mempool */ |
| 155 | + void MempoolRejectedPackage(const Package& package); |
| 156 | + |
| 157 | + /** Marks a tx as ReceivedResponse in txrequest and checks whether AlreadyHaveTx. |
| 158 | + * Return a bool indicating whether this tx should be validated. If false, optionally, a |
| 159 | + * PackageToValidate. */ |
| 160 | + std::pair<bool, std::optional<PackageToValidate>> ReceivedTx(NodeId nodeid, const CTransactionRef& ptx); |
| 161 | + |
| 162 | + /** Whether there are any orphans to reconsider for this peer. */ |
| 163 | + bool HaveMoreWork(NodeId nodeid) const; |
| 164 | + |
| 165 | + /** Returns next orphan tx to consider, or nullptr if none exist. */ |
| 166 | + CTransactionRef GetTxToReconsider(NodeId nodeid); |
| 167 | + |
| 168 | + /** Check that all data structures are empty. */ |
| 169 | + void CheckIsEmpty() const; |
| 170 | + |
| 171 | + /** Check that all data structures that track per-peer information have nothing for this peer. */ |
| 172 | + void CheckIsEmpty(NodeId nodeid) const; |
| 173 | + |
| 174 | + /** Wrapper for TxOrphanage::GetOrphanTransactions */ |
| 175 | + std::vector<TxOrphanage::OrphanTxBase> GetOrphanTransactions() const; |
| 176 | +}; |
| 177 | +} // namespace node |
| 178 | +#endif // BITCOIN_NODE_TXDOWNLOADMAN_H |
0 commit comments