Bitcoin Core  31.0.0
P2P Digital Currency
txorphanage.cpp
Go to the documentation of this file.
1 // Copyright (c) 2021-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 
5 #include <node/txorphanage.h>
6 
7 #include <consensus/validation.h>
8 #include <logging.h>
9 #include <policy/policy.h>
10 #include <primitives/transaction.h>
11 #include <util/feefrac.h>
12 #include <util/time.h>
13 #include <util/hasher.h>
14 
15 #include <boost/multi_index/indexed_by.hpp>
16 #include <boost/multi_index/ordered_index.hpp>
17 #include <boost/multi_index/tag.hpp>
18 #include <boost/multi_index_container.hpp>
19 
20 #include <cassert>
21 #include <cmath>
22 #include <unordered_map>
23 
24 namespace node {
26 static constexpr NodeId MIN_PEER{std::numeric_limits<NodeId>::min()};
28 static constexpr NodeId MAX_PEER{std::numeric_limits<NodeId>::max()};
29 class TxOrphanageImpl final : public TxOrphanage {
30  // Type alias for sequence numbers
31  using SequenceNumber = uint64_t;
34 
37  struct Announcement
38  {
46  bool m_reconsider{false};
47 
49  m_tx{tx}, m_announcer{peer}, m_entry_sequence{seq}
50  { }
51 
58  return GetTransactionWeight(*m_tx);
59  }
60 
67  return 1 + (m_tx->vin.size() / 10);
68  }
69  };
70 
71  // Index by wtxid, then peer
72  struct ByWtxid {};
73  using ByWtxidView = std::tuple<Wtxid, NodeId>;
75  {
78  {
79  return ByWtxidView{ann.m_tx->GetWitnessHash(), ann.m_announcer};
80  }
81  };
82 
83  // Sort by peer, then by whether it is ready to reconsider, then by recency.
84  struct ByPeer {};
85  using ByPeerView = std::tuple<NodeId, bool, SequenceNumber>;
89  {
91  }
92  };
93 
94  struct OrphanIndices final : boost::multi_index::indexed_by<
95  boost::multi_index::ordered_unique<boost::multi_index::tag<ByWtxid>, WtxidExtractor>,
96  boost::multi_index::ordered_unique<boost::multi_index::tag<ByPeer>, ByPeerViewExtractor>
97  >{};
98 
99  using AnnouncementMap = boost::multi_index::multi_index_container<Announcement, OrphanIndices>;
100  template<typename Tag>
101  using Iter = typename AnnouncementMap::index<Tag>::type::iterator;
103 
106 
109 
112 
117 
120  std::unordered_map<COutPoint, std::set<Wtxid>, SaltedOutpointHasher> m_outpoint_to_orphan_wtxids;
121 
123  std::set<Wtxid> m_reconsiderable_wtxids;
124 
125  struct PeerDoSInfo {
129  bool operator==(const PeerDoSInfo& other) const
130  {
131  return m_total_usage == other.m_total_usage &&
134  }
135  void Add(const Announcement& ann)
136  {
137  m_total_usage += ann.GetMemUsage();
140  }
141  bool Subtract(const Announcement& ann)
142  {
143  Assume(m_total_usage >= ann.GetMemUsage());
146 
147  m_total_usage -= ann.GetMemUsage();
150  return m_count_announcements == 0;
151  }
161  FeeFrac GetDosScore(TxOrphanage::Count max_peer_latency_score, TxOrphanage::Usage max_peer_memory) const
162  {
163  assert(max_peer_latency_score > 0);
164  assert(max_peer_memory > 0);
165  const FeeFrac latency_score(m_total_latency_score, max_peer_latency_score);
166  const FeeFrac mem_score(m_total_usage, max_peer_memory);
167  return std::max<FeeFrac>(latency_score, mem_score);
168  }
169  };
172  std::unordered_map<NodeId, PeerDoSInfo> m_peer_orphanage_info;
173 
175  template<typename Tag>
176  void Erase(Iter<Tag> it);
177 
179  bool EraseTxInternal(const Wtxid& wtxid);
180 
182  bool IsUnique(Iter<ByWtxid> it) const;
183 
185  bool NeedsTrim() const;
186 
188  void LimitOrphans();
189 
190 public:
191  TxOrphanageImpl() = default;
192  TxOrphanageImpl(Count max_global_latency_score, Usage reserved_peer_usage) :
193  m_max_global_latency_score{max_global_latency_score},
194  m_reserved_usage_per_peer{reserved_peer_usage}
195  {}
196  ~TxOrphanageImpl() noexcept override = default;
197 
198  TxOrphanage::Count CountAnnouncements() const override;
199  TxOrphanage::Count CountUniqueOrphans() const override;
200  TxOrphanage::Count AnnouncementsFromPeer(NodeId peer) const override;
201  TxOrphanage::Count LatencyScoreFromPeer(NodeId peer) const override;
202  TxOrphanage::Usage UsageByPeer(NodeId peer) const override;
203 
204  TxOrphanage::Count MaxGlobalLatencyScore() const override;
205  TxOrphanage::Count TotalLatencyScore() const override;
206  TxOrphanage::Usage ReservedPeerUsage() const override;
207 
212  TxOrphanage::Count MaxPeerLatencyScore() const override;
213 
218  TxOrphanage::Usage MaxGlobalUsage() const override;
219 
220  bool AddTx(const CTransactionRef& tx, NodeId peer) override;
221  bool AddAnnouncer(const Wtxid& wtxid, NodeId peer) override;
222  CTransactionRef GetTx(const Wtxid& wtxid) const override;
223  bool HaveTx(const Wtxid& wtxid) const override;
224  bool HaveTxFromPeer(const Wtxid& wtxid, NodeId peer) const override;
226  bool EraseTx(const Wtxid& wtxid) override;
227  void EraseForPeer(NodeId peer) override;
228  void EraseForBlock(const CBlock& block) override;
229  std::vector<std::pair<Wtxid, NodeId>> AddChildrenToWorkSet(const CTransaction& tx, FastRandomContext& rng) override;
230  bool HaveTxToReconsider(NodeId peer) override;
231  std::vector<CTransactionRef> GetChildrenFromSamePeer(const CTransactionRef& parent, NodeId nodeid) const override;
232  std::vector<OrphanInfo> GetOrphanTransactions() const override;
233  TxOrphanage::Usage TotalOrphanUsage() const override;
234  void SanityCheck() const override;
235 };
236 
237 template<typename Tag>
238 void TxOrphanageImpl::Erase(Iter<Tag> it)
239 {
240  // Update m_peer_orphanage_info and clean up entries if they point to an empty struct.
241  // This means peers that are not storing any orphans do not have an entry in
242  // m_peer_orphanage_info (they can be added back later if they announce another orphan) and
243  // ensures disconnected peers are not tracked forever.
244  auto peer_it = m_peer_orphanage_info.find(it->m_announcer);
245  Assume(peer_it != m_peer_orphanage_info.end());
246  if (peer_it->second.Subtract(*it)) m_peer_orphanage_info.erase(peer_it);
247 
248  if (IsUnique(m_orphans.project<ByWtxid>(it))) {
249  m_unique_orphans -= 1;
250  m_unique_rounded_input_scores -= it->GetLatencyScore() - 1;
251  m_unique_orphan_usage -= it->GetMemUsage();
252 
253  // Remove references in m_outpoint_to_orphan_wtxids
254  const auto& wtxid{it->m_tx->GetWitnessHash()};
255  for (const auto& input : it->m_tx->vin) {
256  auto it_prev = m_outpoint_to_orphan_wtxids.find(input.prevout);
257  if (it_prev != m_outpoint_to_orphan_wtxids.end()) {
258  it_prev->second.erase(wtxid);
259  // Clean up keys if they point to an empty set.
260  if (it_prev->second.empty()) {
261  m_outpoint_to_orphan_wtxids.erase(it_prev);
262  }
263  }
264  }
265  }
266 
267  // If this was the (unique) reconsiderable announcement for its wtxid, then the wtxid won't
268  // have any reconsiderable announcements left after erasing.
269  if (it->m_reconsider) m_reconsiderable_wtxids.erase(it->m_tx->GetWitnessHash());
270 
271  m_orphans.get<Tag>().erase(it);
272 }
273 
275 {
276  // Iterators ByWtxid are sorted by wtxid, so check if neighboring elements have the same wtxid.
277  auto& index = m_orphans.get<ByWtxid>();
278  if (it == index.end()) return false;
279  if (std::next(it) != index.end() && std::next(it)->m_tx->GetWitnessHash() == it->m_tx->GetWitnessHash()) return false;
280  if (it != index.begin() && std::prev(it)->m_tx->GetWitnessHash() == it->m_tx->GetWitnessHash()) return false;
281  return true;
282 }
283 
285 {
286  auto it = m_peer_orphanage_info.find(peer);
287  return it == m_peer_orphanage_info.end() ? 0 : it->second.m_total_usage;
288 }
289 
291 
293 
295 
297  auto it = m_peer_orphanage_info.find(peer);
298  return it == m_peer_orphanage_info.end() ? 0 : it->second.m_count_announcements;
299 }
300 
302  auto it = m_peer_orphanage_info.find(peer);
303  return it == m_peer_orphanage_info.end() ? 0 : it->second.m_total_latency_score;
304 }
305 
307 {
308  const auto& wtxid{tx->GetWitnessHash()};
309  const auto& txid{tx->GetHash()};
310 
311  // Ignore transactions above max standard size to avoid a send-big-orphans memory exhaustion attack.
313  if (sz > MAX_STANDARD_TX_WEIGHT) {
314  LogDebug(BCLog::TXPACKAGES, "ignoring large orphan tx (size: %u, txid: %s, wtxid: %s)\n", sz, txid.ToString(), wtxid.ToString());
315  return false;
316  }
317 
318  // We will return false if the tx already exists under a different peer.
319  const bool brand_new{!HaveTx(wtxid)};
320 
321  auto [iter, inserted] = m_orphans.get<ByWtxid>().emplace(tx, peer, m_current_sequence);
322  // If the announcement (same wtxid, same peer) already exists, emplacement fails. Return false.
323  if (!inserted) return false;
324 
326  auto& peer_info = m_peer_orphanage_info.try_emplace(peer).first->second;
327  peer_info.Add(*iter);
328 
329  // Add links in m_outpoint_to_orphan_wtxids
330  if (brand_new) {
331  for (const auto& input : tx->vin) {
332  auto& wtxids_for_prevout = m_outpoint_to_orphan_wtxids.try_emplace(input.prevout).first->second;
333  wtxids_for_prevout.emplace(wtxid);
334  }
335 
336  m_unique_orphans += 1;
337  m_unique_orphan_usage += iter->GetMemUsage();
338  m_unique_rounded_input_scores += iter->GetLatencyScore() - 1;
339 
340  LogDebug(BCLog::TXPACKAGES, "stored orphan tx %s (wtxid=%s), weight: %u (mapsz %u outsz %u)\n",
341  txid.ToString(), wtxid.ToString(), sz, m_orphans.size(), m_outpoint_to_orphan_wtxids.size());
342  Assume(IsUnique(iter));
343  } else {
344  LogDebug(BCLog::TXPACKAGES, "added peer=%d as announcer of orphan tx %s (wtxid=%s)\n",
345  peer, txid.ToString(), wtxid.ToString());
346  Assume(!IsUnique(iter));
347  }
348 
349  // DoS prevention: do not allow m_orphanage to grow unbounded (see CVE-2012-3789)
350  LimitOrphans();
351  return brand_new;
352 }
353 
355 {
356  auto& index_by_wtxid = m_orphans.get<ByWtxid>();
357  auto it = index_by_wtxid.lower_bound(ByWtxidView{wtxid, MIN_PEER});
358 
359  // Do nothing if this transaction isn't already present. We can't create an entry if we don't
360  // have the tx data.
361  if (it == index_by_wtxid.end()) return false;
362  if (it->m_tx->GetWitnessHash() != wtxid) return false;
363 
364  // Add another announcement, copying the CTransactionRef from one that already exists.
365  const auto& ptx = it->m_tx;
366  auto [iter, inserted] = index_by_wtxid.emplace(ptx, peer, m_current_sequence);
367  // If the announcement (same wtxid, same peer) already exists, emplacement fails. Return false.
368  if (!inserted) return false;
369 
371  auto& peer_info = m_peer_orphanage_info.try_emplace(peer).first->second;
372  peer_info.Add(*iter);
373 
374  const auto& txid = ptx->GetHash();
375  LogDebug(BCLog::TXPACKAGES, "added peer=%d as announcer of orphan tx %s (wtxid=%s)\n",
376  peer, txid.ToString(), wtxid.ToString());
377 
378  Assume(!IsUnique(iter));
379 
380  // DoS prevention: do not allow m_orphanage to grow unbounded (see CVE-2012-3789)
381  LimitOrphans();
382  return true;
383 }
384 
386 {
387  auto& index_by_wtxid = m_orphans.get<ByWtxid>();
388 
389  auto it = index_by_wtxid.lower_bound(ByWtxidView{wtxid, MIN_PEER});
390  if (it == index_by_wtxid.end() || it->m_tx->GetWitnessHash() != wtxid) return false;
391 
392  auto it_end = index_by_wtxid.upper_bound(ByWtxidView{wtxid, MAX_PEER});
393  unsigned int num_ann{0};
394  const auto txid = it->m_tx->GetHash();
395  while (it != it_end) {
396  Assume(it->m_tx->GetWitnessHash() == wtxid);
397  Erase<ByWtxid>(it++);
398  num_ann += 1;
399  }
400  LogDebug(BCLog::TXPACKAGES, "removed orphan tx %s (wtxid=%s) (%u announcements)\n", txid.ToString(), wtxid.ToString(), num_ann);
401 
402  return true;
403 }
404 
405 bool TxOrphanageImpl::EraseTx(const Wtxid& wtxid)
406 {
407  const auto ret = EraseTxInternal(wtxid);
408 
409  // Deletions can cause the orphanage's MaxGlobalUsage to decrease, so we may need to trim here.
410  LimitOrphans();
411 
412  return ret;
413 }
414 
417 {
418  auto& index_by_peer = m_orphans.get<ByPeer>();
419  auto it = index_by_peer.lower_bound(ByPeerView{peer, false, 0});
420  if (it == index_by_peer.end() || it->m_announcer != peer) return;
421 
422  unsigned int num_ann{0};
423  while (it != index_by_peer.end() && it->m_announcer == peer) {
424  // Delete item, cleaning up m_outpoint_to_orphan_wtxids iff this entry is unique by wtxid.
425  Erase<ByPeer>(it++);
426  num_ann += 1;
427  }
428  Assume(!m_peer_orphanage_info.contains(peer));
429 
430  if (num_ann > 0) LogDebug(BCLog::TXPACKAGES, "Erased %d orphan transaction(s) from peer=%d\n", num_ann, peer);
431 
432  // Deletions can cause the orphanage's MaxGlobalUsage to decrease, so we may need to trim here.
433  LimitOrphans();
434 }
435 
443 {
444  if (!NeedsTrim()) return;
445 
446  const auto original_unique_txns{CountUniqueOrphans()};
447 
448  // Even though it's possible for MaxPeerLatencyScore to increase within this call to LimitOrphans
449  // (e.g. if a peer's orphans are removed entirely, changing the number of peers), use consistent limits throughout.
450  const auto max_lat{MaxPeerLatencyScore()};
451  const auto max_mem{ReservedPeerUsage()};
452 
453  // We have exceeded the global limit(s). Now, identify who is using too much and evict their orphans.
454  // Create a heap of pairs (NodeId, DoS score), sorted by descending DoS score.
455  std::vector<std::pair<NodeId, FeeFrac>> heap_peer_dos;
456  heap_peer_dos.reserve(m_peer_orphanage_info.size());
457  for (const auto& [nodeid, entry] : m_peer_orphanage_info) {
458  // Performance optimization: only consider peers with a DoS score > 1.
459  const auto dos_score = entry.GetDosScore(max_lat, max_mem);
460  if (dos_score >> FeeFrac{1, 1}) {
461  heap_peer_dos.emplace_back(nodeid, dos_score);
462  }
463  }
464  static constexpr auto compare_score = [](const auto& left, const auto& right) {
465  if (left.second != right.second) return left.second < right.second;
466  // Tiebreak by considering the more recent peer (higher NodeId) to be worse.
467  return left.first < right.first;
468  };
469  std::make_heap(heap_peer_dos.begin(), heap_peer_dos.end(), compare_score);
470 
471  unsigned int num_erased{0};
472  // This outer loop finds the peer with the highest DoS score, which is a fraction of memory and latency scores
473  // over the respective allowances. We continue until the orphanage is within global limits. That means some peers
474  // might still have a DoS score > 1 at the end.
475  // Note: if ratios are the same, FeeFrac tiebreaks by denominator. In practice, since the latency denominator (number of
476  // announcements and inputs) is always lower, this means that a peer with only high latency scores will be targeted
477  // before a peer using a lot of memory, even if they have the same ratios.
478  do {
479  Assume(!heap_peer_dos.empty());
480  // This is a max-heap, so the worst peer is at the front. pop_heap()
481  // moves it to the back, and the next worst peer is moved to the front.
482  std::pop_heap(heap_peer_dos.begin(), heap_peer_dos.end(), compare_score);
483  const auto [worst_peer, dos_score] = std::move(heap_peer_dos.back());
484  heap_peer_dos.pop_back();
485 
486  // If needs trim, then at least one peer has a DoS score higher than 1.
487  Assume(dos_score >> (FeeFrac{1, 1}));
488 
489  auto it_worst_peer = m_peer_orphanage_info.find(worst_peer);
490 
491  // This inner loop trims until this peer is no longer the DoSiest one or has a score within 1. The score 1 is
492  // just a conservative fallback: once the last peer goes below ratio 1, NeedsTrim() will return false anyway.
493  // We evict the oldest announcement(s) from this peer, sorting non-reconsiderable before reconsiderable.
494  // The number of inner loop iterations is bounded by the total number of announcements.
495  const auto& dos_threshold = heap_peer_dos.empty() ? FeeFrac{1, 1} : heap_peer_dos.front().second;
496  auto it_ann = m_orphans.get<ByPeer>().lower_bound(ByPeerView{worst_peer, false, 0});
497  unsigned int num_erased_this_round{0};
498  unsigned int starting_num_ann{it_worst_peer->second.m_count_announcements};
499  while (NeedsTrim()) {
500  if (!Assume(it_ann != m_orphans.get<ByPeer>().end())) break;
501  if (!Assume(it_ann->m_announcer == worst_peer)) break;
502 
503  Erase<ByPeer>(it_ann++);
504  num_erased += 1;
505  num_erased_this_round += 1;
506 
507  // If we erased the last orphan from this peer, it_worst_peer will be invalidated.
508  it_worst_peer = m_peer_orphanage_info.find(worst_peer);
509  if (it_worst_peer == m_peer_orphanage_info.end() || it_worst_peer->second.GetDosScore(max_lat, max_mem) <= dos_threshold) break;
510  }
511  LogDebug(BCLog::TXPACKAGES, "peer=%d orphanage overflow, removed %u of %u announcements\n", worst_peer, num_erased_this_round, starting_num_ann);
512 
513  if (!NeedsTrim()) break;
514 
515  // Unless this peer is empty, put it back in the heap so we continue to consider evicting its orphans.
516  // We may select this peer for evictions again if there are multiple DoSy peers.
517  if (it_worst_peer != m_peer_orphanage_info.end() && it_worst_peer->second.m_count_announcements > 0) {
518  heap_peer_dos.emplace_back(worst_peer, it_worst_peer->second.GetDosScore(max_lat, max_mem));
519  std::push_heap(heap_peer_dos.begin(), heap_peer_dos.end(), compare_score);
520  }
521  } while (true);
522 
523  const auto remaining_unique_orphans{CountUniqueOrphans()};
524  LogDebug(BCLog::TXPACKAGES, "orphanage overflow, removed %u tx (%u announcements)\n", original_unique_txns - remaining_unique_orphans, num_erased);
525 }
526 
527 std::vector<std::pair<Wtxid, NodeId>> TxOrphanageImpl::AddChildrenToWorkSet(const CTransaction& tx, FastRandomContext& rng)
528 {
529  std::vector<std::pair<Wtxid, NodeId>> ret;
530  auto& index_by_wtxid = m_orphans.get<ByWtxid>();
531  for (unsigned int i = 0; i < tx.vout.size(); i++) {
532  const auto it_by_prev = m_outpoint_to_orphan_wtxids.find(COutPoint(tx.GetHash(), i));
533  if (it_by_prev != m_outpoint_to_orphan_wtxids.end()) {
534  for (const auto& wtxid : it_by_prev->second) {
535  // If a reconsiderable announcement for this wtxid already exists, skip it.
536  if (m_reconsiderable_wtxids.contains(wtxid)) continue;
537 
538  // Belt and suspenders, each entry in m_outpoint_to_orphan_wtxids should always have at least 1 announcement.
539  auto it = index_by_wtxid.lower_bound(ByWtxidView{wtxid, MIN_PEER});
540  if (!Assume(it != index_by_wtxid.end() && it->m_tx->GetWitnessHash() == wtxid)) continue;
541 
542  // Select a random peer to assign orphan processing, reducing wasted work if the orphan is still missing
543  // inputs. However, we don't want to create an issue in which the assigned peer can purposefully stop us
544  // from processing the orphan by disconnecting.
545  auto it_end = index_by_wtxid.upper_bound(ByWtxidView{wtxid, MAX_PEER});
546  const auto num_announcers{std::distance(it, it_end)};
547  if (!Assume(num_announcers > 0)) continue;
548  std::advance(it, rng.randrange(num_announcers));
549 
550  if (!Assume(it->m_tx->GetWitnessHash() == wtxid)) break;
551 
552  // Mark this orphan as ready to be reconsidered.
553  static constexpr auto mark_reconsidered_modifier = [](auto& ann) { ann.m_reconsider = true; };
554  Assume(!it->m_reconsider);
555  index_by_wtxid.modify(it, mark_reconsidered_modifier);
556  ret.emplace_back(wtxid, it->m_announcer);
557  m_reconsiderable_wtxids.insert(wtxid);
558 
559  LogDebug(BCLog::TXPACKAGES, "added %s (wtxid=%s) to peer %d workset\n",
560  it->m_tx->GetHash().ToString(), it->m_tx->GetWitnessHash().ToString(), it->m_announcer);
561  }
562  }
563  }
564  return ret;
565 }
566 
567 bool TxOrphanageImpl::HaveTx(const Wtxid& wtxid) const
568 {
569  auto it_lower = m_orphans.get<ByWtxid>().lower_bound(ByWtxidView{wtxid, MIN_PEER});
570  return it_lower != m_orphans.get<ByWtxid>().end() && it_lower->m_tx->GetWitnessHash() == wtxid;
571 }
572 
574 {
575  auto it_lower = m_orphans.get<ByWtxid>().lower_bound(ByWtxidView{wtxid, MIN_PEER});
576  if (it_lower != m_orphans.get<ByWtxid>().end() && it_lower->m_tx->GetWitnessHash() == wtxid) return it_lower->m_tx;
577  return nullptr;
578 }
579 
580 bool TxOrphanageImpl::HaveTxFromPeer(const Wtxid& wtxid, NodeId peer) const
581 {
582  return m_orphans.get<ByWtxid>().count(ByWtxidView{wtxid, peer}) > 0;
583 }
584 
588 {
589  auto it = m_orphans.get<ByPeer>().lower_bound(ByPeerView{peer, true, 0});
590  if (it != m_orphans.get<ByPeer>().end() && it->m_announcer == peer && it->m_reconsider) {
591  // Flip m_reconsider. Even if this transaction stays in orphanage, it shouldn't be
592  // reconsidered again until there is a new reason to do so.
593  static constexpr auto mark_reconsidered_modifier = [](auto& ann) { ann.m_reconsider = false; };
594  m_orphans.get<ByPeer>().modify(it, mark_reconsidered_modifier);
595  // As there is exactly one m_reconsider announcement per reconsiderable wtxids, flipping
596  // the m_reconsider flag means the wtxid is no longer reconsiderable.
597  m_reconsiderable_wtxids.erase(it->m_tx->GetWitnessHash());
598  return it->m_tx;
599  }
600  return nullptr;
601 }
602 
605 {
606  auto it = m_orphans.get<ByPeer>().lower_bound(ByPeerView{peer, true, 0});
607  return it != m_orphans.get<ByPeer>().end() && it->m_announcer == peer && it->m_reconsider;
608 }
609 
611 {
612  if (m_orphans.empty()) return;
613 
614  std::set<Wtxid> wtxids_to_erase;
615  for (const CTransactionRef& ptx : block.vtx) {
616  const CTransaction& block_tx = *ptx;
617 
618  // Which orphan pool entries must we evict?
619  for (const auto& input : block_tx.vin) {
620  auto it_prev = m_outpoint_to_orphan_wtxids.find(input.prevout);
621  if (it_prev != m_outpoint_to_orphan_wtxids.end()) {
622  // Copy all wtxids to wtxids_to_erase.
623  std::copy(it_prev->second.cbegin(), it_prev->second.cend(), std::inserter(wtxids_to_erase, wtxids_to_erase.end()));
624  }
625  }
626  }
627 
628  unsigned int num_erased{0};
629  for (const auto& wtxid : wtxids_to_erase) {
630  // Don't use EraseTx here because it calls LimitOrphans and announcements deleted in that call are not reflected
631  // in its return result. Waiting until the end to do LimitOrphans helps save repeated computation and allows us
632  // to check that num_erased is what we expect.
633  num_erased += EraseTxInternal(wtxid) ? 1 : 0;
634  }
635 
636  if (num_erased != 0) {
637  LogDebug(BCLog::TXPACKAGES, "Erased %d orphan transaction(s) included or conflicted by block\n", num_erased);
638  }
639  Assume(wtxids_to_erase.size() == num_erased);
640 
641  // Deletions can cause the orphanage's MaxGlobalUsage to decrease, so we may need to trim here.
642  LimitOrphans();
643 }
644 
645 std::vector<CTransactionRef> TxOrphanageImpl::GetChildrenFromSamePeer(const CTransactionRef& parent, NodeId peer) const
646 {
647  std::vector<CTransactionRef> children_found;
648  const auto& parent_txid{parent->GetHash()};
649 
650  // Iterate through all orphans from this peer, in reverse order, so that more recent
651  // transactions are added first. Doing so helps avoid work when one of the orphans replaced
652  // an earlier one. Since we require the NodeId to match, one peer's announcement order does
653  // not bias how we process other peer's orphans.
654  auto& index_by_peer = m_orphans.get<ByPeer>();
655  auto it_upper = index_by_peer.upper_bound(ByPeerView{peer, true, std::numeric_limits<uint64_t>::max()});
656  auto it_lower = index_by_peer.lower_bound(ByPeerView{peer, false, 0});
657 
658  while (it_upper != it_lower) {
659  --it_upper;
660  if (!Assume(it_upper->m_announcer == peer)) break;
661  // Check if this tx spends from parent.
662  for (const auto& input : it_upper->m_tx->vin) {
663  if (input.prevout.hash == parent_txid) {
664  children_found.emplace_back(it_upper->m_tx);
665  break;
666  }
667  }
668  }
669  return children_found;
670 }
671 
672 std::vector<TxOrphanage::OrphanInfo> TxOrphanageImpl::GetOrphanTransactions() const
673 {
674  std::vector<TxOrphanage::OrphanInfo> result;
675  result.reserve(m_unique_orphans);
676 
677  auto& index_by_wtxid = m_orphans.get<ByWtxid>();
678  auto it = index_by_wtxid.begin();
679  std::set<NodeId> this_orphan_announcers;
680  while (it != index_by_wtxid.end()) {
681  this_orphan_announcers.insert(it->m_announcer);
682  // If this is the last entry, or the next entry has a different wtxid, build a OrphanInfo.
683  if (std::next(it) == index_by_wtxid.end() || std::next(it)->m_tx->GetWitnessHash() != it->m_tx->GetWitnessHash()) {
684  result.emplace_back(it->m_tx, std::move(this_orphan_announcers));
685  this_orphan_announcers.clear();
686  }
687 
688  ++it;
689  }
690  Assume(m_unique_orphans == result.size());
691 
692  return result;
693 }
694 
696 {
697  std::unordered_map<NodeId, PeerDoSInfo> reconstructed_peer_info;
698  std::map<Wtxid, std::pair<TxOrphanage::Usage, TxOrphanage::Count>> unique_wtxids_to_scores;
699  std::set<COutPoint> all_outpoints;
700  std::set<Wtxid> reconstructed_reconsiderable_wtxids;
701 
702  for (auto it = m_orphans.begin(); it != m_orphans.end(); ++it) {
703  for (const auto& input : it->m_tx->vin) {
704  all_outpoints.insert(input.prevout);
705  }
706  unique_wtxids_to_scores.emplace(it->m_tx->GetWitnessHash(), std::make_pair(it->GetMemUsage(), it->GetLatencyScore() - 1));
707 
708  auto& peer_info = reconstructed_peer_info[it->m_announcer];
709  peer_info.m_total_usage += it->GetMemUsage();
710  peer_info.m_count_announcements += 1;
711  peer_info.m_total_latency_score += it->GetLatencyScore();
712 
713  if (it->m_reconsider) {
714  auto [_, added] = reconstructed_reconsiderable_wtxids.insert(it->m_tx->GetWitnessHash());
715  // Check that there is only ever 1 announcement per wtxid with m_reconsider set.
716  assert(added);
717  }
718  }
719  assert(reconstructed_peer_info.size() == m_peer_orphanage_info.size());
720 
721  // Recalculated per-peer stats are identical to m_peer_orphanage_info
722  assert(reconstructed_peer_info == m_peer_orphanage_info);
723 
724  // Recalculated set of reconsiderable wtxids must match.
725  assert(m_reconsiderable_wtxids == reconstructed_reconsiderable_wtxids);
726 
727  // All outpoints exist in m_outpoint_to_orphan_wtxids, all keys in m_outpoint_to_orphan_wtxids correspond to some
728  // orphan, and all wtxids referenced in m_outpoint_to_orphan_wtxids are also in m_orphans.
729  // This ensures m_outpoint_to_orphan_wtxids is cleaned up.
730  assert(all_outpoints.size() == m_outpoint_to_orphan_wtxids.size());
731  for (const auto& [outpoint, wtxid_set] : m_outpoint_to_orphan_wtxids) {
732  assert(all_outpoints.contains(outpoint));
733  for (const auto& wtxid : wtxid_set) {
734  assert(unique_wtxids_to_scores.contains(wtxid));
735  }
736  }
737 
738  // Cached m_unique_orphans value is correct.
739  assert(m_orphans.size() >= m_unique_orphans);
741  assert(unique_wtxids_to_scores.size() == m_unique_orphans);
742 
743  const auto calculated_dedup_usage = std::accumulate(unique_wtxids_to_scores.begin(), unique_wtxids_to_scores.end(),
744  TxOrphanage::Usage{0}, [](TxOrphanage::Usage sum, const auto pair) { return sum + pair.second.first; });
745  assert(calculated_dedup_usage == m_unique_orphan_usage);
746 
747  // Global usage is deduplicated, should be less than or equal to the sum of all per-peer usages.
748  const auto summed_peer_usage = std::accumulate(m_peer_orphanage_info.begin(), m_peer_orphanage_info.end(),
749  TxOrphanage::Usage{0}, [](TxOrphanage::Usage sum, const auto pair) { return sum + pair.second.m_total_usage; });
750  assert(summed_peer_usage >= m_unique_orphan_usage);
751 
752  // Cached m_unique_rounded_input_scores value is correct.
753  const auto calculated_total_latency_score = std::accumulate(unique_wtxids_to_scores.begin(), unique_wtxids_to_scores.end(),
754  TxOrphanage::Count{0}, [](TxOrphanage::Count sum, const auto pair) { return sum + pair.second.second; });
755  assert(calculated_total_latency_score == m_unique_rounded_input_scores);
756 
757  // Global latency score is deduplicated, should be less than or equal to the sum of all per-peer latency scores.
758  const auto summed_peer_latency_score = std::accumulate(m_peer_orphanage_info.begin(), m_peer_orphanage_info.end(),
759  TxOrphanage::Count{0}, [](TxOrphanage::Count sum, const auto pair) { return sum + pair.second.m_total_latency_score; });
760  assert(summed_peer_latency_score >= m_unique_rounded_input_scores + m_orphans.size());
761 
762  assert(!NeedsTrim());
763 }
764 
770 
772 {
774 }
775 std::unique_ptr<TxOrphanage> MakeTxOrphanage() noexcept
776 {
777  return std::make_unique<TxOrphanageImpl>();
778 }
779 std::unique_ptr<TxOrphanage> MakeTxOrphanage(TxOrphanage::Count max_global_latency_score, TxOrphanage::Usage reserved_peer_usage) noexcept
780 {
781  return std::make_unique<TxOrphanageImpl>(max_global_latency_score, reserved_peer_usage);
782 }
783 } // namespace node
std::shared_ptr< const CTransaction > CTransactionRef
Definition: transaction.h:403
TxOrphanageImpl(Count max_global_latency_score, Usage reserved_peer_usage)
bool NeedsTrim() const
Check if the orphanage needs trimming.
TxOrphanage::Usage UsageByPeer(NodeId peer) const override
Total usage (weight) of orphans for which this peer is an announcer.
int ret
TxOrphanage::Usage ReservedPeerUsage() const override
Get the reserved usage per peer.
bool HaveTx(const Wtxid &wtxid) const override
Check if we already have an orphan transaction (by wtxid only)
boost::multi_index::multi_index_container< Announcement, OrphanIndices > AnnouncementMap
Definition: txorphanage.cpp:99
assert(!tx.IsCoinBase())
TxOrphanage::Count MaxPeerLatencyScore() const override
Maximum allowed (deduplicated) latency score for all transactions (see Announcement::GetLatencyScore(...
result_type operator()(const Announcement &ann) const
Definition: txorphanage.cpp:88
Definition: block.h:73
TxOrphanage::Count TotalLatencyScore() const override
Get the total latency score of all orphans.
void Add(const Announcement &ann)
bool m_reconsider
Whether this tx should be reconsidered.
Definition: txorphanage.cpp:46
TxOrphanage::Count m_unique_orphans
Number of unique orphans by wtxid.
A class to track orphan transactions (failed on TX_MISSING_INPUTS) Since we cannot distinguish orphan...
Definition: txorphanage.h:38
const SequenceNumber m_entry_sequence
What order this transaction entered the orphanage.
Definition: txorphanage.cpp:43
bool EraseTxInternal(const Wtxid &wtxid)
Erase by wtxid.
Definition: common.h:29
std::unordered_map< COutPoint, std::set< Wtxid >, SaltedOutpointHasher > m_outpoint_to_orphan_wtxids
Index from the parents&#39; outputs to wtxids that exist in m_orphans.
bool HaveTxToReconsider(NodeId peer) override
Return whether there is a tx that can be reconsidered.
TxOrphanage::Count m_unique_rounded_input_scores
The sum of each unique transaction&#39;s latency scores including the inputs only (see Announcement::GetL...
consteval auto _(util::TranslatedLiteral str)
Definition: translation.h:79
static int32_t GetTransactionWeight(const CTransaction &tx)
Definition: validation.h:132
TxOrphanage::Count GetLatencyScore() const
Get an approximation of how much this transaction contributes to latency in EraseForBlock and EraseFo...
Definition: txorphanage.cpp:66
std::vector< OrphanInfo > GetOrphanTransactions() const override
Get all orphan transactions.
bool EraseTx(const Wtxid &wtxid) override
Erase an orphan by wtxid, including all announcements if there are multiple.
static constexpr unsigned int DEFAULT_MAX_ORPHANAGE_LATENCY_SCORE
Default value for TxOrphanage::m_max_global_latency_score.
Definition: txorphanage.h:23
const std::vector< CTxIn > vin
Definition: transaction.h:291
const TxOrphanage::Usage m_reserved_usage_per_peer
volatile double sum
Definition: examples.cpp:10
bool HaveTxFromPeer(const Wtxid &wtxid, NodeId peer) const override
Check if a {tx, peer} exists in the orphanage.
FeeFrac GetDosScore(TxOrphanage::Count max_peer_latency_score, TxOrphanage::Usage max_peer_memory) const
There are 2 DoS scores:
bool IsUnique(Iter< ByWtxid > it) const
Check if there is exactly one announcement with the same wtxid as it.
Announcement(const CTransactionRef &tx, NodeId peer, SequenceNumber seq)
Definition: txorphanage.cpp:48
bool AddTx(const CTransactionRef &tx, NodeId peer) override
Add a new orphan transaction.
TxOrphanage::Usage MaxGlobalUsage() const override
Maximum allowed (deduplicated) memory usage for all transactions (see Announcement::GetMemUsage()).
TxOrphanage::Count m_total_latency_score
CTransactionRef GetTxToReconsider(NodeId peer) override
If there is a tx that can be reconsidered, return it and set it back to non-reconsiderable.
Fast randomness source.
Definition: random.h:385
std::tuple< Wtxid, NodeId > ByWtxidView
Definition: txorphanage.cpp:73
std::vector< std::pair< Wtxid, NodeId > > AddChildrenToWorkSet(const CTransaction &tx, FastRandomContext &rng) override
Add any orphans that list a particular tx as a parent into the from peer&#39;s work set.
TxOrphanage::Count LatencyScoreFromPeer(NodeId peer) const override
Latency score of transactions announced by this peer.
const std::vector< CTxOut > vout
Definition: transaction.h:292
std::vector< CTransactionRef > GetChildrenFromSamePeer(const CTransactionRef &parent, NodeId nodeid) const override
Get all children that spend from this tx and were received from nodeid.
std::string ToString() const
const TxOrphanage::Count m_max_global_latency_score
int64_t NodeId
Definition: net.h:103
CTransactionRef GetTx(const Wtxid &wtxid) const override
Get a transaction by its witness txid.
result_type operator()(const Announcement &ann) const
Definition: txorphanage.cpp:77
TxOrphanage::Count m_count_announcements
void EraseForBlock(const CBlock &block) override
Erase all orphans included in or invalidated by a new block.
void EraseForPeer(NodeId peer) override
Erase all entries by this peer.
An outpoint - a combination of a transaction hash and an index n into its vout.
Definition: transaction.h:28
unsigned int Count
Definition: txorphanage.h:41
#define Assume(val)
Assume is the identity function.
Definition: check.h:125
Definition: messages.h:21
static constexpr int64_t DEFAULT_RESERVED_ORPHAN_WEIGHT_PER_PEER
Default value for TxOrphanage::m_reserved_usage_per_peer.
Definition: txorphanage.h:20
static constexpr NodeId MAX_PEER
Maximum NodeId for upper_bound lookups.
Definition: txorphanage.cpp:28
std::unique_ptr< TxOrphanage > MakeTxOrphanage() noexcept
Create a new TxOrphanage instance.
#define LogDebug(category,...)
Definition: log.h:115
std::unordered_map< NodeId, PeerDoSInfo > m_peer_orphanage_info
Store per-peer statistics.
TxOrphanage::Count CountUniqueOrphans() const override
Number of unique orphans (by wtxid).
static constexpr int32_t MAX_STANDARD_TX_WEIGHT
The maximum weight for transactions we&#39;re willing to relay/mine.
Definition: policy.h:37
One orphan announcement.
Definition: txorphanage.cpp:37
TxOrphanage::Usage TotalOrphanUsage() const override
Get the total usage (weight) of all orphans.
std::vector< CTransactionRef > vtx
Definition: block.h:77
TxOrphanage::Count AnnouncementsFromPeer(NodeId peer) const override
Number of orphans stored from this peer.
void SanityCheck() const override
Check consistency between PeerOrphanInfo and m_orphans.
Data structure storing a fee and size, ordered by increasing fee/size.
Definition: feefrac.h:39
auto result
Definition: common-types.h:74
void LimitOrphans()
Limit the orphanage to MaxGlobalLatencyScore and MaxGlobalUsage.
bool Subtract(const Announcement &ann)
TxOrphanage::Count MaxGlobalLatencyScore() const override
Get the maximum global latency score allowed.
TxOrphanage::Count CountAnnouncements() const override
Number of announcements, i.e.
TxOrphanage::Usage m_unique_orphan_usage
Memory used by orphans (see Announcement::GetMemUsage()), deduplicated by wtxid.
std::set< Wtxid > m_reconsiderable_wtxids
Set of Wtxids for which (exactly) one announcement with m_reconsider=true exists. ...
I randrange(I range) noexcept
Generate a random integer in the range [0..range), with range > 0.
Definition: random.h:254
static int count
std::tuple< NodeId, bool, SequenceNumber > ByPeerView
Definition: txorphanage.cpp:85
AnnouncementMap m_orphans
The basic transaction that is broadcasted on the network and contained in blocks. ...
Definition: transaction.h:280
TxOrphanage::Usage GetMemUsage() const
Get an approximation for "memory usage".
Definition: txorphanage.cpp:57
static constexpr NodeId MIN_PEER
Minimum NodeId for lower_bound lookups (in practice, NodeIds start at 0).
Definition: txorphanage.cpp:26
typename AnnouncementMap::index< Tag >::type::iterator Iter
const NodeId m_announcer
Which peer announced this tx.
Definition: txorphanage.cpp:41
SequenceNumber m_current_sequence
Global sequence number, increment each time an announcement is added.
Definition: txorphanage.cpp:33
const Txid & GetHash() const LIFETIMEBOUND
Definition: transaction.h:328
void Erase(Iter< Tag > it)
Erase from m_orphans and update m_peer_orphanage_info.
bool operator==(const PeerDoSInfo &other) const
bool AddAnnouncer(const Wtxid &wtxid, NodeId peer) override
Add an additional announcer to an orphan if it exists.
transaction_identifier represents the two canonical transaction identifier types (txid, wtxid).
~TxOrphanageImpl() noexcept override=default