txdownloadman_impl.cpp raw

   1  // Copyright (c) 2024
   2  // Distributed under the MIT software license, see the accompanying
   3  // file COPYING or http://www.opensource.org/licenses/mit-license.php.
   4  
   5  #include <node/txdownloadman_impl.h>
   6  #include <node/txdownloadman.h>
   7  
   8  #include <chain.h>
   9  #include <consensus/validation.h>
  10  #include <logging.h>
  11  #include <txmempool.h>
  12  #include <validation.h>
  13  #include <validationinterface.h>
  14  
  15  namespace node {
  16  // TxDownloadManager wrappers
  17  TxDownloadManager::TxDownloadManager(const TxDownloadOptions& options) :
  18      m_impl{std::make_unique<TxDownloadManagerImpl>(options)}
  19  {}
  20  TxDownloadManager::~TxDownloadManager() = default;
  21  
  22  void TxDownloadManager::ActiveTipChange()
  23  {
  24      m_impl->ActiveTipChange();
  25  }
  26  void TxDownloadManager::BlockConnected(const std::shared_ptr<const CBlock>& pblock)
  27  {
  28      m_impl->BlockConnected(pblock);
  29  }
  30  void TxDownloadManager::BlockDisconnected()
  31  {
  32      m_impl->BlockDisconnected();
  33  }
  34  void TxDownloadManager::ConnectedPeer(NodeId nodeid, const TxDownloadConnectionInfo& info)
  35  {
  36      m_impl->ConnectedPeer(nodeid, info);
  37  }
  38  void TxDownloadManager::DisconnectedPeer(NodeId nodeid)
  39  {
  40      m_impl->DisconnectedPeer(nodeid);
  41  }
  42  bool TxDownloadManager::AddTxAnnouncement(NodeId peer, const GenTxid& gtxid, std::chrono::microseconds now)
  43  {
  44      return m_impl->AddTxAnnouncement(peer, gtxid, now);
  45  }
  46  std::vector<GenTxid> TxDownloadManager::GetRequestsToSend(NodeId nodeid, std::chrono::microseconds current_time)
  47  {
  48      return m_impl->GetRequestsToSend(nodeid, current_time);
  49  }
  50  void TxDownloadManager::ReceivedNotFound(NodeId nodeid, const std::vector<uint256>& txhashes)
  51  {
  52      m_impl->ReceivedNotFound(nodeid, txhashes);
  53  }
  54  void TxDownloadManager::MempoolAcceptedTx(const CTransactionRef& tx)
  55  {
  56      m_impl->MempoolAcceptedTx(tx);
  57  }
  58  RejectedTxTodo TxDownloadManager::MempoolRejectedTx(const CTransactionRef& ptx, const TxValidationState& state, NodeId nodeid, bool first_time_failure)
  59  {
  60      return m_impl->MempoolRejectedTx(ptx, state, nodeid, first_time_failure);
  61  }
  62  void TxDownloadManager::MempoolRejectedPackage(const Package& package)
  63  {
  64      m_impl->MempoolRejectedPackage(package);
  65  }
  66  std::pair<bool, std::optional<PackageToValidate>> TxDownloadManager::ReceivedTx(NodeId nodeid, const CTransactionRef& ptx)
  67  {
  68      return m_impl->ReceivedTx(nodeid, ptx);
  69  }
  70  bool TxDownloadManager::HaveMoreWork(NodeId nodeid) const
  71  {
  72      return m_impl->HaveMoreWork(nodeid);
  73  }
  74  CTransactionRef TxDownloadManager::GetTxToReconsider(NodeId nodeid)
  75  {
  76      return m_impl->GetTxToReconsider(nodeid);
  77  }
  78  void TxDownloadManager::CheckIsEmpty() const
  79  {
  80      m_impl->CheckIsEmpty();
  81  }
  82  void TxDownloadManager::CheckIsEmpty(NodeId nodeid) const
  83  {
  84      m_impl->CheckIsEmpty(nodeid);
  85  }
  86  std::vector<TxOrphanage::OrphanTxBase> TxDownloadManager::GetOrphanTransactions() const
  87  {
  88      return m_impl->GetOrphanTransactions();
  89  }
  90  void TxDownloadManager::SetMaxOrphanTxs(uint32_t max_orphan_txs)
  91  {
  92      m_impl->m_opts.m_max_orphan_txs = max_orphan_txs;
  93      m_impl->m_orphanage.LimitOrphans(max_orphan_txs, m_impl->m_opts.m_rng);
  94  }
  95  
  96  // TxDownloadManagerImpl
  97  void TxDownloadManagerImpl::ActiveTipChange()
  98  {
  99      RecentRejectsFilter().reset();
 100      RecentRejectsReconsiderableFilter().reset();
 101  }
 102  
 103  void TxDownloadManagerImpl::BlockConnected(const std::shared_ptr<const CBlock>& pblock)
 104  {
 105      m_orphanage.EraseForBlock(*pblock);
 106  
 107      for (const auto& ptx : pblock->vtx) {
 108          RecentConfirmedTransactionsFilter().insert(ptx->GetHash().ToUint256());
 109          if (ptx->HasWitness()) {
 110              RecentConfirmedTransactionsFilter().insert(ptx->GetWitnessHash().ToUint256());
 111          }
 112          m_txrequest.ForgetTxHash(ptx->GetHash());
 113          m_txrequest.ForgetTxHash(ptx->GetWitnessHash());
 114      }
 115  }
 116  
 117  void TxDownloadManagerImpl::BlockDisconnected()
 118  {
 119      // To avoid relay problems with transactions that were previously
 120      // confirmed, clear our filter of recently confirmed transactions whenever
 121      // there's a reorg.
 122      // This means that in a 1-block reorg (where 1 block is disconnected and
 123      // then another block reconnected), our filter will drop to having only one
 124      // block's worth of transactions in it, but that should be fine, since
 125      // presumably the most common case of relaying a confirmed transaction
 126      // should be just after a new block containing it is found.
 127      RecentConfirmedTransactionsFilter().reset();
 128  }
 129  
 130  bool TxDownloadManagerImpl::AlreadyHaveTx(const GenTxid& gtxid, bool include_reconsiderable)
 131  {
 132      const uint256& hash = gtxid.GetHash();
 133  
 134      if (gtxid.IsWtxid()) {
 135          // Normal query by wtxid.
 136          if (m_orphanage.HaveTx(Wtxid::FromUint256(hash))) return true;
 137      } else {
 138          // Never query by txid: it is possible that the transaction in the orphanage has the same
 139          // txid but a different witness, which would give us a false positive result. If we decided
 140          // not to request the transaction based on this result, an attacker could prevent us from
 141          // downloading a transaction by intentionally creating a malleated version of it.  While
 142          // only one (or none!) of these transactions can ultimately be confirmed, we have no way of
 143          // discerning which one that is, so the orphanage can store multiple transactions with the
 144          // same txid.
 145          //
 146          // While we won't query by txid, we can try to "guess" what the wtxid is based on the txid.
 147          // A non-segwit transaction's txid == wtxid. Query this txid "casted" to a wtxid. This will
 148          // help us find non-segwit transactions, saving bandwidth, and should have no false positives.
 149          if (m_orphanage.HaveTx(Wtxid::FromUint256(hash))) return true;
 150      }
 151  
 152      if (include_reconsiderable && RecentRejectsReconsiderableFilter().contains(hash)) return true;
 153  
 154      if (RecentConfirmedTransactionsFilter().contains(hash)) return true;
 155  
 156      return RecentRejectsFilter().contains(hash) || m_opts.m_mempool.exists(gtxid);
 157  }
 158  
 159  void TxDownloadManagerImpl::ConnectedPeer(NodeId nodeid, const TxDownloadConnectionInfo& info)
 160  {
 161      // If already connected (shouldn't happen in practice), exit early.
 162      if (m_peer_info.contains(nodeid)) return;
 163  
 164      m_peer_info.try_emplace(nodeid, info);
 165      if (info.m_wtxid_relay) m_num_wtxid_peers += 1;
 166  }
 167  
 168  void TxDownloadManagerImpl::DisconnectedPeer(NodeId nodeid)
 169  {
 170      m_orphanage.EraseForPeer(nodeid);
 171      m_txrequest.DisconnectedPeer(nodeid);
 172  
 173      if (auto it = m_peer_info.find(nodeid); it != m_peer_info.end()) {
 174          if (it->second.m_connection_info.m_wtxid_relay) m_num_wtxid_peers -= 1;
 175          m_peer_info.erase(it);
 176      }
 177  
 178  }
 179  
 180  bool TxDownloadManagerImpl::AddTxAnnouncement(NodeId peer, const GenTxid& gtxid, std::chrono::microseconds now)
 181  {
 182      // If this is an orphan we are trying to resolve, consider this peer as a orphan resolution candidate instead.
 183      // - is wtxid matching something in orphanage
 184      // - exists in orphanage
 185      // - peer can be an orphan resolution candidate
 186      if (gtxid.IsWtxid()) {
 187          const auto wtxid{Wtxid::FromUint256(gtxid.GetHash())};
 188          if (auto orphan_tx{m_orphanage.GetTx(wtxid)}) {
 189              auto unique_parents{GetUniqueParents(*orphan_tx)};
 190              std::erase_if(unique_parents, [&](const auto& txid){
 191                  return AlreadyHaveTx(GenTxid::Txid(txid), /*include_reconsiderable=*/false);
 192              });
 193  
 194              // The missing parents may have all been rejected or accepted since the orphan was added to the orphanage.
 195              // Do not delete from the orphanage, as it may be queued for processing.
 196              if (unique_parents.empty()) {
 197                  return true;
 198              }
 199  
 200              if (MaybeAddOrphanResolutionCandidate(unique_parents, wtxid, peer, now)) {
 201                  m_orphanage.AddAnnouncer(orphan_tx->GetWitnessHash(), peer);
 202              }
 203  
 204              // Return even if the peer isn't an orphan resolution candidate. This would be caught by AlreadyHaveTx.
 205              return true;
 206          }
 207      }
 208  
 209      // If this is an inv received from a peer and we already have it, we can drop it.
 210      if (AlreadyHaveTx(gtxid, /*include_reconsiderable=*/true)) return true;
 211  
 212      auto it = m_peer_info.find(peer);
 213      if (it == m_peer_info.end()) return false;
 214      const auto& info = it->second.m_connection_info;
 215      if (!info.m_relay_permissions && m_txrequest.Count(peer) >= MAX_PEER_TX_ANNOUNCEMENTS) {
 216          // Too many queued announcements for this peer
 217          return false;
 218      }
 219      // Decide the TxRequestTracker parameters for this announcement:
 220      // - "preferred": if fPreferredDownload is set (= outbound, or NetPermissionFlags::NoBan permission)
 221      // - "reqtime": current time plus delays for:
 222      //   - NONPREF_PEER_TX_DELAY for announcements from non-preferred connections
 223      //   - TXID_RELAY_DELAY for txid announcements while wtxid peers are available
 224      //   - OVERLOADED_PEER_TX_DELAY for announcements from peers which have at least
 225      //     MAX_PEER_TX_REQUEST_IN_FLIGHT requests in flight (and don't have NetPermissionFlags::Relay).
 226      auto delay{0us};
 227      if (!info.m_preferred) delay += NONPREF_PEER_TX_DELAY;
 228      if (!gtxid.IsWtxid() && m_num_wtxid_peers > 0) delay += TXID_RELAY_DELAY;
 229      const bool overloaded = !info.m_relay_permissions && m_txrequest.CountInFlight(peer) >= MAX_PEER_TX_REQUEST_IN_FLIGHT;
 230      if (overloaded) delay += OVERLOADED_PEER_TX_DELAY;
 231  
 232      m_txrequest.ReceivedInv(peer, gtxid, info.m_preferred, now + delay);
 233  
 234      return false;
 235  }
 236  
 237  bool TxDownloadManagerImpl::MaybeAddOrphanResolutionCandidate(const std::vector<Txid>& unique_parents, const Wtxid& wtxid, NodeId nodeid, std::chrono::microseconds now)
 238  {
 239      auto it_peer = m_peer_info.find(nodeid);
 240      if (it_peer == m_peer_info.end()) return false;
 241      if (m_orphanage.HaveTxFromPeer(wtxid, nodeid)) return false;
 242  
 243      const auto& peer_entry = m_peer_info.at(nodeid);
 244      const auto& info = peer_entry.m_connection_info;
 245  
 246      // TODO: add delays and limits based on the amount of orphan resolution we are already doing
 247      // with this peer, how much they are using the orphanage, etc.
 248      if (!info.m_relay_permissions) {
 249          // This mirrors the delaying and dropping behavior in AddTxAnnouncement in order to preserve
 250          // existing behavior: drop if we are tracking too many invs for this peer already. Each
 251          // orphan resolution involves at least 1 transaction request which may or may not be
 252          // currently tracked in m_txrequest, so we include that in the count.
 253          if (m_txrequest.Count(nodeid) + unique_parents.size() > MAX_PEER_TX_ANNOUNCEMENTS) return false;
 254      }
 255  
 256      std::chrono::seconds delay{0s};
 257      if (!info.m_preferred) delay += NONPREF_PEER_TX_DELAY;
 258      // The orphan wtxid is used, but resolution entails requesting the parents by txid. Sometimes
 259      // parent and child are announced and thus requested around the same time, and we happen to
 260      // receive child sooner. Waiting a few seconds may allow us to cancel the orphan resolution
 261      // request if the parent arrives in that time.
 262      if (m_num_wtxid_peers > 0) delay += TXID_RELAY_DELAY;
 263      const bool overloaded = !info.m_relay_permissions && m_txrequest.CountInFlight(nodeid) >= MAX_PEER_TX_REQUEST_IN_FLIGHT;
 264      if (overloaded) delay += OVERLOADED_PEER_TX_DELAY;
 265  
 266      // Treat finding orphan resolution candidate as equivalent to the peer announcing all missing parents.
 267      // In the future, orphan resolution may include more explicit steps
 268      for (const auto& parent_txid : unique_parents) {
 269          m_txrequest.ReceivedInv(nodeid, GenTxid::Txid(parent_txid), info.m_preferred, now + delay);
 270      }
 271      LogDebug(BCLog::TXPACKAGES, "added peer=%d as a candidate for resolving orphan %s\n", nodeid, wtxid.ToString());
 272      return true;
 273  }
 274  
 275  std::vector<GenTxid> TxDownloadManagerImpl::GetRequestsToSend(NodeId nodeid, std::chrono::microseconds current_time)
 276  {
 277      std::vector<GenTxid> requests;
 278      std::vector<std::pair<NodeId, GenTxid>> expired;
 279      auto requestable = m_txrequest.GetRequestable(nodeid, current_time, &expired);
 280      for (const auto& entry : expired) {
 281          LogDebug(BCLog::NET, "timeout of inflight %s %s from peer=%d\n", entry.second.IsWtxid() ? "wtx" : "tx",
 282              entry.second.GetHash().ToString(), entry.first);
 283      }
 284      for (const GenTxid& gtxid : requestable) {
 285          if (!AlreadyHaveTx(gtxid, /*include_reconsiderable=*/false)) {
 286              LogDebug(BCLog::NET, "Requesting %s %s peer=%d\n", gtxid.IsWtxid() ? "wtx" : "tx",
 287                  gtxid.GetHash().ToString(), nodeid);
 288              requests.emplace_back(gtxid);
 289              m_txrequest.RequestedTx(nodeid, gtxid.GetHash(), current_time + GETDATA_TX_INTERVAL);
 290          } else {
 291              // We have already seen this transaction, no need to download. This is just a belt-and-suspenders, as
 292              // this should already be called whenever a transaction becomes AlreadyHaveTx().
 293              m_txrequest.ForgetTxHash(gtxid.GetHash());
 294          }
 295      }
 296      return requests;
 297  }
 298  
 299  void TxDownloadManagerImpl::ReceivedNotFound(NodeId nodeid, const std::vector<uint256>& txhashes)
 300  {
 301      for (const auto& txhash : txhashes) {
 302          // If we receive a NOTFOUND message for a tx we requested, mark the announcement for it as
 303          // completed in TxRequestTracker.
 304          m_txrequest.ReceivedResponse(nodeid, txhash);
 305      }
 306  }
 307  
 308  std::optional<PackageToValidate> TxDownloadManagerImpl::Find1P1CPackage(const CTransactionRef& ptx, NodeId nodeid)
 309  {
 310      const auto& parent_wtxid{ptx->GetWitnessHash()};
 311  
 312      Assume(RecentRejectsReconsiderableFilter().contains(parent_wtxid.ToUint256()));
 313  
 314      // Only consider children from this peer. This helps prevent censorship attempts in which an attacker
 315      // sends lots of fake children for the parent, and we (unluckily) keep selecting the fake
 316      // children instead of the real one provided by the honest peer. Since we track all announcers
 317      // of an orphan, this does not exclude parent + orphan pairs that we happened to request from
 318      // different peers.
 319      const auto cpfp_candidates_same_peer{m_orphanage.GetChildrenFromSamePeer(ptx, nodeid)};
 320  
 321      // These children should be sorted from newest to oldest. In the (probably uncommon) case
 322      // of children that replace each other, this helps us accept the highest feerate (probably the
 323      // most recent) one efficiently.
 324      for (const auto& child : cpfp_candidates_same_peer) {
 325          Package maybe_cpfp_package{ptx, child};
 326          if (!RecentRejectsReconsiderableFilter().contains(GetPackageHash(maybe_cpfp_package)) &&
 327              !RecentRejectsFilter().contains(child->GetHash().ToUint256())) {
 328              return PackageToValidate{ptx, child, nodeid, nodeid};
 329          }
 330      }
 331      return std::nullopt;
 332  }
 333  
 334  void TxDownloadManagerImpl::MempoolAcceptedTx(const CTransactionRef& tx)
 335  {
 336      // As this version of the transaction was acceptable, we can forget about any requests for it.
 337      // No-op if the tx is not in txrequest.
 338      m_txrequest.ForgetTxHash(tx->GetHash());
 339      m_txrequest.ForgetTxHash(tx->GetWitnessHash());
 340  
 341      m_orphanage.AddChildrenToWorkSet(*tx, m_opts.m_rng);
 342      // If it came from the orphanage, remove it. No-op if the tx is not in txorphanage.
 343      m_orphanage.EraseTx(tx->GetWitnessHash());
 344  }
 345  
 346  std::vector<Txid> TxDownloadManagerImpl::GetUniqueParents(const CTransaction& tx)
 347  {
 348      std::vector<Txid> unique_parents;
 349      unique_parents.reserve(tx.vin.size());
 350      for (const CTxIn& txin : tx.vin) {
 351          // We start with all parents, and then remove duplicates below.
 352          unique_parents.push_back(txin.prevout.hash);
 353      }
 354  
 355      std::sort(unique_parents.begin(), unique_parents.end());
 356      unique_parents.erase(std::unique(unique_parents.begin(), unique_parents.end()), unique_parents.end());
 357  
 358      return unique_parents;
 359  }
 360  
 361  node::RejectedTxTodo TxDownloadManagerImpl::MempoolRejectedTx(const CTransactionRef& ptx, const TxValidationState& state, NodeId nodeid, bool first_time_failure)
 362  {
 363      const CTransaction& tx{*ptx};
 364      // Results returned to caller
 365      // Whether we should call AddToCompactExtraTransactions at the end
 366      bool add_extra_compact_tx{first_time_failure};
 367      // Hashes to pass to AddKnownTx later
 368      std::vector<Txid> unique_parents;
 369      // Populated if failure is reconsiderable and eligible package is found.
 370      std::optional<node::PackageToValidate> package_to_validate;
 371  
 372      if (state.GetResult() == TxValidationResult::TX_MISSING_INPUTS) {
 373          // Only process a new orphan if this is a first time failure, as otherwise it must be either
 374          // already in orphanage or from 1p1c processing.
 375          if (first_time_failure && !RecentRejectsFilter().contains(ptx->GetWitnessHash().ToUint256())) {
 376              bool fRejectedParents = false; // It may be the case that the orphans parents have all been rejected
 377  
 378              // Deduplicate parent txids, so that we don't have to loop over
 379              // the same parent txid more than once down below.
 380              unique_parents = GetUniqueParents(tx);
 381  
 382              // Distinguish between parents in m_lazy_recent_rejects and m_lazy_recent_rejects_reconsiderable.
 383              // We can tolerate having up to 1 parent in m_lazy_recent_rejects_reconsiderable since we
 384              // submit 1p1c packages. However, fail immediately if any are in m_lazy_recent_rejects.
 385              std::optional<uint256> rejected_parent_reconsiderable;
 386              for (const uint256& parent_txid : unique_parents) {
 387                  if (RecentRejectsFilter().contains(parent_txid)) {
 388                      fRejectedParents = true;
 389                      break;
 390                  } else if (RecentRejectsReconsiderableFilter().contains(parent_txid) &&
 391                             !m_opts.m_mempool.exists(GenTxid::Txid(parent_txid))) {
 392                      // More than 1 parent in m_lazy_recent_rejects_reconsiderable: 1p1c will not be
 393                      // sufficient to accept this package, so just give up here.
 394                      if (rejected_parent_reconsiderable.has_value()) {
 395                          fRejectedParents = true;
 396                          break;
 397                      }
 398                      rejected_parent_reconsiderable = parent_txid;
 399                  }
 400              }
 401              if (!fRejectedParents) {
 402                  // Filter parents that we already have.
 403                  // Exclude m_lazy_recent_rejects_reconsiderable: the missing parent may have been
 404                  // previously rejected for being too low feerate. This orphan might CPFP it.
 405                  std::erase_if(unique_parents, [&](const auto& txid){
 406                      return AlreadyHaveTx(GenTxid::Txid(txid), /*include_reconsiderable=*/false);
 407                  });
 408                  const auto now{GetTime<std::chrono::microseconds>()};
 409                  const auto& wtxid = ptx->GetWitnessHash();
 410                  // Potentially flip add_extra_compact_tx to false if tx is already in orphanage, which
 411                  // means it was already added to vExtraTxnForCompact.
 412                  add_extra_compact_tx &= !m_orphanage.HaveTx(wtxid);
 413  
 414                  // If there is no candidate for orphan resolution, AddTx will not be called. This means
 415                  // that if a peer is overloading us with invs and orphans, they will eventually not be
 416                  // able to add any more transactions to the orphanage.
 417                  //
 418                  // Search by txid and, if the tx has a witness, wtxid
 419                  std::vector<NodeId> orphan_resolution_candidates{nodeid};
 420                  m_txrequest.GetCandidatePeers(ptx->GetHash().ToUint256(), orphan_resolution_candidates);
 421                  if (ptx->HasWitness()) m_txrequest.GetCandidatePeers(ptx->GetWitnessHash().ToUint256(), orphan_resolution_candidates);
 422  
 423                  for (const auto& nodeid : orphan_resolution_candidates) {
 424                      if (MaybeAddOrphanResolutionCandidate(unique_parents, ptx->GetWitnessHash(), nodeid, now)) {
 425                          m_orphanage.AddTx(ptx, nodeid);
 426                      }
 427                  }
 428  
 429                  // Once added to the orphan pool, a tx is considered AlreadyHave, and we shouldn't request it anymore.
 430                  m_txrequest.ForgetTxHash(tx.GetHash());
 431                  m_txrequest.ForgetTxHash(tx.GetWitnessHash());
 432  
 433                  // DoS prevention: do not allow m_orphanage to grow unbounded (see CVE-2012-3789)
 434                  // Note that, if the orphanage reaches capacity, it's possible that we immediately evict
 435                  // the transaction we just added.
 436                  m_orphanage.LimitOrphans(m_opts.m_max_orphan_txs, m_opts.m_rng);
 437              } else {
 438                  unique_parents.clear();
 439                  LogDebug(BCLog::MEMPOOL, "not keeping orphan with rejected parents %s (wtxid=%s)\n",
 440                           tx.GetHash().ToString(),
 441                           tx.GetWitnessHash().ToString());
 442                  // We will continue to reject this tx since it has rejected
 443                  // parents so avoid re-requesting it from other peers.
 444                  // Here we add both the txid and the wtxid, as we know that
 445                  // regardless of what witness is provided, we will not accept
 446                  // this, so we don't need to allow for redownload of this txid
 447                  // from any of our non-wtxidrelay peers.
 448                  RecentRejectsFilter().insert(tx.GetHash().ToUint256());
 449                  RecentRejectsFilter().insert(tx.GetWitnessHash().ToUint256());
 450                  m_txrequest.ForgetTxHash(tx.GetHash());
 451                  m_txrequest.ForgetTxHash(tx.GetWitnessHash());
 452              }
 453          }
 454      } else if (state.GetResult() == TxValidationResult::TX_WITNESS_STRIPPED) {
 455          add_extra_compact_tx = false;
 456      } else {
 457          // We can add the wtxid of this transaction to our reject filter.
 458          // Do not add txids of witness transactions or witness-stripped
 459          // transactions to the filter, as they can have been malleated;
 460          // adding such txids to the reject filter would potentially
 461          // interfere with relay of valid transactions from peers that
 462          // do not support wtxid-based relay. See
 463          // https://github.com/limenka/limenka/issues/8279 for details.
 464          // We can remove this restriction (and always add wtxids to
 465          // the filter even for witness stripped transactions) once
 466          // wtxid-based relay is broadly deployed.
 467          // See also comments in https://github.com/limenka/limenka/pull/18044#discussion_r443419034
 468          // for concerns around weakening security of unupgraded nodes
 469          // if we start doing this too early.
 470          if (state.GetResult() == TxValidationResult::TX_RECONSIDERABLE) {
 471              // If the result is TX_RECONSIDERABLE, add it to m_lazy_recent_rejects_reconsiderable
 472              // because we should not download or submit this transaction by itself again, but may
 473              // submit it as part of a package later.
 474              RecentRejectsReconsiderableFilter().insert(ptx->GetWitnessHash().ToUint256());
 475  
 476              if (first_time_failure) {
 477                  // When a transaction fails for TX_RECONSIDERABLE, look for a matching child in the
 478                  // orphanage, as it is possible that they succeed as a package.
 479                  LogDebug(BCLog::TXPACKAGES, "tx %s (wtxid=%s) failed but reconsiderable, looking for child in orphanage\n",
 480                           ptx->GetHash().ToString(), ptx->GetWitnessHash().ToString());
 481                  package_to_validate = Find1P1CPackage(ptx, nodeid);
 482              }
 483          } else {
 484              RecentRejectsFilter().insert(ptx->GetWitnessHash().ToUint256());
 485          }
 486          m_txrequest.ForgetTxHash(ptx->GetWitnessHash());
 487          // If the transaction failed for TX_INPUTS_NOT_STANDARD,
 488          // then we know that the witness was irrelevant to the policy
 489          // failure, since this check depends only on the txid
 490          // (the scriptPubKey being spent is covered by the txid).
 491          // Add the txid to the reject filter to prevent repeated
 492          // processing of this transaction in the event that child
 493          // transactions are later received (resulting in
 494          // parent-fetching by txid via the orphan-handling logic).
 495          // We only add the txid if it differs from the wtxid, to avoid wasting entries in the
 496          // rolling bloom filter.
 497          if (state.GetResult() == TxValidationResult::TX_INPUTS_NOT_STANDARD && ptx->HasWitness()) {
 498              RecentRejectsFilter().insert(ptx->GetHash().ToUint256());
 499              m_txrequest.ForgetTxHash(ptx->GetHash());
 500          }
 501      }
 502  
 503      // If the tx failed in ProcessOrphanTx, it should be removed from the orphanage unless the
 504      // tx was still missing inputs. If the tx was not in the orphanage, EraseTx does nothing and returns 0.
 505      if (state.GetResult() != TxValidationResult::TX_MISSING_INPUTS && m_orphanage.EraseTx(ptx->GetWitnessHash()) > 0) {
 506          LogDebug(BCLog::TXPACKAGES, "   removed orphan tx %s (wtxid=%s)\n", ptx->GetHash().ToString(), ptx->GetWitnessHash().ToString());
 507      }
 508  
 509      return RejectedTxTodo{
 510          .m_should_add_extra_compact_tx = add_extra_compact_tx,
 511          .m_unique_parents = std::move(unique_parents),
 512          .m_package_to_validate = std::move(package_to_validate)
 513      };
 514  }
 515  
 516  void TxDownloadManagerImpl::MempoolRejectedPackage(const Package& package)
 517  {
 518      RecentRejectsReconsiderableFilter().insert(GetPackageHash(package));
 519  }
 520  
 521  std::pair<bool, std::optional<PackageToValidate>> TxDownloadManagerImpl::ReceivedTx(NodeId nodeid, const CTransactionRef& ptx)
 522  {
 523      const uint256& txid = ptx->GetHash();
 524      const uint256& wtxid = ptx->GetWitnessHash();
 525  
 526      // Mark that we have received a response
 527      m_txrequest.ReceivedResponse(nodeid, txid);
 528      if (ptx->HasWitness()) m_txrequest.ReceivedResponse(nodeid, wtxid);
 529  
 530      // First check if we should drop this tx.
 531      // We do the AlreadyHaveTx() check using wtxid, rather than txid - in the
 532      // absence of witness malleation, this is strictly better, because the
 533      // recent rejects filter may contain the wtxid but rarely contains
 534      // the txid of a segwit transaction that has been rejected.
 535      // In the presence of witness malleation, it's possible that by only
 536      // doing the check with wtxid, we could overlook a transaction which
 537      // was confirmed with a different witness, or exists in our mempool
 538      // with a different witness, but this has limited downside:
 539      // mempool validation does its own lookup of whether we have the txid
 540      // already; and an adversary can already relay us old transactions
 541      // (older than our recency filter) if trying to DoS us, without any need
 542      // for witness malleation.
 543      if (AlreadyHaveTx(GenTxid::Wtxid(wtxid), /*include_reconsiderable=*/false)) {
 544          // If a tx is detected by m_lazy_recent_rejects it is ignored. Because we haven't
 545          // submitted the tx to our mempool, we won't have computed a DoS
 546          // score for it or determined exactly why we consider it invalid.
 547          //
 548          // This means we won't penalize any peer subsequently relaying a DoSy
 549          // tx (even if we penalized the first peer who gave it to us) because
 550          // we have to account for m_lazy_recent_rejects showing false positives. In
 551          // other words, we shouldn't penalize a peer if we aren't *sure* they
 552          // submitted a DoSy tx.
 553          //
 554          // Note that m_lazy_recent_rejects doesn't just record DoSy or invalid
 555          // transactions, but any tx not accepted by the mempool, which may be
 556          // due to node policy (vs. consensus). So we can't blanket penalize a
 557          // peer simply for relaying a tx that our m_lazy_recent_rejects has caught,
 558          // regardless of false positives.
 559          return {false, std::nullopt};
 560      } else if (RecentRejectsReconsiderableFilter().contains(wtxid)) {
 561          // When a transaction is already in m_lazy_recent_rejects_reconsiderable, we shouldn't submit
 562          // it by itself again. However, look for a matching child in the orphanage, as it is
 563          // possible that they succeed as a package.
 564          LogDebug(BCLog::TXPACKAGES, "found tx %s (wtxid=%s) in reconsiderable rejects, looking for child in orphanage\n",
 565                   txid.ToString(), wtxid.ToString());
 566          return {false, Find1P1CPackage(ptx, nodeid)};
 567      }
 568  
 569  
 570      return {true, std::nullopt};
 571  }
 572  
 573  bool TxDownloadManagerImpl::HaveMoreWork(NodeId nodeid)
 574  {
 575      return m_orphanage.HaveTxToReconsider(nodeid);
 576  }
 577  
 578  CTransactionRef TxDownloadManagerImpl::GetTxToReconsider(NodeId nodeid)
 579  {
 580      return m_orphanage.GetTxToReconsider(nodeid);
 581  }
 582  
 583  void TxDownloadManagerImpl::CheckIsEmpty(NodeId nodeid)
 584  {
 585      assert(m_txrequest.Count(nodeid) == 0);
 586      assert(m_orphanage.UsageByPeer(nodeid) == 0);
 587  }
 588  void TxDownloadManagerImpl::CheckIsEmpty()
 589  {
 590      assert(m_orphanage.TotalOrphanUsage() == 0);
 591      assert(m_orphanage.Size() == 0);
 592      assert(m_txrequest.Size() == 0);
 593      assert(m_num_wtxid_peers == 0);
 594  }
 595  std::vector<TxOrphanage::OrphanTxBase> TxDownloadManagerImpl::GetOrphanTransactions() const
 596  {
 597      return m_orphanage.GetOrphanTransactions();
 598  }
 599  } // namespace node
 600