txdownloadman_impl.h raw

   1  // Copyright (c) 2024-present 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  #ifndef BITCOIN_NODE_TXDOWNLOADMAN_IMPL_H
   5  #define BITCOIN_NODE_TXDOWNLOADMAN_IMPL_H
   6  
   7  #include <node/txdownloadman.h>
   8  
   9  #include <common/bloom.h>
  10  #include <consensus/validation.h>
  11  #include <kernel/chain.h>
  12  #include <net.h>
  13  #include <node/txorphanage.h>
  14  #include <primitives/transaction.h>
  15  #include <policy/packages.h>
  16  #include <txrequest.h>
  17  
  18  class CTxMemPool;
  19  namespace node {
  20  class TxDownloadManagerImpl {
  21  public:
  22      TxDownloadOptions m_opts;
  23  
  24      /** Manages unvalidated tx data (orphan transactions for which we are downloading ancestors). */
  25      std::unique_ptr<TxOrphanage> m_orphanage;
  26      /** Tracks candidates for requesting and downloading transaction data. */
  27      TxRequestTracker m_txrequest;
  28  
  29      /**
  30       * Filter for transactions that were recently rejected by the mempool.
  31       * These are not rerequested until the chain tip changes, at which point
  32       * the entire filter is reset.
  33       *
  34       * Without this filter we'd be re-requesting txs from each of our peers,
  35       * increasing bandwidth consumption considerably. For instance, with 100
  36       * peers, half of which relay a tx we don't accept, that might be a 50x
  37       * bandwidth increase. A flooding attacker attempting to roll-over the
  38       * filter using minimum-sized, 60byte, transactions might manage to send
  39       * 1000/sec if we have fast peers, so we pick 120,000 to give our peers a
  40       * two minute window to send invs to us.
  41       *
  42       * Decreasing the false positive rate is fairly cheap, so we pick one in a
  43       * million to make it highly unlikely for users to have issues with this
  44       * filter.
  45       *
  46       * We typically only add wtxids to this filter. For non-segwit
  47       * transactions, the txid == wtxid, so this only prevents us from
  48       * re-downloading non-segwit transactions when communicating with
  49       * non-wtxidrelay peers -- which is important for avoiding malleation
  50       * attacks that could otherwise interfere with transaction relay from
  51       * non-wtxidrelay peers. For communicating with wtxidrelay peers, having
  52       * the reject filter store wtxids is exactly what we want to avoid
  53       * redownload of a rejected transaction.
  54       *
  55       * In cases where we can tell that a segwit transaction will fail
  56       * validation no matter the witness, we may add the txid of such
  57       * transaction to the filter as well. This can be helpful when
  58       * communicating with txid-relay peers or if we were to otherwise fetch a
  59       * transaction via txid (eg in our orphan handling).
  60       *
  61       * Memory used: 1.3 MB
  62       */
  63      std::unique_ptr<CRollingBloomFilter> m_lazy_recent_rejects{nullptr};
  64  
  65      CRollingBloomFilter& RecentRejectsFilter()
  66      {
  67          if (!m_lazy_recent_rejects) {
  68              m_lazy_recent_rejects = std::make_unique<CRollingBloomFilter>(120'000, 0.000'001);
  69          }
  70  
  71          return *m_lazy_recent_rejects;
  72      }
  73  
  74      /**
  75       * Filter for:
  76       * (1) wtxids of transactions that were recently rejected by the mempool but are
  77       * eligible for reconsideration if submitted with other transactions.
  78       * (2) packages (see GetPackageHash) we have already rejected before and should not retry.
  79       *
  80       * Similar to m_lazy_recent_rejects, this filter is used to save bandwidth when e.g. all of our peers
  81       * have larger mempools and thus lower minimum feerates than us.
  82       *
  83       * When a transaction's error is TxValidationResult::TX_RECONSIDERABLE (in a package or by
  84       * itself), add its wtxid to this filter. When a package fails for any reason, add the combined
  85       * hash to this filter.
  86       *
  87       * Upon receiving an announcement for a transaction, if it exists in this filter, do not
  88       * download the txdata. When considering packages, if it exists in this filter, drop it.
  89       *
  90       * Reset this filter when the chain tip changes.
  91       *
  92       * Parameters are picked to be the same as m_lazy_recent_rejects, with the same rationale.
  93       */
  94      std::unique_ptr<CRollingBloomFilter> m_lazy_recent_rejects_reconsiderable{nullptr};
  95  
  96      CRollingBloomFilter& RecentRejectsReconsiderableFilter()
  97      {
  98          if (!m_lazy_recent_rejects_reconsiderable) {
  99              m_lazy_recent_rejects_reconsiderable = std::make_unique<CRollingBloomFilter>(120'000, 0.000'001);
 100          }
 101  
 102          return *m_lazy_recent_rejects_reconsiderable;
 103      }
 104  
 105      /*
 106       * Filter for transactions that have been recently confirmed.
 107       * We use this to avoid requesting transactions that have already been
 108       * confirmed.
 109       *
 110       * Blocks don't typically have more than 4000 transactions, so this should
 111       * be at least six blocks (~1 hr) worth of transactions that we can store,
 112       * inserting both a txid and wtxid for every observed transaction.
 113       * If the number of transactions appearing in a block goes up, or if we are
 114       * seeing getdata requests more than an hour after initial announcement, we
 115       * can increase this number.
 116       * The false positive rate of 1/1M should come out to less than 1
 117       * transaction per day that would be inadvertently ignored (which is the
 118       * same probability that we have in the reject filter).
 119       */
 120      std::unique_ptr<CRollingBloomFilter> m_lazy_recent_confirmed_transactions{nullptr};
 121  
 122      CRollingBloomFilter& RecentConfirmedTransactionsFilter()
 123      {
 124          if (!m_lazy_recent_confirmed_transactions) {
 125              m_lazy_recent_confirmed_transactions = std::make_unique<CRollingBloomFilter>(48'000, 0.000'001);
 126          }
 127  
 128          return *m_lazy_recent_confirmed_transactions;
 129      }
 130  
 131      TxDownloadManagerImpl(const TxDownloadOptions& options) : m_opts{options}, m_orphanage{MakeTxOrphanage()}, m_txrequest{options.m_deterministic_txrequest} {}
 132  
 133      struct PeerInfo {
 134          /** Information relevant to scheduling tx requests. */
 135          const TxDownloadConnectionInfo m_connection_info;
 136  
 137          PeerInfo(const TxDownloadConnectionInfo& info) : m_connection_info{info} {}
 138      };
 139  
 140      /** Information for all of the peers we may download transactions from. This is not necessarily
 141       * all peers we are connected to (no block-relay-only and temporary connections). */
 142      std::map<NodeId, PeerInfo> m_peer_info;
 143  
 144      /** Number of wtxid relay peers we have in m_peer_info. */
 145      uint32_t m_num_wtxid_peers{0};
 146  
 147      void ActiveTipChange();
 148      void BlockConnected(const std::shared_ptr<const CBlock>& pblock);
 149      void BlockDisconnected();
 150  
 151      /** Check whether we already have this gtxid in:
 152       *  - mempool
 153       *  - orphanage
 154       *  - m_recent_rejects
 155       *  - m_recent_rejects_reconsiderable (if include_reconsiderable = true)
 156       *  - m_recent_confirmed_transactions
 157       *  */
 158      bool AlreadyHaveTx(const GenTxid& gtxid, bool include_reconsiderable);
 159  
 160      void ConnectedPeer(NodeId nodeid, const TxDownloadConnectionInfo& info);
 161      void DisconnectedPeer(NodeId nodeid);
 162  
 163      /** Consider adding this tx hash to txrequest. Should be called whenever a new inv has been received.
 164       * Also called internally when a transaction is missing parents so that we can request them.
 165       */
 166      bool AddTxAnnouncement(NodeId peer, const GenTxid& gtxid, std::chrono::microseconds now);
 167  
 168      /** Get getdata requests to send. */
 169      std::vector<GenTxid> GetRequestsToSend(NodeId nodeid, std::chrono::microseconds current_time);
 170  
 171      /** Marks a tx as ReceivedResponse in txrequest. */
 172      void ReceivedNotFound(NodeId nodeid, const std::vector<GenTxid>& gtxids);
 173  
 174      /** Look for a child of this transaction in the orphanage to form a 1-parent-1-child package,
 175       * skipping any combinations that have already been tried. Return the resulting package along with
 176       * the senders of its respective transactions, or std::nullopt if no package is found. */
 177      std::optional<PackageToValidate> Find1P1CPackage(const CTransactionRef& ptx, NodeId nodeid);
 178  
 179      void MempoolAcceptedTx(const CTransactionRef& tx);
 180      RejectedTxTodo MempoolRejectedTx(const CTransactionRef& ptx, const TxValidationState& state, NodeId nodeid, bool first_time_failure);
 181      void MempoolRejectedPackage(const Package& package);
 182  
 183      std::pair<bool, std::optional<PackageToValidate>> ReceivedTx(NodeId nodeid, const CTransactionRef& ptx);
 184  
 185      bool HaveMoreWork(NodeId nodeid);
 186      CTransactionRef GetTxToReconsider(NodeId nodeid);
 187  
 188      void CheckIsEmpty();
 189      void CheckIsEmpty(NodeId nodeid);
 190  
 191      std::vector<TxOrphanage::OrphanInfo> GetOrphanTransactions() const;
 192  
 193  protected:
 194      /** Helper for getting deduplicated vector of Txids in vin. */
 195      std::vector<Txid> GetUniqueParents(const CTransaction& tx);
 196  
 197      /** If this peer is an orphan resolution candidate for this transaction, treat the unique_parents as announced by
 198       * this peer; add them as new invs to m_txrequest.
 199       * @returns whether this transaction was a valid orphan resolution candidate.
 200       * */
 201      bool MaybeAddOrphanResolutionCandidate(const std::vector<Txid>& unique_parents, const Wtxid& wtxid, NodeId nodeid, std::chrono::microseconds now);
 202  };
 203  } // namespace node
 204  #endif // BITCOIN_NODE_TXDOWNLOADMAN_IMPL_H
 205