LCOV - code coverage report
Current view: top level - src - txorphanage.cpp (source / functions) Coverage Total Hit
Test: fuzz_coverage.info Lines: 99.0 % 193 191
Test Date: 2025-02-22 04:11:21 Functions: 100.0 % 16 16
Branches: 67.2 % 198 133

             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                 :      249423 : bool TxOrphanage::AddTx(const CTransactionRef& tx, NodeId peer)
      16                 :             : {
      17                 :      249423 :     const Txid& hash = tx->GetHash();
      18                 :      249423 :     const Wtxid& wtxid = tx->GetWitnessHash();
      19         [ +  + ]:      249423 :     if (auto it{m_orphans.find(wtxid)}; it != m_orphans.end()) {
      20                 :      214798 :         AddAnnouncer(wtxid, peer);
      21                 :             :         // No new orphan entry was created. An announcer may have been added.
      22                 :      214798 :         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                 :       34625 :     unsigned int sz = GetTransactionWeight(*tx);
      33         [ +  + ]:       34625 :     if (sz > MAX_STANDARD_TX_WEIGHT)
      34                 :             :     {
      35   [ -  +  -  -  :        2550 :         LogDebug(BCLog::TXPACKAGES, "ignoring large orphan tx (size: %u, txid: %s, wtxid: %s)\n", sz, hash.ToString(), wtxid.ToString());
                   -  - ]
      36                 :        2550 :         return false;
      37                 :             :     }
      38                 :             : 
      39   [ +  -  +  -  :       96225 :     auto ret = m_orphans.emplace(wtxid, OrphanTx{{tx, {peer}, Now<NodeSeconds>() + ORPHAN_TX_EXPIRE_TIME}, m_orphan_list.size()});
          +  -  -  +  -  
                      - ]
      40         [ -  + ]:       32075 :     assert(ret.second);
      41                 :       32075 :     m_orphan_list.push_back(ret.first);
      42         [ +  + ]:    14856579 :     for (const CTxIn& txin : tx->vin) {
      43                 :    14824504 :         m_outpoint_to_orphan_it[txin.prevout].insert(ret.first);
      44                 :             :     }
      45                 :       32075 :     m_total_orphan_usage += sz;
      46                 :       32075 :     m_total_announcements += 1;
      47                 :       32075 :     auto& peer_info = m_peer_orphanage_info.try_emplace(peer).first->second;
      48                 :       32075 :     peer_info.m_total_usage += sz;
      49                 :             : 
      50   [ -  +  -  -  :       32075 :     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                 :      219361 : bool TxOrphanage::AddAnnouncer(const Wtxid& wtxid, NodeId peer)
      56                 :             : {
      57                 :      219361 :     const auto it = m_orphans.find(wtxid);
      58         [ +  + ]:      219361 :     if (it != m_orphans.end()) {
      59                 :      217057 :         Assume(!it->second.announcers.empty());
      60                 :      217057 :         const auto ret = it->second.announcers.insert(peer);
      61         [ +  + ]:      217057 :         if (ret.second) {
      62                 :        3305 :             auto& peer_info = m_peer_orphanage_info.try_emplace(peer).first->second;
      63                 :        3305 :             peer_info.m_total_usage += it->second.GetUsage();
      64                 :        3305 :             m_total_announcements += 1;
      65   [ -  +  -  - ]:        3305 :             LogDebug(BCLog::TXPACKAGES, "added peer=%d as announcer of orphan tx %s\n", peer, wtxid.ToString());
      66                 :        3305 :             return true;
      67                 :             :         }
      68                 :             :     }
      69                 :             :     return false;
      70                 :             : }
      71                 :             : 
      72                 :     5929506 : int TxOrphanage::EraseTx(const Wtxid& wtxid)
      73                 :             : {
      74                 :     5929506 :     std::map<Wtxid, OrphanTx>::iterator it = m_orphans.find(wtxid);
      75         [ +  + ]:     5929506 :     if (it == m_orphans.end())
      76                 :             :         return 0;
      77         [ +  + ]:    14800740 :     for (const CTxIn& txin : it->second.tx->vin)
      78                 :             :     {
      79                 :    14769725 :         auto itPrev = m_outpoint_to_orphan_it.find(txin.prevout);
      80         [ +  + ]:    14769725 :         if (itPrev == m_outpoint_to_orphan_it.end())
      81                 :    13803612 :             continue;
      82                 :      966113 :         itPrev->second.erase(it);
      83         [ +  + ]:      966113 :         if (itPrev->second.empty())
      84                 :      621912 :             m_outpoint_to_orphan_it.erase(itPrev);
      85                 :             :     }
      86                 :             : 
      87                 :       31015 :     const auto tx_size{it->second.GetUsage()};
      88                 :       31015 :     m_total_orphan_usage -= tx_size;
      89                 :       31015 :     m_total_announcements -= it->second.announcers.size();
      90                 :             :     // Decrement each announcer's m_total_usage
      91         [ +  + ]:       59061 :     for (const auto& peer : it->second.announcers) {
      92                 :       28046 :         auto peer_it = m_peer_orphanage_info.find(peer);
      93         [ +  - ]:       28046 :         if (Assume(peer_it != m_peer_orphanage_info.end())) {
      94                 :       28046 :             peer_it->second.m_total_usage -= tx_size;
      95                 :             :         }
      96                 :             :     }
      97                 :             : 
      98         [ -  + ]:       31015 :     size_t old_pos = it->second.list_pos;
      99         [ -  + ]:       31015 :     assert(m_orphan_list[old_pos] == it);
     100         [ +  + ]:       31015 :     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                 :        3632 :         auto it_last = m_orphan_list.back();
     104                 :        3632 :         m_orphan_list[old_pos] = it_last;
     105                 :        3632 :         it_last->second.list_pos = old_pos;
     106                 :             :     }
     107                 :       31015 :     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   [ -  +  -  -  :       31015 :     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                 :       31015 :     m_orphan_list.pop_back();
     113                 :             : 
     114                 :       31015 :     m_orphans.erase(it);
     115                 :       31015 :     return 1;
     116                 :             : }
     117                 :             : 
     118                 :       87446 : void TxOrphanage::EraseForPeer(NodeId peer)
     119                 :             : {
     120                 :             :     // Zeroes out this peer's m_total_usage.
     121                 :       87446 :     m_peer_orphanage_info.erase(peer);
     122                 :             : 
     123                 :       87446 :     int nErased = 0;
     124                 :       87446 :     std::map<Wtxid, OrphanTx>::iterator iter = m_orphans.begin();
     125         [ +  + ]:      186683 :     while (iter != m_orphans.end())
     126                 :             :     {
     127                 :             :         // increment to avoid iterator becoming invalid after erasure
     128                 :       11791 :         auto& [wtxid, orphan] = *iter++;
     129                 :       11791 :         auto orphan_it = orphan.announcers.find(peer);
     130         [ +  + ]:       11791 :         if (orphan_it != orphan.announcers.end()) {
     131                 :        5254 :             orphan.announcers.erase(peer);
     132                 :        5254 :             m_total_announcements -= 1;
     133                 :             : 
     134                 :             :             // No remaining announcers: clean up entry
     135         [ +  + ]:        5254 :             if (orphan.announcers.empty()) {
     136                 :        5202 :                 nErased += EraseTx(orphan.tx->GetWitnessHash());
     137                 :             :             }
     138                 :             :         }
     139                 :             :     }
     140   [ +  +  -  + ]:       87446 :     if (nErased > 0) LogDebug(BCLog::TXPACKAGES, "Erased %d orphan transaction(s) from peer=%d\n", nErased, peer);
     141                 :       87446 : }
     142                 :             : 
     143                 :        8000 : void TxOrphanage::LimitOrphans(unsigned int max_orphans, FastRandomContext& rng)
     144                 :             : {
     145                 :        8000 :     unsigned int nEvicted = 0;
     146                 :        8000 :     auto nNow{Now<NodeSeconds>()};
     147         [ +  + ]:        8000 :     if (m_next_sweep <= nNow) {
     148                 :             :         // Sweep out expired orphan pool entries:
     149                 :         622 :         int nErased = 0;
     150                 :         622 :         auto nMinExpTime{nNow + ORPHAN_TX_EXPIRE_TIME - ORPHAN_TX_EXPIRE_INTERVAL};
     151                 :         622 :         std::map<Wtxid, OrphanTx>::iterator iter = m_orphans.begin();
     152         [ +  + ]:        1244 :         while (iter != m_orphans.end())
     153                 :             :         {
     154                 :         622 :             std::map<Wtxid, OrphanTx>::iterator maybeErase = iter++;
     155         [ +  + ]:         622 :             if (maybeErase->second.nTimeExpire <= nNow) {
     156                 :         350 :                 nErased += EraseTx(maybeErase->first);
     157                 :             :             } else {
     158                 :         272 :                 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                 :         622 :         m_next_sweep = nMinExpTime + ORPHAN_TX_EXPIRE_INTERVAL;
     163   [ +  +  -  + ]:         622 :         if (nErased > 0) LogDebug(BCLog::TXPACKAGES, "Erased %d orphan tx due to expiration\n", nErased);
     164                 :             :     }
     165         [ +  + ]:        8022 :     while (m_orphans.size() > max_orphans)
     166                 :             :     {
     167                 :             :         // Evict a random orphan:
     168                 :          22 :         size_t randompos = rng.randrange(m_orphan_list.size());
     169                 :          22 :         EraseTx(m_orphan_list[randompos]->first);
     170                 :          22 :         ++nEvicted;
     171                 :             :     }
     172   [ +  +  -  + ]:        8000 :     if (nEvicted > 0) LogDebug(BCLog::TXPACKAGES, "orphanage overflow, removed %u tx\n", nEvicted);
     173                 :        8000 : }
     174                 :             : 
     175                 :        5366 : void TxOrphanage::AddChildrenToWorkSet(const CTransaction& tx, FastRandomContext& rng)
     176                 :             : {
     177         [ +  + ]:      520177 :     for (unsigned int i = 0; i < tx.vout.size(); i++) {
     178                 :      514811 :         const auto it_by_prev = m_outpoint_to_orphan_it.find(COutPoint(tx.GetHash(), i));
     179         [ +  + ]:      514811 :         if (it_by_prev != m_outpoint_to_orphan_it.end()) {
     180         [ +  + ]:       33697 :             for (const auto& elem : it_by_prev->second) {
     181                 :             :                 // Belt and suspenders, each orphan should always have at least 1 announcer.
     182         [ -  + ]:       18661 :                 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                 :       18661 :                 auto announcer_iter = std::begin(elem->second.announcers);
     188                 :       18661 :                 std::advance(announcer_iter, rng.randrange(elem->second.announcers.size()));
     189                 :       18661 :                 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                 :       18661 :                 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                 :       18661 :                 orphan_work_set.insert(elem->first);
     196   [ -  +  -  -  :       18661 :                 LogDebug(BCLog::TXPACKAGES, "added %s (wtxid=%s) to peer %d workset\n",
                   -  - ]
     197                 :             :                          tx.GetHash().ToString(), tx.GetWitnessHash().ToString(), announcer);
     198                 :             :             }
     199                 :             :         }
     200                 :             :     }
     201                 :        5366 : }
     202                 :             : 
     203                 :     7777665 : bool TxOrphanage::HaveTx(const Wtxid& wtxid) const
     204                 :             : {
     205                 :     7777665 :     return m_orphans.count(wtxid);
     206                 :             : }
     207                 :             : 
     208                 :        7230 : CTransactionRef TxOrphanage::GetTx(const Wtxid& wtxid) const
     209                 :             : {
     210                 :        7230 :     auto it = m_orphans.find(wtxid);
     211   [ +  +  +  - ]:        7230 :     return it != m_orphans.end() ? it->second.tx : nullptr;
     212                 :             : }
     213                 :             : 
     214                 :     7131140 : bool TxOrphanage::HaveTxFromPeer(const Wtxid& wtxid, NodeId peer) const
     215                 :             : {
     216                 :     7131140 :     auto it = m_orphans.find(wtxid);
     217   [ +  +  +  + ]:     7131140 :     return (it != m_orphans.end() && it->second.announcers.contains(peer));
     218                 :             : }
     219                 :             : 
     220                 :     1575529 : CTransactionRef TxOrphanage::GetTxToReconsider(NodeId peer)
     221                 :             : {
     222                 :     1575529 :     auto peer_it = m_peer_orphanage_info.find(peer);
     223         [ +  + ]:     1575529 :     if (peer_it == m_peer_orphanage_info.end()) return nullptr;
     224                 :             : 
     225                 :       14901 :     auto& work_set = peer_it->second.m_work_set;
     226         [ +  + ]:       14921 :     while (!work_set.empty()) {
     227                 :         323 :         Wtxid wtxid = *work_set.begin();
     228                 :         323 :         work_set.erase(work_set.begin());
     229                 :             : 
     230                 :         323 :         const auto orphan_it = m_orphans.find(wtxid);
     231         [ +  + ]:         323 :         if (orphan_it != m_orphans.end()) {
     232         [ +  - ]:         303 :             return orphan_it->second.tx;
     233                 :             :         }
     234                 :             :     }
     235                 :       14598 :     return nullptr;
     236                 :             : }
     237                 :             : 
     238                 :      120763 : bool TxOrphanage::HaveTxToReconsider(NodeId peer)
     239                 :             : {
     240                 :      120763 :     auto peer_it = m_peer_orphanage_info.find(peer);
     241         [ +  + ]:      120763 :     if (peer_it == m_peer_orphanage_info.end()) return false;
     242                 :             : 
     243                 :        8385 :     auto& work_set = peer_it->second.m_work_set;
     244                 :        8385 :     return !work_set.empty();
     245                 :             : }
     246                 :             : 
     247                 :       97602 : void TxOrphanage::EraseForBlock(const CBlock& block)
     248                 :             : {
     249                 :       97602 :     std::vector<Wtxid> vOrphanErase;
     250                 :             : 
     251         [ +  + ]:     7158823 :     for (const CTransactionRef& ptx : block.vtx) {
     252                 :     7061221 :         const CTransaction& tx = *ptx;
     253                 :             : 
     254                 :             :         // Which orphan pool entries must we evict?
     255         [ +  + ]:  1701616767 :         for (const auto& txin : tx.vin) {
     256                 :  1694555546 :             auto itByPrev = m_outpoint_to_orphan_it.find(txin.prevout);
     257         [ +  + ]:  1694555546 :             if (itByPrev == m_outpoint_to_orphan_it.end()) continue;
     258         [ +  + ]:     9927599 :             for (auto mi = itByPrev->second.begin(); mi != itByPrev->second.end(); ++mi) {
     259         [ +  - ]:     5857147 :                 const CTransaction& orphanTx = *(*mi)->second.tx;
     260         [ +  - ]:     5857147 :                 vOrphanErase.push_back(orphanTx.GetWitnessHash());
     261                 :             :             }
     262                 :             :         }
     263                 :             :     }
     264                 :             : 
     265                 :             :     // Erase orphan transactions included or precluded by this block
     266         [ +  + ]:       97602 :     if (vOrphanErase.size()) {
     267                 :         585 :         int nErased = 0;
     268         [ +  + ]:     5857732 :         for (const auto& orphanHash : vOrphanErase) {
     269         [ +  - ]:     5857147 :             nErased += EraseTx(orphanHash);
     270                 :             :         }
     271   [ +  -  -  +  :         585 :         LogDebug(BCLog::TXPACKAGES, "Erased %d orphan transaction(s) included or conflicted by block\n", nErased);
                   -  - ]
     272                 :             :     }
     273                 :       97602 : }
     274                 :             : 
     275                 :        4849 : 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                 :        4849 :     std::vector<OrphanMap::iterator> iters;
     280                 :             : 
     281                 :             :     // For each output, get all entries spending this prevout, filtering for ones from the specified peer.
     282         [ +  + ]:      368849 :     for (unsigned int i = 0; i < parent->vout.size(); i++) {
     283                 :      364000 :         const auto it_by_prev = m_outpoint_to_orphan_it.find(COutPoint(parent->GetHash(), i));
     284         [ +  + ]:      364000 :         if (it_by_prev != m_outpoint_to_orphan_it.end()) {
     285         [ +  + ]:       33695 :             for (const auto& elem : it_by_prev->second) {
     286         [ +  + ]:       18660 :                 if (elem->second.announcers.contains(nodeid)) {
     287         [ +  - ]:        3900 :                     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         [ +  + ]:       19849 :     std::sort(iters.begin(), iters.end(), [](const auto& lhs, const auto& rhs) {
     297         [ +  + ]:       15000 :         if (lhs->second.nTimeExpire == rhs->second.nTimeExpire) {
     298                 :       11716 :             return &(*lhs) < &(*rhs);
     299                 :             :         } else {
     300                 :        3284 :             return lhs->second.nTimeExpire > rhs->second.nTimeExpire;
     301                 :             :         }
     302                 :             :     });
     303                 :             :     // Erase duplicates
     304                 :        4849 :     iters.erase(std::unique(iters.begin(), iters.end()), iters.end());
     305                 :             : 
     306                 :             :     // Convert to a vector of CTransactionRef
     307                 :        4849 :     std::vector<CTransactionRef> children_found;
     308         [ +  - ]:        4849 :     children_found.reserve(iters.size());
     309         [ +  + ]:        6250 :     for (const auto& child_iter : iters) {
     310         [ +  - ]:        1401 :         children_found.emplace_back(child_iter->second.tx);
     311                 :             :     }
     312                 :        4849 :     return children_found;
     313                 :        4849 : }
     314                 :             : 
     315                 :           7 : std::vector<TxOrphanage::OrphanTxBase> TxOrphanage::GetOrphanTransactions() const
     316                 :             : {
     317                 :           7 :     std::vector<OrphanTxBase> ret;
     318         [ +  - ]:           7 :     ret.reserve(m_orphans.size());
     319         [ -  + ]:           7 :     for (auto const& o : m_orphans) {
     320         [ #  # ]:           0 :         ret.push_back({o.second.tx, o.second.announcers, o.second.nTimeExpire});
     321                 :             :     }
     322                 :           7 :     return ret;
     323   [ #  #  #  # ]:           0 : }
     324                 :             : 
     325                 :        2408 : void TxOrphanage::SanityCheck() const
     326                 :             : {
     327                 :             :     // Check that cached m_total_announcements is correct
     328                 :        2408 :     unsigned int counted_total_announcements{0};
     329                 :             :     // Check that m_total_orphan_usage is correct
     330                 :        2408 :     unsigned int counted_total_usage{0};
     331                 :             : 
     332                 :             :     // Check that cached PeerOrphanInfo::m_total_size is correct
     333                 :        2408 :     std::map<NodeId, unsigned int> counted_size_per_peer;
     334                 :             : 
     335         [ +  + ]:        3469 :     for (const auto& [wtxid, orphan] : m_orphans) {
     336                 :        1061 :         counted_total_announcements += orphan.announcers.size();
     337                 :        1061 :         counted_total_usage += orphan.GetUsage();
     338                 :             : 
     339         [ +  - ]:        1061 :         Assume(!orphan.announcers.empty());
     340         [ +  + ]:        3142 :         for (const auto& peer : orphan.announcers) {
     341         [ +  - ]:        2081 :             auto& count_peer_entry = counted_size_per_peer.try_emplace(peer).first->second;
     342                 :        2081 :             count_peer_entry += orphan.GetUsage();
     343                 :             :         }
     344                 :             :     }
     345                 :             : 
     346         [ +  - ]:        2408 :     Assume(m_total_announcements >= m_orphans.size());
     347         [ +  - ]:        2408 :     Assume(counted_total_announcements == m_total_announcements);
     348         [ +  - ]:        2408 :     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         [ +  - ]:        2408 :     Assume(counted_size_per_peer.size() <= m_peer_orphanage_info.size());
     354                 :             : 
     355         [ +  + ]:        6241 :     for (const auto& [peerid, info] : m_peer_orphanage_info) {
     356                 :        3833 :         auto it_counted = counted_size_per_peer.find(peerid);
     357         [ +  + ]:        3833 :         if (it_counted == counted_size_per_peer.end()) {
     358         [ +  - ]:        3149 :             Assume(info.m_total_usage == 0);
     359                 :             :         } else {
     360         [ +  - ]:         684 :             Assume(it_counted->second == info.m_total_usage);
     361                 :             :         }
     362                 :             :     }
     363                 :        2408 : }
        

Generated by: LCOV version 2.0-1