LCOV - code coverage report
Current view: top level - src - txorphanage.cpp (source / functions) Coverage Total Hit
Test: total_coverage.info Lines: 88.5 % 192 170
Test Date: 2025-02-19 05:07:00 Functions: 93.8 % 16 15
Branches: 69.2 % 182 126

             Branch data     Line data    Source code
       1                 :             : // Copyright (c) 2021-2022 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 <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/time.h>
      12                 :             : 
      13                 :             : #include <cassert>
      14                 :             : 
      15                 :         575 : bool TxOrphanage::AddTx(const CTransactionRef& tx, NodeId peer)
      16                 :             : {
      17                 :         575 :     const Txid& hash = tx->GetHash();
      18                 :         575 :     const Wtxid& wtxid = tx->GetWitnessHash();
      19         [ +  + ]:         575 :     if (auto it{m_orphans.find(wtxid)}; it != m_orphans.end()) {
      20                 :           8 :         AddAnnouncer(wtxid, peer);
      21                 :             :         // No new orphan entry was created. An announcer may have been added.
      22                 :           8 :         return false;
      23                 :             :     }
      24                 :             : 
      25                 :             :     // Ignore big transactions, to avoid a
      26                 :             :     // send-big-orphans memory exhaustion attack. If a peer has a legitimate
      27                 :             :     // large transaction with a missing parent then we assume
      28                 :             :     // it will rebroadcast it later, after the parent transaction(s)
      29                 :             :     // have been mined or received.
      30                 :             :     // 100 orphans, each of which is at most 100,000 bytes big is
      31                 :             :     // at most 10 megabytes of orphans and somewhat more byprev index (in the worst case):
      32                 :         567 :     unsigned int sz = GetTransactionWeight(*tx);
      33         [ +  + ]:         567 :     if (sz > MAX_STANDARD_TX_WEIGHT)
      34                 :             :     {
      35   [ +  -  +  -  :          22 :         LogDebug(BCLog::TXPACKAGES, "ignoring large orphan tx (size: %u, txid: %s, wtxid: %s)\n", sz, hash.ToString(), wtxid.ToString());
                   +  - ]
      36                 :          11 :         return false;
      37                 :             :     }
      38                 :             : 
      39   [ +  -  +  -  :        1668 :     auto ret = m_orphans.emplace(wtxid, OrphanTx{{tx, {peer}, Now<NodeSeconds>() + ORPHAN_TX_EXPIRE_TIME}, m_orphan_list.size()});
          +  -  -  +  -  
                      - ]
      40         [ -  + ]:         556 :     assert(ret.second);
      41                 :         556 :     m_orphan_list.push_back(ret.first);
      42         [ +  + ]:        1188 :     for (const CTxIn& txin : tx->vin) {
      43                 :         632 :         m_outpoint_to_orphan_it[txin.prevout].insert(ret.first);
      44                 :             :     }
      45                 :         556 :     m_total_orphan_usage += sz;
      46                 :         556 :     m_total_announcements += 1;
      47                 :         556 :     auto& peer_info = m_peer_orphanage_info.try_emplace(peer).first->second;
      48                 :         556 :     peer_info.m_total_usage += sz;
      49                 :             : 
      50   [ +  -  +  -  :        1112 :     LogDebug(BCLog::TXPACKAGES, "stored orphan tx %s (wtxid=%s), weight: %u (mapsz %u outsz %u)\n", hash.ToString(), wtxid.ToString(), sz,
                   +  - ]
      51                 :             :              m_orphans.size(), m_outpoint_to_orphan_it.size());
      52                 :             :     return true;
      53                 :             : }
      54                 :             : 
      55                 :          14 : bool TxOrphanage::AddAnnouncer(const Wtxid& wtxid, NodeId peer)
      56                 :             : {
      57                 :          14 :     const auto it = m_orphans.find(wtxid);
      58         [ +  - ]:          14 :     if (it != m_orphans.end()) {
      59                 :          14 :         Assume(!it->second.announcers.empty());
      60                 :          14 :         const auto ret = it->second.announcers.insert(peer);
      61         [ +  + ]:          14 :         if (ret.second) {
      62                 :          11 :             auto& peer_info = m_peer_orphanage_info.try_emplace(peer).first->second;
      63                 :          11 :             peer_info.m_total_usage += it->second.GetUsage();
      64                 :          11 :             m_total_announcements += 1;
      65   [ +  -  +  - ]:          22 :             LogDebug(BCLog::TXPACKAGES, "added peer=%d as announcer of orphan tx %s\n", peer, wtxid.ToString());
      66                 :          11 :             return true;
      67                 :             :         }
      68                 :             :     }
      69                 :             :     return false;
      70                 :             : }
      71                 :             : 
      72                 :       11716 : int TxOrphanage::EraseTx(const Wtxid& wtxid)
      73                 :             : {
      74                 :       11716 :     std::map<Wtxid, OrphanTx>::iterator it = m_orphans.find(wtxid);
      75         [ +  + ]:       11716 :     if (it == m_orphans.end())
      76                 :             :         return 0;
      77         [ +  + ]:        1031 :     for (const CTxIn& txin : it->second.tx->vin)
      78                 :             :     {
      79                 :         528 :         auto itPrev = m_outpoint_to_orphan_it.find(txin.prevout);
      80         [ -  + ]:         528 :         if (itPrev == m_outpoint_to_orphan_it.end())
      81                 :           0 :             continue;
      82                 :         528 :         itPrev->second.erase(it);
      83         [ +  + ]:         528 :         if (itPrev->second.empty())
      84                 :         507 :             m_outpoint_to_orphan_it.erase(itPrev);
      85                 :             :     }
      86                 :             : 
      87                 :         503 :     const auto tx_size{it->second.GetUsage()};
      88                 :         503 :     m_total_orphan_usage -= tx_size;
      89                 :         503 :     m_total_announcements -= it->second.announcers.size();
      90                 :             :     // Decrement each announcer's m_total_usage
      91         [ +  + ]:         781 :     for (const auto& peer : it->second.announcers) {
      92                 :         278 :         auto peer_it = m_peer_orphanage_info.find(peer);
      93         [ +  - ]:         278 :         if (Assume(peer_it != m_peer_orphanage_info.end())) {
      94                 :         278 :             peer_it->second.m_total_usage -= tx_size;
      95                 :             :         }
      96                 :             :     }
      97                 :             : 
      98         [ -  + ]:         503 :     size_t old_pos = it->second.list_pos;
      99         [ -  + ]:         503 :     assert(m_orphan_list[old_pos] == it);
     100         [ +  + ]:         503 :     if (old_pos + 1 != m_orphan_list.size()) {
     101                 :             :         // Unless we're deleting the last entry in m_orphan_list, move the last
     102                 :             :         // entry to the position we're deleting.
     103                 :         389 :         auto it_last = m_orphan_list.back();
     104                 :         389 :         m_orphan_list[old_pos] = it_last;
     105                 :         389 :         it_last->second.list_pos = old_pos;
     106                 :             :     }
     107                 :         503 :     const auto& txid = it->second.tx->GetHash();
     108                 :             :     // Time spent in orphanage = difference between current and entry time.
     109                 :             :     // Entry time is equal to ORPHAN_TX_EXPIRE_TIME earlier than entry's expiry.
     110   [ +  -  +  -  :        1006 :     LogDebug(BCLog::TXPACKAGES, "   removed orphan tx %s (wtxid=%s) after %ds\n", txid.ToString(), wtxid.ToString(),
                   +  - ]
     111                 :             :              Ticks<std::chrono::seconds>(NodeClock::now() + ORPHAN_TX_EXPIRE_TIME - it->second.nTimeExpire));
     112                 :         503 :     m_orphan_list.pop_back();
     113                 :             : 
     114                 :         503 :     m_orphans.erase(it);
     115                 :         503 :     return 1;
     116                 :             : }
     117                 :             : 
     118                 :        1587 : void TxOrphanage::EraseForPeer(NodeId peer)
     119                 :             : {
     120                 :             :     // Zeroes out this peer's m_total_usage.
     121                 :        1587 :     m_peer_orphanage_info.erase(peer);
     122                 :             : 
     123                 :        1587 :     int nErased = 0;
     124                 :        1587 :     std::map<Wtxid, OrphanTx>::iterator iter = m_orphans.begin();
     125         [ +  + ]:        3803 :     while (iter != m_orphans.end())
     126                 :             :     {
     127                 :             :         // increment to avoid iterator becoming invalid after erasure
     128                 :         629 :         auto& [wtxid, orphan] = *iter++;
     129                 :         629 :         auto orphan_it = orphan.announcers.find(peer);
     130         [ +  + ]:         629 :         if (orphan_it != orphan.announcers.end()) {
     131                 :         236 :             orphan.announcers.erase(peer);
     132                 :         236 :             m_total_announcements -= 1;
     133                 :             : 
     134                 :             :             // No remaining announcers: clean up entry
     135         [ +  + ]:         236 :             if (orphan.announcers.empty()) {
     136                 :         227 :                 nErased += EraseTx(orphan.tx->GetWitnessHash());
     137                 :             :             }
     138                 :             :         }
     139                 :             :     }
     140   [ +  +  +  - ]:        1587 :     if (nErased > 0) LogDebug(BCLog::TXPACKAGES, "Erased %d orphan transaction(s) from peer=%d\n", nErased, peer);
     141                 :        1587 : }
     142                 :             : 
     143                 :         443 : void TxOrphanage::LimitOrphans(unsigned int max_orphans, FastRandomContext& rng)
     144                 :             : {
     145                 :         443 :     unsigned int nEvicted = 0;
     146                 :         443 :     auto nNow{Now<NodeSeconds>()};
     147         [ +  + ]:         443 :     if (m_next_sweep <= nNow) {
     148                 :             :         // Sweep out expired orphan pool entries:
     149                 :          66 :         int nErased = 0;
     150                 :          66 :         auto nMinExpTime{nNow + ORPHAN_TX_EXPIRE_TIME - ORPHAN_TX_EXPIRE_INTERVAL};
     151                 :          66 :         std::map<Wtxid, OrphanTx>::iterator iter = m_orphans.begin();
     152         [ +  + ]:         225 :         while (iter != m_orphans.end())
     153                 :             :         {
     154                 :         159 :             std::map<Wtxid, OrphanTx>::iterator maybeErase = iter++;
     155         [ +  + ]:         159 :             if (maybeErase->second.nTimeExpire <= nNow) {
     156                 :           1 :                 nErased += EraseTx(maybeErase->first);
     157                 :             :             } else {
     158                 :         158 :                 nMinExpTime = std::min(maybeErase->second.nTimeExpire, nMinExpTime);
     159                 :             :             }
     160                 :             :         }
     161                 :             :         // Sweep again 5 minutes after the next entry that expires in order to batch the linear scan.
     162                 :          66 :         m_next_sweep = nMinExpTime + ORPHAN_TX_EXPIRE_INTERVAL;
     163   [ +  +  +  - ]:          66 :         if (nErased > 0) LogDebug(BCLog::TXPACKAGES, "Erased %d orphan tx due to expiration\n", nErased);
     164                 :             :     }
     165         [ +  + ]:         540 :     while (m_orphans.size() > max_orphans)
     166                 :             :     {
     167                 :             :         // Evict a random orphan:
     168                 :          97 :         size_t randompos = rng.randrange(m_orphan_list.size());
     169                 :          97 :         EraseTx(m_orphan_list[randompos]->first);
     170                 :          97 :         ++nEvicted;
     171                 :             :     }
     172   [ +  +  +  - ]:         443 :     if (nEvicted > 0) LogDebug(BCLog::TXPACKAGES, "orphanage overflow, removed %u tx\n", nEvicted);
     173                 :         443 : }
     174                 :             : 
     175                 :       11136 : void TxOrphanage::AddChildrenToWorkSet(const CTransaction& tx, FastRandomContext& rng)
     176                 :             : {
     177         [ +  + ]:       31556 :     for (unsigned int i = 0; i < tx.vout.size(); i++) {
     178                 :       20420 :         const auto it_by_prev = m_outpoint_to_orphan_it.find(COutPoint(tx.GetHash(), i));
     179         [ +  + ]:       20420 :         if (it_by_prev != m_outpoint_to_orphan_it.end()) {
     180         [ +  + ]:         245 :             for (const auto& elem : it_by_prev->second) {
     181                 :             :                 // Belt and suspenders, each orphan should always have at least 1 announcer.
     182         [ -  + ]:         124 :                 if (!Assume(!elem->second.announcers.empty())) continue;
     183                 :             : 
     184                 :             :                 // Select a random peer to assign orphan processing, reducing wasted work if the orphan is still missing
     185                 :             :                 // inputs. However, we don't want to create an issue in which the assigned peer can purposefully stop us
     186                 :             :                 // from processing the orphan by disconnecting.
     187                 :         124 :                 auto announcer_iter = std::begin(elem->second.announcers);
     188                 :         124 :                 std::advance(announcer_iter, rng.randrange(elem->second.announcers.size()));
     189                 :         124 :                 auto announcer = *(announcer_iter);
     190                 :             : 
     191                 :             :                 // Get this source peer's work set, emplacing an empty set if it didn't exist
     192                 :             :                 // (note: if this peer wasn't still connected, we would have removed the orphan tx already)
     193                 :         124 :                 std::set<Wtxid>& orphan_work_set = m_peer_orphanage_info.try_emplace(announcer).first->second.m_work_set;
     194                 :             :                 // Add this tx to the work set
     195                 :         124 :                 orphan_work_set.insert(elem->first);
     196   [ +  -  +  -  :         248 :                 LogDebug(BCLog::TXPACKAGES, "added %s (wtxid=%s) to peer %d workset\n",
                   +  - ]
     197                 :             :                          tx.GetHash().ToString(), tx.GetWitnessHash().ToString(), announcer);
     198                 :             :             }
     199                 :             :         }
     200                 :             :     }
     201                 :       11136 : }
     202                 :             : 
     203                 :       59875 : bool TxOrphanage::HaveTx(const Wtxid& wtxid) const
     204                 :             : {
     205                 :       59875 :     return m_orphans.count(wtxid);
     206                 :             : }
     207                 :             : 
     208                 :       25875 : CTransactionRef TxOrphanage::GetTx(const Wtxid& wtxid) const
     209                 :             : {
     210                 :       25875 :     auto it = m_orphans.find(wtxid);
     211   [ +  +  +  - ]:       25875 :     return it != m_orphans.end() ? it->second.tx : nullptr;
     212                 :             : }
     213                 :             : 
     214                 :         453 : bool TxOrphanage::HaveTxFromPeer(const Wtxid& wtxid, NodeId peer) const
     215                 :             : {
     216                 :         453 :     auto it = m_orphans.find(wtxid);
     217   [ +  +  +  + ]:         453 :     return (it != m_orphans.end() && it->second.announcers.contains(peer));
     218                 :             : }
     219                 :             : 
     220                 :      404356 : CTransactionRef TxOrphanage::GetTxToReconsider(NodeId peer)
     221                 :             : {
     222                 :      404356 :     auto peer_it = m_peer_orphanage_info.find(peer);
     223         [ +  + ]:      404356 :     if (peer_it == m_peer_orphanage_info.end()) return nullptr;
     224                 :             : 
     225                 :        2423 :     auto& work_set = peer_it->second.m_work_set;
     226         [ +  + ]:        2423 :     while (!work_set.empty()) {
     227                 :         123 :         Wtxid wtxid = *work_set.begin();
     228                 :         123 :         work_set.erase(work_set.begin());
     229                 :             : 
     230                 :         123 :         const auto orphan_it = m_orphans.find(wtxid);
     231         [ +  - ]:         123 :         if (orphan_it != m_orphans.end()) {
     232         [ +  - ]:         123 :             return orphan_it->second.tx;
     233                 :             :         }
     234                 :             :     }
     235                 :        2300 :     return nullptr;
     236                 :             : }
     237                 :             : 
     238                 :      157547 : bool TxOrphanage::HaveTxToReconsider(NodeId peer)
     239                 :             : {
     240                 :      157547 :     auto peer_it = m_peer_orphanage_info.find(peer);
     241         [ +  + ]:      157547 :     if (peer_it == m_peer_orphanage_info.end()) return false;
     242                 :             : 
     243                 :        1270 :     auto& work_set = peer_it->second.m_work_set;
     244                 :        1270 :     return !work_set.empty();
     245                 :             : }
     246                 :             : 
     247                 :      119877 : void TxOrphanage::EraseForBlock(const CBlock& block)
     248                 :             : {
     249                 :      119877 :     std::vector<Wtxid> vOrphanErase;
     250                 :             : 
     251         [ +  + ]:      281406 :     for (const CTransactionRef& ptx : block.vtx) {
     252                 :      161529 :         const CTransaction& tx = *ptx;
     253                 :             : 
     254                 :             :         // Which orphan pool entries must we evict?
     255         [ +  + ]:      346083 :         for (const auto& txin : tx.vin) {
     256                 :      184554 :             auto itByPrev = m_outpoint_to_orphan_it.find(txin.prevout);
     257         [ +  + ]:      184554 :             if (itByPrev == m_outpoint_to_orphan_it.end()) continue;
     258         [ +  + ]:          58 :             for (auto mi = itByPrev->second.begin(); mi != itByPrev->second.end(); ++mi) {
     259         [ +  - ]:          29 :                 const CTransaction& orphanTx = *(*mi)->second.tx;
     260         [ +  - ]:          29 :                 vOrphanErase.push_back(orphanTx.GetWitnessHash());
     261                 :             :             }
     262                 :             :         }
     263                 :             :     }
     264                 :             : 
     265                 :             :     // Erase orphan transactions included or precluded by this block
     266         [ +  + ]:      119877 :     if (vOrphanErase.size()) {
     267                 :           8 :         int nErased = 0;
     268         [ +  + ]:          37 :         for (const auto& orphanHash : vOrphanErase) {
     269         [ +  - ]:          29 :             nErased += EraseTx(orphanHash);
     270                 :             :         }
     271   [ +  -  +  -  :           8 :         LogDebug(BCLog::TXPACKAGES, "Erased %d orphan transaction(s) included or conflicted by block\n", nErased);
                   +  - ]
     272                 :             :     }
     273                 :      119877 : }
     274                 :             : 
     275                 :          61 : std::vector<CTransactionRef> TxOrphanage::GetChildrenFromSamePeer(const CTransactionRef& parent, NodeId nodeid) const
     276                 :             : {
     277                 :             :     // First construct a vector of iterators to ensure we do not return duplicates of the same tx
     278                 :             :     // and so we can sort by nTimeExpire.
     279                 :          61 :     std::vector<OrphanMap::iterator> iters;
     280                 :             : 
     281                 :             :     // For each output, get all entries spending this prevout, filtering for ones from the specified peer.
     282         [ +  + ]:         144 :     for (unsigned int i = 0; i < parent->vout.size(); i++) {
     283                 :          83 :         const auto it_by_prev = m_outpoint_to_orphan_it.find(COutPoint(parent->GetHash(), i));
     284         [ +  + ]:          83 :         if (it_by_prev != m_outpoint_to_orphan_it.end()) {
     285         [ +  + ]:         111 :             for (const auto& elem : it_by_prev->second) {
     286         [ +  + ]:          60 :                 if (elem->second.announcers.contains(nodeid)) {
     287         [ +  - ]:          46 :                     iters.emplace_back(elem);
     288                 :             :                 }
     289                 :             :             }
     290                 :             :         }
     291                 :             :     }
     292                 :             : 
     293                 :             :     // Sort by address so that duplicates can be deleted. At the same time, sort so that more recent
     294                 :             :     // orphans (which expire later) come first.  Break ties based on address, as nTimeExpire is
     295                 :             :     // quantified in seconds and it is possible for orphans to have the same expiry.
     296         [ +  - ]:          89 :     std::sort(iters.begin(), iters.end(), [](const auto& lhs, const auto& rhs) {
     297         [ +  - ]:          28 :         if (lhs->second.nTimeExpire == rhs->second.nTimeExpire) {
     298                 :          28 :             return &(*lhs) < &(*rhs);
     299                 :             :         } else {
     300                 :           0 :             return lhs->second.nTimeExpire > rhs->second.nTimeExpire;
     301                 :             :         }
     302                 :             :     });
     303                 :             :     // Erase duplicates
     304                 :          61 :     iters.erase(std::unique(iters.begin(), iters.end()), iters.end());
     305                 :             : 
     306                 :             :     // Convert to a vector of CTransactionRef
     307                 :          61 :     std::vector<CTransactionRef> children_found;
     308         [ +  - ]:          61 :     children_found.reserve(iters.size());
     309         [ +  + ]:          98 :     for (const auto& child_iter : iters) {
     310         [ +  - ]:          37 :         children_found.emplace_back(child_iter->second.tx);
     311                 :             :     }
     312                 :          61 :     return children_found;
     313                 :          61 : }
     314                 :             : 
     315                 :         162 : std::vector<TxOrphanage::OrphanTxBase> TxOrphanage::GetOrphanTransactions() const
     316                 :             : {
     317                 :         162 :     std::vector<OrphanTxBase> ret;
     318         [ +  - ]:         162 :     ret.reserve(m_orphans.size());
     319         [ +  + ]:       10516 :     for (auto const& o : m_orphans) {
     320         [ +  - ]:       20708 :         ret.push_back({o.second.tx, o.second.announcers, o.second.nTimeExpire});
     321                 :             :     }
     322                 :         162 :     return ret;
     323   [ +  -  +  - ]:       10354 : }
     324                 :             : 
     325                 :           0 : void TxOrphanage::SanityCheck() const
     326                 :             : {
     327                 :             :     // Check that cached m_total_announcements is correct
     328                 :           0 :     unsigned int counted_total_announcements{0};
     329                 :             :     // Check that m_total_orphan_usage is correct
     330                 :           0 :     unsigned int counted_total_usage{0};
     331                 :             : 
     332                 :             :     // Check that cached PeerOrphanInfo::m_total_size is correct
     333                 :           0 :     std::map<NodeId, unsigned int> counted_size_per_peer;
     334                 :             : 
     335         [ #  # ]:           0 :     for (const auto& [wtxid, orphan] : m_orphans) {
     336                 :           0 :         counted_total_announcements += orphan.announcers.size();
     337                 :           0 :         counted_total_usage += orphan.GetUsage();
     338                 :             : 
     339                 :           0 :         Assume(!orphan.announcers.empty());
     340         [ #  # ]:           0 :         for (const auto& peer : orphan.announcers) {
     341         [ #  # ]:           0 :             auto& count_peer_entry = counted_size_per_peer.try_emplace(peer).first->second;
     342                 :           0 :             count_peer_entry += orphan.GetUsage();
     343                 :             :         }
     344                 :             :     }
     345                 :             : 
     346                 :           0 :     Assume(m_total_announcements >= m_orphans.size());
     347                 :           0 :     Assume(counted_total_announcements == m_total_announcements);
     348                 :           0 :     Assume(counted_total_usage == m_total_orphan_usage);
     349                 :             : 
     350                 :             :     // There must be an entry in m_peer_orphanage_info for each peer
     351                 :             :     // However, there may be m_peer_orphanage_info entries corresponding to peers for whom we
     352                 :             :     // previously had orphans but no longer do.
     353                 :           0 :     Assume(counted_size_per_peer.size() <= m_peer_orphanage_info.size());
     354                 :             : 
     355         [ #  # ]:           0 :     for (const auto& [peerid, info] : m_peer_orphanage_info) {
     356                 :           0 :         auto it_counted = counted_size_per_peer.find(peerid);
     357                 :           0 :         if (it_counted == counted_size_per_peer.end()) {
     358                 :             :             Assume(info.m_total_usage == 0);
     359                 :             :         } else {
     360                 :           0 :             Assume(it_counted->second == info.m_total_usage);
     361                 :             :         }
     362                 :             :     }
     363                 :           0 : }
        

Generated by: LCOV version 2.0-1