LCOV - code coverage report
Current view: top level - src/policy - truc_policy.cpp (source / functions) Coverage Total Hit
Test: fuzz_coverage.info Lines: 92.2 % 128 118
Test Date: 2024-09-01 05:20:30 Functions: 100.0 % 6 6
Branches: 55.3 % 333 184

             Branch data     Line data    Source code
       1                 :             : // Copyright (c) 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 <policy/truc_policy.h>
       6                 :             : 
       7                 :             : #include <coins.h>
       8                 :             : #include <consensus/amount.h>
       9                 :             : #include <logging.h>
      10                 :             : #include <tinyformat.h>
      11                 :             : #include <util/check.h>
      12                 :             : 
      13                 :             : #include <algorithm>
      14                 :             : #include <numeric>
      15                 :             : #include <vector>
      16                 :             : 
      17                 :             : /** Helper for PackageTRUCChecks: Returns a vector containing the indices of transactions (within
      18                 :             :  * package) that are direct parents of ptx. */
      19                 :       19444 : std::vector<size_t> FindInPackageParents(const Package& package, const CTransactionRef& ptx)
      20                 :             : {
      21                 :       19444 :     std::vector<size_t> in_package_parents;
      22                 :             : 
      23                 :       19444 :     std::set<Txid> possible_parents;
      24         [ +  + ]:       68589 :     for (auto &input : ptx->vin) {
      25         [ +  - ]:       49145 :         possible_parents.insert(input.prevout.hash);
      26                 :       49145 :     }
      27                 :             : 
      28         [ +  - ]:       41411 :     for (size_t i{0}; i < package.size(); ++i) {
      29         [ +  - ]:       21967 :         const auto& tx = package.at(i);
      30                 :             :         // We assume the package is sorted, so that we don't need to continue
      31                 :             :         // looking past the transaction itself.
      32         [ +  + ]:       21967 :         if (&(*tx) == &(*ptx)) break;
      33   [ +  -  +  -  :        2523 :         if (possible_parents.count(tx->GetHash())) {
                   +  + ]
      34         [ +  - ]:        1956 :             in_package_parents.push_back(i);
      35                 :        1956 :         }
      36         [ +  + ]:       21967 :     }
      37                 :       19444 :     return in_package_parents;
      38         [ +  - ]:       19444 : }
      39                 :             : 
      40                 :             : /** Helper for PackageTRUCChecks, storing info for a mempool or package parent. */
      41                 :             : struct ParentInfo {
      42                 :             :     /** Txid used to identify this parent by prevout */
      43                 :             :     const Txid& m_txid;
      44                 :             :     /** Wtxid used for debug string */
      45                 :             :     const Wtxid& m_wtxid;
      46                 :             :     /** version used to check inheritance of TRUC and non-TRUC */
      47                 :             :     decltype(CTransaction::version) m_version;
      48                 :             :     /** If parent is in mempool, whether it has any descendants in mempool. */
      49                 :             :     bool m_has_mempool_descendant;
      50                 :             : 
      51                 :             :     ParentInfo() = delete;
      52                 :        1308 :     ParentInfo(const Txid& txid, const Wtxid& wtxid, decltype(CTransaction::version) version, bool has_mempool_descendant) :
      53                 :        1308 :         m_txid{txid}, m_wtxid{wtxid}, m_version{version},
      54                 :        1308 :         m_has_mempool_descendant{has_mempool_descendant}
      55                 :        1308 :     {}
      56                 :             : };
      57                 :             : 
      58                 :       19444 : std::optional<std::string> PackageTRUCChecks(const CTransactionRef& ptx, int64_t vsize,
      59                 :             :                                            const Package& package,
      60                 :             :                                            const CTxMemPool::setEntries& mempool_ancestors)
      61                 :             : {
      62                 :             :     // This function is specialized for these limits, and must be reimplemented if they ever change.
      63                 :             :     static_assert(TRUC_ANCESTOR_LIMIT == 2);
      64                 :             :     static_assert(TRUC_DESCENDANT_LIMIT == 2);
      65                 :             : 
      66                 :       19444 :     const auto in_package_parents{FindInPackageParents(package, ptx)};
      67                 :             : 
      68                 :             :     // Now we have all ancestors, so we can start checking TRUC rules.
      69         [ +  + ]:       19444 :     if (ptx->version == TRUC_VERSION) {
      70                 :             :         // SingleTRUCChecks should have checked this already.
      71   [ +  -  -  + ]:       10046 :         if (!Assume(vsize <= TRUC_MAX_VSIZE)) {
      72         [ #  # ]:           0 :             return strprintf("version=3 tx %s (wtxid=%s) is too big: %u > %u virtual bytes",
      73   [ #  #  #  #  :           0 :                              ptx->GetHash().ToString(), ptx->GetWitnessHash().ToString(), vsize, TRUC_MAX_VSIZE);
             #  #  #  # ]
      74                 :             :         }
      75                 :             : 
      76         [ +  + ]:       10046 :         if (mempool_ancestors.size() + in_package_parents.size() + 1 > TRUC_ANCESTOR_LIMIT) {
      77         [ +  - ]:          52 :             return strprintf("tx %s (wtxid=%s) would have too many ancestors",
      78   [ +  -  +  -  :          26 :                              ptx->GetHash().ToString(), ptx->GetWitnessHash().ToString());
             +  -  +  - ]
      79                 :             :         }
      80                 :             : 
      81                 :       10020 :         const bool has_parent{mempool_ancestors.size() + in_package_parents.size() > 0};
      82         [ +  + ]:       10020 :         if (has_parent) {
      83                 :             :             // A TRUC child cannot be too large.
      84         [ +  + ]:        1390 :             if (vsize > TRUC_CHILD_MAX_VSIZE) {
      85         [ +  - ]:         164 :                 return strprintf("version=3 child tx %s (wtxid=%s) is too big: %u > %u virtual bytes",
      86   [ +  -  +  -  :          82 :                                  ptx->GetHash().ToString(), ptx->GetWitnessHash().ToString(),
             +  -  +  - ]
      87                 :             :                                  vsize, TRUC_CHILD_MAX_VSIZE);
      88                 :             :             }
      89                 :             : 
      90                 :             :             // Exactly 1 parent exists, either in mempool or package. Find it.
      91         [ +  - ]:        2616 :             const auto parent_info = [&] {
      92         [ +  + ]:        1308 :                 if (mempool_ancestors.size() > 0) {
      93                 :         303 :                     auto& mempool_parent = *mempool_ancestors.begin();
      94                 :         606 :                     return ParentInfo{mempool_parent->GetTx().GetHash(),
      95                 :         303 :                                       mempool_parent->GetTx().GetWitnessHash(),
      96                 :         303 :                                       mempool_parent->GetTx().version,
      97                 :         303 :                                       /*has_mempool_descendant=*/mempool_parent->GetCountWithDescendants() > 1};
      98                 :         303 :                 } else {
      99                 :        1005 :                     auto& parent_index = in_package_parents.front();
     100                 :        1005 :                     auto& package_parent = package.at(parent_index);
     101                 :        2010 :                     return ParentInfo{package_parent->GetHash(),
     102                 :        1005 :                                       package_parent->GetWitnessHash(),
     103                 :        1005 :                                       package_parent->version,
     104                 :             :                                       /*has_mempool_descendant=*/false};
     105                 :        1005 :                 }
     106                 :        1308 :             }();
     107                 :             : 
     108                 :             :             // If there is a parent, it must have the right version.
     109         [ +  + ]:        1308 :             if (parent_info.m_version != TRUC_VERSION) {
     110         [ +  - ]:          14 :                 return strprintf("version=3 tx %s (wtxid=%s) cannot spend from non-version=3 tx %s (wtxid=%s)",
     111   [ +  -  +  -  :           7 :                                  ptx->GetHash().ToString(), ptx->GetWitnessHash().ToString(),
             +  -  +  - ]
     112   [ +  -  +  - ]:           7 :                                  parent_info.m_txid.ToString(), parent_info.m_wtxid.ToString());
     113                 :             :             }
     114                 :             : 
     115   [ +  +  +  + ]:        3672 :             for (const auto& package_tx : package) {
     116                 :             :                 // Skip same tx.
     117         [ +  + ]:        2371 :                 if (&(*package_tx) == &(*ptx)) continue;
     118                 :             : 
     119   [ +  +  +  + ]:        2751 :                 for (auto& input : package_tx->vin) {
     120                 :             :                     // Fail if we find another tx with the same parent. We don't check whether the
     121                 :             :                     // sibling is to-be-replaced (done in SingleTRUCChecks) because these transactions
     122                 :             :                     // are within the same package.
     123   [ +  -  +  - ]:        1681 :                     if (input.prevout.hash == parent_info.m_txid) {
     124         [ #  # ]:           0 :                         return strprintf("tx %s (wtxid=%s) would exceed descendant count limit",
     125         [ #  # ]:           0 :                                          parent_info.m_txid.ToString(),
     126         [ #  # ]:           0 :                                          parent_info.m_wtxid.ToString());
     127                 :             :                     }
     128                 :             : 
     129                 :             :                     // This tx can't have both a parent and an in-package child.
     130   [ +  -  +  -  :        1681 :                     if (input.prevout.hash == ptx->GetHash()) {
                   +  + ]
     131         [ +  - ]:         144 :                         return strprintf("tx %s (wtxid=%s) would have too many ancestors",
     132   [ +  -  +  -  :          72 :                                          package_tx->GetHash().ToString(), package_tx->GetWitnessHash().ToString());
             +  -  +  - ]
     133                 :             :                     }
     134         [ +  + ]:        1681 :                 }
     135      [ +  +  + ]:        2371 :             }
     136                 :             : 
     137         [ -  + ]:        1229 :             if (parent_info.m_has_mempool_descendant) {
     138         [ #  # ]:           0 :                 return strprintf("tx %s (wtxid=%s) would exceed descendant count limit",
     139   [ #  #  #  # ]:           0 :                                 parent_info.m_txid.ToString(), parent_info.m_wtxid.ToString());
     140                 :             :             }
     141         [ +  + ]:        1308 :         }
     142         [ +  + ]:       10020 :     } else {
     143                 :             :         // Non-TRUC transactions cannot have TRUC parents.
     144   [ +  +  -  + ]:       16834 :         for (auto it : mempool_ancestors) {
     145   [ +  -  +  -  :        7436 :             if (it->GetTx().version == TRUC_VERSION) {
                   -  + ]
     146         [ #  # ]:           0 :                 return strprintf("non-version=3 tx %s (wtxid=%s) cannot spend from version=3 tx %s (wtxid=%s)",
     147   [ #  #  #  #  :           0 :                                  ptx->GetHash().ToString(), ptx->GetWitnessHash().ToString(),
             #  #  #  # ]
     148   [ #  #  #  #  :           0 :                                  it->GetSharedTx()->GetHash().ToString(), it->GetSharedTx()->GetWitnessHash().ToString());
          #  #  #  #  #  
          #  #  #  #  #  
                   #  # ]
     149                 :             :             }
     150         [ -  + ]:        7436 :         }
     151   [ +  +  +  + ]:       10137 :         for (const auto& index: in_package_parents) {
     152   [ +  -  +  + ]:         739 :             if (package.at(index)->version == TRUC_VERSION) {
     153         [ -  + ]:         130 :                 return strprintf("non-version=3 tx %s (wtxid=%s) cannot spend from version=3 tx %s (wtxid=%s)",
     154   [ +  -  +  - ]:          65 :                                  ptx->GetHash().ToString(),
     155   [ +  -  +  - ]:          65 :                                  ptx->GetWitnessHash().ToString(),
     156   [ +  -  +  -  :          65 :                                  package.at(index)->GetHash().ToString(),
                   +  - ]
     157   [ +  -  +  -  :          65 :                                  package.at(index)->GetWitnessHash().ToString());
                   +  - ]
     158                 :             :             }
     159         [ +  + ]:         739 :         }
     160                 :             :     }
     161                 :       19192 :     return std::nullopt;
     162                 :       19444 : }
     163                 :             : 
     164                 :      238484 : std::optional<std::pair<std::string, CTransactionRef>> SingleTRUCChecks(const CTransactionRef& ptx,
     165                 :             :                                           const CTxMemPool::setEntries& mempool_ancestors,
     166                 :             :                                           const std::set<Txid>& direct_conflicts,
     167                 :             :                                           int64_t vsize)
     168                 :             : {
     169                 :             :     // Check TRUC and non-TRUC inheritance.
     170   [ +  +  +  + ]:      345410 :     for (const auto& entry : mempool_ancestors) {
     171   [ +  +  +  + ]:      106926 :         if (ptx->version != TRUC_VERSION && entry->GetTx().version == TRUC_VERSION) {
     172   [ -  +  +  - ]:        6850 :             return std::make_pair(strprintf("non-version=3 tx %s (wtxid=%s) cannot spend from version=3 tx %s (wtxid=%s)",
     173   [ +  -  +  - ]:        3425 :                              ptx->GetHash().ToString(), ptx->GetWitnessHash().ToString(),
     174   [ +  -  +  -  :        3425 :                              entry->GetSharedTx()->GetHash().ToString(), entry->GetSharedTx()->GetWitnessHash().ToString()),
          +  -  +  -  +  
          -  +  -  +  -  
                   +  - ]
     175                 :        3425 :                 nullptr);
     176   [ +  +  +  + ]:      103501 :         } else if (ptx->version == TRUC_VERSION && entry->GetTx().version != TRUC_VERSION) {
     177   [ -  +  +  - ]:        9900 :             return std::make_pair(strprintf("version=3 tx %s (wtxid=%s) cannot spend from non-version=3 tx %s (wtxid=%s)",
     178   [ +  -  +  - ]:        4950 :                              ptx->GetHash().ToString(), ptx->GetWitnessHash().ToString(),
     179   [ +  -  +  -  :        4950 :                              entry->GetSharedTx()->GetHash().ToString(), entry->GetSharedTx()->GetWitnessHash().ToString()),
          +  -  +  -  +  
          -  +  -  +  -  
                   +  - ]
     180                 :        4950 :                 nullptr);
     181                 :             :         }
     182         [ +  + ]:      106926 :     }
     183                 :             : 
     184                 :             :     // This function is specialized for these limits, and must be reimplemented if they ever change.
     185                 :             :     static_assert(TRUC_ANCESTOR_LIMIT == 2);
     186                 :             :     static_assert(TRUC_DESCENDANT_LIMIT == 2);
     187                 :             : 
     188                 :             :     // The rest of the rules only apply to transactions with version=3.
     189         [ +  + ]:      230109 :     if (ptx->version != TRUC_VERSION) return std::nullopt;
     190                 :             : 
     191         [ +  + ]:       75343 :     if (vsize > TRUC_MAX_VSIZE) {
     192   [ +  -  +  - ]:       15618 :         return std::make_pair(strprintf("version=3 tx %s (wtxid=%s) is too big: %u > %u virtual bytes",
     193   [ +  -  +  - ]:        7809 :                          ptx->GetHash().ToString(), ptx->GetWitnessHash().ToString(), vsize, TRUC_MAX_VSIZE),
     194                 :        7809 :             nullptr);
     195                 :             :     }
     196                 :             : 
     197                 :             :     // Check that TRUC_ANCESTOR_LIMIT would not be violated.
     198         [ +  + ]:       67534 :     if (mempool_ancestors.size() + 1 > TRUC_ANCESTOR_LIMIT) {
     199   [ +  -  +  - ]:       10648 :         return std::make_pair(strprintf("tx %s (wtxid=%s) would have too many ancestors",
     200   [ +  -  +  - ]:        5324 :                          ptx->GetHash().ToString(), ptx->GetWitnessHash().ToString()),
     201                 :        5324 :             nullptr);
     202                 :             :     }
     203                 :             : 
     204                 :             :     // Remaining checks only pertain to transactions with unconfirmed ancestors.
     205         [ +  + ]:       62210 :     if (mempool_ancestors.size() > 0) {
     206                 :             :         // If this transaction spends TRUC parents, it cannot be too large.
     207         [ +  + ]:       18156 :         if (vsize > TRUC_CHILD_MAX_VSIZE) {
     208   [ +  -  +  - ]:        5574 :             return std::make_pair(strprintf("version=3 child tx %s (wtxid=%s) is too big: %u > %u virtual bytes",
     209   [ +  -  +  - ]:        2787 :                              ptx->GetHash().ToString(), ptx->GetWitnessHash().ToString(), vsize, TRUC_CHILD_MAX_VSIZE),
     210                 :        2787 :                 nullptr);
     211                 :             :         }
     212                 :             : 
     213                 :             :         // Check the descendant counts of in-mempool ancestors.
     214                 :       15369 :         const auto& parent_entry = *mempool_ancestors.begin();
     215                 :             :         // If there are any ancestors, this is the only child allowed. The parent cannot have any
     216                 :             :         // other descendants. We handle the possibility of multiple children as that case is
     217                 :             :         // possible through a reorg.
     218                 :       15369 :         const auto& children = parent_entry->GetMemPoolChildrenConst();
     219                 :             :         // Don't double-count a transaction that is going to be replaced. This logic assumes that
     220                 :             :         // any descendant of the TRUC transaction is a direct child, which makes sense because a
     221                 :             :         // TRUC transaction can only have 1 descendant.
     222         [ +  + ]:       25958 :         const bool child_will_be_replaced = !children.empty() &&
     223                 :       21178 :             std::any_of(children.cbegin(), children.cend(),
     224                 :       21178 :                 [&direct_conflicts](const CTxMemPoolEntry& child){return direct_conflicts.count(child.GetTx().GetHash()) > 0;});
     225   [ +  +  +  + ]:       15369 :         if (parent_entry->GetCountWithDescendants() + 1 > TRUC_DESCENDANT_LIMIT && !child_will_be_replaced) {
     226                 :             :             // Allow sibling eviction for TRUC transaction: if another child already exists, even if
     227                 :             :             // we don't conflict inputs with it, consider evicting it under RBF rules. We rely on TRUC rules
     228                 :             :             // only permitting 1 descendant, as otherwise we would need to have logic for deciding
     229                 :             :             // which descendant to evict. Skip if this isn't true, e.g. if the transaction has
     230                 :             :             // multiple children or the sibling also has descendants due to a reorg.
     231         [ -  + ]:        8834 :             const bool consider_sibling_eviction{parent_entry->GetCountWithDescendants() == 2 &&
     232                 :        4417 :                 children.begin()->get().GetCountWithAncestors() == 2};
     233                 :             : 
     234                 :             :             // Return the sibling if its eviction can be considered. Provide the "descendant count
     235                 :             :             // limit" string either way, as the caller may decide not to do sibling eviction.
     236   [ +  -  +  - ]:       13251 :             return std::make_pair(strprintf("tx %u (wtxid=%s) would exceed descendant count limit",
     237   [ +  -  +  - ]:        4417 :                                             parent_entry->GetSharedTx()->GetHash().ToString(),
     238   [ +  -  +  -  :        4417 :                                             parent_entry->GetSharedTx()->GetWitnessHash().ToString()),
             +  -  +  - ]
     239   [ +  -  +  - ]:        4417 :                                   consider_sibling_eviction ?  children.begin()->get().GetSharedTx() : nullptr);
     240                 :        4417 :         }
     241         [ +  + ]:       15369 :     }
     242                 :       55006 :     return std::nullopt;
     243                 :      238484 : }
        

Generated by: LCOV version 2.0-1