LCOV - code coverage report
Current view: top level - src - txorphanage.cpp (source / functions) Hit Total Coverage
Test: coverage.lcov Lines: 137 140 97.9 %
Date: 2021-06-29 14:35:33 Functions: 8 8 100.0 %
Legend: Modified by patch:
Lines: hit not hit | Branches: + taken - not taken # not executed

Not modified by patch:
Lines: hit not hit | Branches: + taken - not taken # not executed
Branches: 52 64 81.2 %

           Branch data     Line data    Source code
#       1                 :            : // Copyright (c) 2021 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                 :            : 
#      11                 :            : #include <cassert>
#      12                 :            : 
#      13                 :            : /** Expiration time for orphan transactions in seconds */
#      14                 :            : static constexpr int64_t ORPHAN_TX_EXPIRE_TIME = 20 * 60;
#      15                 :            : /** Minimum time between orphan transactions expire time checks in seconds */
#      16                 :            : static constexpr int64_t ORPHAN_TX_EXPIRE_INTERVAL = 5 * 60;
#      17                 :            : 
#      18                 :            : RecursiveMutex g_cs_orphans;
#      19                 :            : 
#      20                 :            : bool TxOrphanage::AddTx(const CTransactionRef& tx, NodeId peer)
#      21                 :        350 : {
#      22                 :        350 :     AssertLockHeld(g_cs_orphans);
#      23                 :            : 
#      24                 :        350 :     const uint256& hash = tx->GetHash();
#      25         [ +  + ]:        350 :     if (m_orphans.count(hash))
#      26                 :         42 :         return false;
#      27                 :            : 
#      28                 :            :     // Ignore big transactions, to avoid a
#      29                 :            :     // send-big-orphans memory exhaustion attack. If a peer has a legitimate
#      30                 :            :     // large transaction with a missing parent then we assume
#      31                 :            :     // it will rebroadcast it later, after the parent transaction(s)
#      32                 :            :     // have been mined or received.
#      33                 :            :     // 100 orphans, each of which is at most 100,000 bytes big is
#      34                 :            :     // at most 10 megabytes of orphans and somewhat more byprev index (in the worst case):
#      35                 :        308 :     unsigned int sz = GetTransactionWeight(*tx);
#      36         [ +  + ]:        308 :     if (sz > MAX_STANDARD_TX_WEIGHT)
#      37                 :         20 :     {
#      38         [ +  - ]:         20 :         LogPrint(BCLog::MEMPOOL, "ignoring large orphan tx (size: %u, hash: %s)\n", sz, hash.ToString());
#      39                 :         20 :         return false;
#      40                 :         20 :     }
#      41                 :            : 
#      42                 :        288 :     auto ret = m_orphans.emplace(hash, OrphanTx{tx, peer, GetTime() + ORPHAN_TX_EXPIRE_TIME, m_orphan_list.size()});
#      43                 :        288 :     assert(ret.second);
#      44                 :        288 :     m_orphan_list.push_back(ret.first);
#      45                 :            :     // Allow for lookups in the orphan pool by wtxid, as well as txid
#      46                 :        288 :     m_wtxid_to_orphan_it.emplace(tx->GetWitnessHash(), ret.first);
#      47         [ +  + ]:        289 :     for (const CTxIn& txin : tx->vin) {
#      48                 :        289 :         m_outpoint_to_orphan_it[txin.prevout].insert(ret.first);
#      49                 :        289 :     }
#      50                 :            : 
#      51         [ +  - ]:        288 :     LogPrint(BCLog::MEMPOOL, "stored orphan tx %s (mapsz %u outsz %u)\n", hash.ToString(),
#      52                 :        288 :              m_orphans.size(), m_outpoint_to_orphan_it.size());
#      53                 :        288 :     return true;
#      54                 :        288 : }
#      55                 :            : 
#      56                 :            : int TxOrphanage::EraseTx(const uint256& txid)
#      57                 :        288 : {
#      58                 :        288 :     AssertLockHeld(g_cs_orphans);
#      59                 :        288 :     std::map<uint256, OrphanTx>::iterator it = m_orphans.find(txid);
#      60         [ -  + ]:        288 :     if (it == m_orphans.end())
#      61                 :          0 :         return 0;
#      62         [ +  + ]:        288 :     for (const CTxIn& txin : it->second.tx->vin)
#      63                 :        289 :     {
#      64                 :        289 :         auto itPrev = m_outpoint_to_orphan_it.find(txin.prevout);
#      65         [ -  + ]:        289 :         if (itPrev == m_outpoint_to_orphan_it.end())
#      66                 :          0 :             continue;
#      67                 :        289 :         itPrev->second.erase(it);
#      68         [ +  - ]:        289 :         if (itPrev->second.empty())
#      69                 :        289 :             m_outpoint_to_orphan_it.erase(itPrev);
#      70                 :        289 :     }
#      71                 :            : 
#      72                 :        288 :     size_t old_pos = it->second.list_pos;
#      73                 :        288 :     assert(m_orphan_list[old_pos] == it);
#      74         [ +  + ]:        288 :     if (old_pos + 1 != m_orphan_list.size()) {
#      75                 :            :         // Unless we're deleting the last entry in m_orphan_list, move the last
#      76                 :            :         // entry to the position we're deleting.
#      77                 :        265 :         auto it_last = m_orphan_list.back();
#      78                 :        265 :         m_orphan_list[old_pos] = it_last;
#      79                 :        265 :         it_last->second.list_pos = old_pos;
#      80                 :        265 :     }
#      81                 :        288 :     m_orphan_list.pop_back();
#      82                 :        288 :     m_wtxid_to_orphan_it.erase(it->second.tx->GetWitnessHash());
#      83                 :            : 
#      84                 :        288 :     m_orphans.erase(it);
#      85                 :        288 :     return 1;
#      86                 :        288 : }
#      87                 :            : 
#      88                 :            : void TxOrphanage::EraseForPeer(NodeId peer)
#      89                 :       1010 : {
#      90                 :       1010 :     AssertLockHeld(g_cs_orphans);
#      91                 :            : 
#      92                 :       1010 :     int nErased = 0;
#      93                 :       1010 :     std::map<uint256, OrphanTx>::iterator iter = m_orphans.begin();
#      94         [ +  + ]:       1577 :     while (iter != m_orphans.end())
#      95                 :        567 :     {
#      96                 :        567 :         std::map<uint256, OrphanTx>::iterator maybeErase = iter++; // increment to avoid iterator becoming invalid
#      97         [ +  + ]:        567 :         if (maybeErase->second.fromPeer == peer)
#      98                 :        115 :         {
#      99                 :        115 :             nErased += EraseTx(maybeErase->second.tx->GetHash());
#     100                 :        115 :         }
#     101                 :        567 :     }
#     102 [ +  + ][ +  - ]:       1010 :     if (nErased > 0) LogPrint(BCLog::MEMPOOL, "Erased %d orphan tx from peer=%d\n", nErased, peer);
#     103                 :       1010 : }
#     104                 :            : 
#     105                 :            : unsigned int TxOrphanage::LimitOrphans(unsigned int max_orphans)
#     106                 :        136 : {
#     107                 :        136 :     AssertLockHeld(g_cs_orphans);
#     108                 :            : 
#     109                 :        136 :     unsigned int nEvicted = 0;
#     110                 :        136 :     static int64_t nNextSweep;
#     111                 :        136 :     int64_t nNow = GetTime();
#     112         [ +  + ]:        136 :     if (nNextSweep <= nNow) {
#     113                 :            :         // Sweep out expired orphan pool entries:
#     114                 :          7 :         int nErased = 0;
#     115                 :          7 :         int64_t nMinExpTime = nNow + ORPHAN_TX_EXPIRE_TIME - ORPHAN_TX_EXPIRE_INTERVAL;
#     116                 :          7 :         std::map<uint256, OrphanTx>::iterator iter = m_orphans.begin();
#     117         [ +  + ]:        160 :         while (iter != m_orphans.end())
#     118                 :        153 :         {
#     119                 :        153 :             std::map<uint256, OrphanTx>::iterator maybeErase = iter++;
#     120         [ -  + ]:        153 :             if (maybeErase->second.nTimeExpire <= nNow) {
#     121                 :          0 :                 nErased += EraseTx(maybeErase->second.tx->GetHash());
#     122                 :        153 :             } else {
#     123                 :        153 :                 nMinExpTime = std::min(maybeErase->second.nTimeExpire, nMinExpTime);
#     124                 :        153 :             }
#     125                 :        153 :         }
#     126                 :            :         // Sweep again 5 minutes after the next entry that expires in order to batch the linear scan.
#     127                 :          7 :         nNextSweep = nMinExpTime + ORPHAN_TX_EXPIRE_INTERVAL;
#     128 [ -  + ][ #  # ]:          7 :         if (nErased > 0) LogPrint(BCLog::MEMPOOL, "Erased %d orphan tx due to expiration\n", nErased);
#     129                 :          7 :     }
#     130                 :        136 :     FastRandomContext rng;
#     131         [ +  + ]:        285 :     while (m_orphans.size() > max_orphans)
#     132                 :        149 :     {
#     133                 :            :         // Evict a random orphan:
#     134                 :        149 :         size_t randompos = rng.randrange(m_orphan_list.size());
#     135                 :        149 :         EraseTx(m_orphan_list[randompos]->first);
#     136                 :        149 :         ++nEvicted;
#     137                 :        149 :     }
#     138                 :        136 :     return nEvicted;
#     139                 :        136 : }
#     140                 :            : 
#     141                 :            : void TxOrphanage::AddChildrenToWorkSet(const CTransaction& tx, std::set<uint256>& orphan_work_set) const
#     142                 :       9719 : {
#     143                 :       9719 :     AssertLockHeld(g_cs_orphans);
#     144         [ +  + ]:      31761 :     for (unsigned int i = 0; i < tx.vout.size(); i++) {
#     145                 :      22042 :         const auto it_by_prev = m_outpoint_to_orphan_it.find(COutPoint(tx.GetHash(), i));
#     146         [ +  + ]:      22042 :         if (it_by_prev != m_outpoint_to_orphan_it.end()) {
#     147         [ +  + ]:          5 :             for (const auto& elem : it_by_prev->second) {
#     148                 :          5 :                 orphan_work_set.insert(elem->first);
#     149                 :          5 :             }
#     150                 :          5 :         }
#     151                 :      22042 :     }
#     152                 :       9719 : }
#     153                 :            : 
#     154                 :            : bool TxOrphanage::HaveTx(const GenTxid& gtxid) const
#     155                 :      54379 : {
#     156                 :      54379 :     LOCK(g_cs_orphans);
#     157         [ +  + ]:      54379 :     if (gtxid.IsWtxid()) {
#     158                 :      54226 :         return m_wtxid_to_orphan_it.count(gtxid.GetHash());
#     159                 :      54226 :     } else {
#     160                 :        153 :         return m_orphans.count(gtxid.GetHash());
#     161                 :        153 :     }
#     162                 :      54379 : }
#     163                 :            : 
#     164                 :            : std::pair<CTransactionRef, NodeId> TxOrphanage::GetTx(const uint256& txid) const
#     165                 :          5 : {
#     166                 :          5 :     AssertLockHeld(g_cs_orphans);
#     167                 :            : 
#     168                 :          5 :     const auto it = m_orphans.find(txid);
#     169         [ -  + ]:          5 :     if (it == m_orphans.end()) return {nullptr, -1};
#     170                 :          5 :     return {it->second.tx, it->second.fromPeer};
#     171                 :          5 : }
#     172                 :            : 
#     173                 :            : void TxOrphanage::EraseForBlock(const CBlock& block)
#     174                 :      62992 : {
#     175                 :      62992 :     LOCK(g_cs_orphans);
#     176                 :            : 
#     177                 :      62992 :     std::vector<uint256> vOrphanErase;
#     178                 :            : 
#     179         [ +  + ]:     114398 :     for (const CTransactionRef& ptx : block.vtx) {
#     180                 :     114398 :         const CTransaction& tx = *ptx;
#     181                 :            : 
#     182                 :            :         // Which orphan pool entries must we evict?
#     183         [ +  + ]:     152767 :         for (const auto& txin : tx.vin) {
#     184                 :     152767 :             auto itByPrev = m_outpoint_to_orphan_it.find(txin.prevout);
#     185         [ +  + ]:     152767 :             if (itByPrev == m_outpoint_to_orphan_it.end()) continue;
#     186         [ +  + ]:         38 :             for (auto mi = itByPrev->second.begin(); mi != itByPrev->second.end(); ++mi) {
#     187                 :         19 :                 const CTransaction& orphanTx = *(*mi)->second.tx;
#     188                 :         19 :                 const uint256& orphanHash = orphanTx.GetHash();
#     189                 :         19 :                 vOrphanErase.push_back(orphanHash);
#     190                 :         19 :             }
#     191                 :         19 :         }
#     192                 :     114398 :     }
#     193                 :            : 
#     194                 :            :     // Erase orphan transactions included or precluded by this block
#     195         [ +  + ]:      62992 :     if (vOrphanErase.size()) {
#     196                 :          1 :         int nErased = 0;
#     197         [ +  + ]:         19 :         for (const uint256& orphanHash : vOrphanErase) {
#     198                 :         19 :             nErased += EraseTx(orphanHash);
#     199                 :         19 :         }
#     200         [ +  - ]:          1 :         LogPrint(BCLog::MEMPOOL, "Erased %d orphan tx included or conflicted by block\n", nErased);
#     201                 :          1 :     }
#     202                 :      62992 : }

Generated by: LCOV version 1.14