LCOV - code coverage report
Current view: top level - src/policy - truc_policy.cpp (source / functions) Coverage Total Hit
Test: fuzz_coverage.info Lines: 93.2 % 132 123
Test Date: 2025-11-28 04:05:21 Functions: 100.0 % 4 4
Branches: 54.1 % 316 171

             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                 :       75976 : std::vector<size_t> FindInPackageParents(const Package& package, const CTransactionRef& ptx)
      20                 :             : {
      21                 :       75976 :     std::vector<size_t> in_package_parents;
      22                 :             : 
      23                 :       75976 :     std::set<Txid> possible_parents;
      24         [ +  + ]:      249360 :     for (auto &input : ptx->vin) {
      25         [ +  - ]:      173384 :         possible_parents.insert(input.prevout.hash);
      26                 :             :     }
      27                 :             : 
      28   [ -  +  +  - ]:      113929 :     for (size_t i{0}; i < package.size(); ++i) {
      29         [ +  - ]:      113929 :         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         [ +  + ]:      113929 :         if (&(*tx) == &(*ptx)) break;
      33         [ +  + ]:       37953 :         if (possible_parents.count(tx->GetHash())) {
      34         [ +  - ]:       31465 :             in_package_parents.push_back(i);
      35                 :             :         }
      36                 :             :     }
      37                 :       75976 :     return in_package_parents;
      38                 :       75976 : }
      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                 :         744 :     ParentInfo(const Txid& txid, const Wtxid& wtxid, decltype(CTransaction::version) version, bool has_mempool_descendant) :
      53                 :         744 :         m_txid{txid}, m_wtxid{wtxid}, m_version{version},
      54                 :         744 :         m_has_mempool_descendant{has_mempool_descendant}
      55                 :         744 :     {}
      56                 :             : };
      57                 :             : 
      58                 :       75976 : std::optional<std::string> PackageTRUCChecks(const CTxMemPool& pool, const CTransactionRef& ptx, int64_t vsize,
      59                 :             :                                            const Package& package,
      60                 :             :                                            const std::vector<CTxMemPoolEntry::CTxMemPoolEntryRef>& mempool_parents)
      61                 :             : {
      62                 :             :     // This function is specialized for these limits, and must be reimplemented if they ever change.
      63                 :       75976 :     static_assert(TRUC_ANCESTOR_LIMIT == 2);
      64                 :       75976 :     static_assert(TRUC_DESCENDANT_LIMIT == 2);
      65                 :             : 
      66                 :       75976 :     const auto in_package_parents{FindInPackageParents(package, ptx)};
      67                 :             : 
      68                 :             :     // Now we have all parents, so we can start checking TRUC rules.
      69         [ +  + ]:       75976 :     if (ptx->version == TRUC_VERSION) {
      70                 :             :         // SingleTRUCChecks should have checked this already.
      71         [ +  - ]:        2950 :         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   [ -  +  -  +  :        2950 :         if (mempool_parents.size() + in_package_parents.size() + 1 > TRUC_ANCESTOR_LIMIT) {
                   +  + ]
      77         [ +  - ]:          32 :             return strprintf("tx %s (wtxid=%s) would have too many ancestors",
      78   [ +  -  +  -  :          48 :                              ptx->GetHash().ToString(), ptx->GetWitnessHash().ToString());
                   +  - ]
      79                 :             :         }
      80                 :             : 
      81         [ +  + ]:        2934 :         if (mempool_parents.size()) {
      82   [ +  -  -  +  :         319 :             if (pool.GetAncestorCount(mempool_parents[0]) + in_package_parents.size() + 1 > TRUC_ANCESTOR_LIMIT) {
                   -  + ]
      83         [ #  # ]:           0 :                 return strprintf("tx %s (wtxid=%s) would have too many ancestors",
      84   [ #  #  #  #  :           0 :                                  ptx->GetHash().ToString(), ptx->GetWitnessHash().ToString());
                   #  # ]
      85                 :             :             }
      86                 :             :         }
      87                 :             : 
      88   [ -  +  -  + ]:        2934 :         const bool has_parent{mempool_parents.size() + in_package_parents.size() > 0};
      89         [ +  + ]:        2934 :         if (has_parent) {
      90                 :             :             // A TRUC child cannot be too large.
      91         [ +  + ]:         853 :             if (vsize > TRUC_CHILD_MAX_VSIZE) {
      92         [ +  - ]:         218 :                 return strprintf("version=3 child tx %s (wtxid=%s) is too big: %u > %u virtual bytes",
      93   [ +  -  +  -  :         218 :                                  ptx->GetHash().ToString(), ptx->GetWitnessHash().ToString(),
                   +  - ]
      94                 :         109 :                                  vsize, TRUC_CHILD_MAX_VSIZE);
      95                 :             :             }
      96                 :             : 
      97                 :             :             // Exactly 1 parent exists, either in mempool or package. Find it.
      98                 :        1488 :             const auto parent_info = [&] {
      99   [ -  +  +  + ]:         744 :                 if (mempool_parents.size() > 0) {
     100                 :         319 :                     const auto& mempool_parent = &mempool_parents[0].get();
     101                 :         319 :                     return ParentInfo{mempool_parent->GetTx().GetHash(),
     102                 :         319 :                                       mempool_parent->GetTx().GetWitnessHash(),
     103                 :         319 :                                       mempool_parent->GetTx().version,
     104                 :         319 :                                       /*has_mempool_descendant=*/pool.GetDescendantCount(*mempool_parent) > 1};
     105                 :             :                 } else {
     106                 :         425 :                     auto& parent_index = in_package_parents.front();
     107                 :         425 :                     auto& package_parent = package.at(parent_index);
     108                 :         425 :                     return ParentInfo{package_parent->GetHash(),
     109                 :             :                                       package_parent->GetWitnessHash(),
     110                 :         425 :                                       package_parent->version,
     111                 :         425 :                                       /*has_mempool_descendant=*/false};
     112                 :             :                 }
     113         [ +  - ]:         744 :             }();
     114                 :             : 
     115                 :             :             // If there is a parent, it must have the right version.
     116         [ +  + ]:         744 :             if (parent_info.m_version != TRUC_VERSION) {
     117                 :          79 :                 return strprintf("version=3 tx %s (wtxid=%s) cannot spend from non-version=3 tx %s (wtxid=%s)",
     118   [ +  -  +  -  :         158 :                                  ptx->GetHash().ToString(), ptx->GetWitnessHash().ToString(),
                   +  - ]
     119   [ +  -  +  -  :         237 :                                  parent_info.m_txid.ToString(), parent_info.m_wtxid.ToString());
                   +  - ]
     120                 :             :             }
     121                 :             : 
     122         [ +  + ]:        1677 :             for (const auto& package_tx : package) {
     123                 :             :                 // Skip same tx.
     124         [ +  + ]:        1214 :                 if (&(*package_tx) == &(*ptx)) continue;
     125                 :             : 
     126         [ +  + ]:        1394 :                 for (auto& input : package_tx->vin) {
     127                 :             :                     // Fail if we find another tx with the same parent. We don't check whether the
     128                 :             :                     // sibling is to-be-replaced (done in SingleTRUCChecks) because these transactions
     129                 :             :                     // are within the same package.
     130         [ +  + ]:        1047 :                     if (input.prevout.hash == parent_info.m_txid) {
     131                 :          10 :                         return strprintf("tx %s (wtxid=%s) would exceed descendant count limit",
     132         [ +  - ]:          20 :                                          parent_info.m_txid.ToString(),
     133   [ +  -  +  - ]:          30 :                                          parent_info.m_wtxid.ToString());
     134                 :             :                     }
     135                 :             : 
     136                 :             :                     // This tx can't have both a parent and an in-package child.
     137         [ +  + ]:        1037 :                     if (input.prevout.hash == ptx->GetHash()) {
     138         [ +  - ]:         384 :                         return strprintf("tx %s (wtxid=%s) would have too many ancestors",
     139   [ +  -  +  -  :         576 :                                          package_tx->GetHash().ToString(), package_tx->GetWitnessHash().ToString());
                   +  - ]
     140                 :             :                     }
     141                 :             :                 }
     142                 :             :             }
     143                 :             : 
     144         [ -  + ]:         463 :             if (parent_info.m_has_mempool_descendant) {
     145                 :           0 :                 return strprintf("tx %s (wtxid=%s) would exceed descendant count limit",
     146   [ #  #  #  #  :           0 :                                 parent_info.m_txid.ToString(), parent_info.m_wtxid.ToString());
                   #  # ]
     147                 :             :             }
     148                 :             :         }
     149                 :             :     } else {
     150                 :             :         // Non-TRUC transactions cannot have TRUC parents.
     151         [ +  + ]:      105240 :         for (auto it : mempool_parents) {
     152         [ -  + ]:       32214 :             if (it.get().GetTx().version == TRUC_VERSION) {
     153                 :           0 :                 return strprintf("non-version=3 tx %s (wtxid=%s) cannot spend from version=3 tx %s (wtxid=%s)",
     154   [ #  #  #  #  :           0 :                                  ptx->GetHash().ToString(), ptx->GetWitnessHash().ToString(),
                   #  # ]
     155   [ #  #  #  #  :           0 :                                  it.get().GetSharedTx()->GetHash().ToString(), it.get().GetSharedTx()->GetWitnessHash().ToString());
          #  #  #  #  #  
             #  #  #  #  
                      # ]
     156                 :             :             }
     157                 :             :         }
     158         [ +  + ]:      103772 :         for (const auto& index: in_package_parents) {
     159   [ +  -  +  + ]:       30900 :             if (package.at(index)->version == TRUC_VERSION) {
     160                 :         154 :                 return strprintf("non-version=3 tx %s (wtxid=%s) cannot spend from version=3 tx %s (wtxid=%s)",
     161   [ +  -  +  - ]:         308 :                                  ptx->GetHash().ToString(),
     162   [ +  -  +  - ]:         308 :                                  ptx->GetWitnessHash().ToString(),
     163   [ +  -  +  - ]:         308 :                                  package.at(index)->GetHash().ToString(),
     164   [ +  -  +  -  :         462 :                                  package.at(index)->GetWitnessHash().ToString());
                   +  - ]
     165                 :             :             }
     166                 :             :         }
     167                 :             :     }
     168                 :       75416 :     return std::nullopt;
     169                 :       75976 : }
     170                 :             : 
     171                 :      300702 : std::optional<std::pair<std::string, CTransactionRef>> SingleTRUCChecks(const CTxMemPool& pool, const CTransactionRef& ptx,
     172                 :             :                                           const std::vector<CTxMemPoolEntry::CTxMemPoolEntryRef>& mempool_parents,
     173                 :             :                                           const std::set<Txid>& direct_conflicts,
     174                 :             :                                           int64_t vsize)
     175                 :             : {
     176                 :      300702 :     LOCK(pool.cs);
     177                 :             :     // Check TRUC and non-TRUC inheritance.
     178         [ +  + ]:      485231 :     for (const auto& entry_ref : mempool_parents) {
     179         [ +  + ]:      188855 :         const auto& entry = &entry_ref.get();
     180   [ +  +  +  + ]:      188855 :         if (ptx->version != TRUC_VERSION && entry->GetTx().version == TRUC_VERSION) {
     181                 :        4598 :             return std::make_pair(strprintf("non-version=3 tx %s (wtxid=%s) cannot spend from version=3 tx %s (wtxid=%s)",
     182   [ +  -  +  -  :        4598 :                              ptx->GetHash().ToString(), ptx->GetWitnessHash().ToString(),
                   +  - ]
     183   [ +  -  +  -  :       13794 :                              entry->GetSharedTx()->GetHash().ToString(), entry->GetSharedTx()->GetWitnessHash().ToString()),
          +  -  +  -  +  
             -  +  -  +  
                      - ]
     184                 :        2299 :                 nullptr);
     185   [ +  +  +  + ]:      186556 :         } else if (ptx->version == TRUC_VERSION && entry->GetTx().version != TRUC_VERSION) {
     186                 :        4054 :             return std::make_pair(strprintf("version=3 tx %s (wtxid=%s) cannot spend from non-version=3 tx %s (wtxid=%s)",
     187   [ +  -  +  -  :        4054 :                              ptx->GetHash().ToString(), ptx->GetWitnessHash().ToString(),
                   +  - ]
     188   [ +  -  +  -  :       12162 :                              entry->GetSharedTx()->GetHash().ToString(), entry->GetSharedTx()->GetWitnessHash().ToString()),
          +  -  +  -  +  
             -  +  -  +  
                      - ]
     189                 :        2027 :                 nullptr);
     190                 :             :         }
     191                 :             :     }
     192                 :             : 
     193                 :             :     // This function is specialized for these limits, and must be reimplemented if they ever change.
     194                 :      296376 :     static_assert(TRUC_ANCESTOR_LIMIT == 2);
     195                 :      296376 :     static_assert(TRUC_DESCENDANT_LIMIT == 2);
     196                 :             : 
     197                 :             :     // The rest of the rules only apply to transactions with version=3.
     198         [ +  + ]:      296376 :     if (ptx->version != TRUC_VERSION) return std::nullopt;
     199                 :             : 
     200         [ +  + ]:       20889 :     if (vsize > TRUC_MAX_VSIZE) {
     201                 :        1944 :         return std::make_pair(strprintf("version=3 tx %s (wtxid=%s) is too big: %u > %u virtual bytes",
     202   [ +  -  +  -  :        1944 :                          ptx->GetHash().ToString(), ptx->GetWitnessHash().ToString(), vsize, TRUC_MAX_VSIZE),
                   +  - ]
     203         [ +  - ]:        1944 :             nullptr);
     204                 :             :     }
     205                 :             : 
     206                 :             :     // Check that TRUC_ANCESTOR_LIMIT would not be violated.
     207   [ -  +  +  + ]:       19917 :     if (mempool_parents.size() + 1 > TRUC_ANCESTOR_LIMIT) {
     208                 :          16 :         return std::make_pair(strprintf("tx %s (wtxid=%s) would have too many ancestors",
     209   [ +  -  +  -  :          16 :                          ptx->GetHash().ToString(), ptx->GetWitnessHash().ToString()),
                   +  - ]
     210         [ +  - ]:          16 :             nullptr);
     211                 :             :     }
     212                 :             : 
     213                 :             :     // Remaining checks only pertain to transactions with unconfirmed ancestors.
     214         [ +  + ]:       19909 :     if (mempool_parents.size() > 0) {
     215                 :             :         // Ensure that the in-mempool parent doesn't have any additional
     216                 :             :         // ancestors, as that would also be a violation.
     217   [ +  -  +  + ]:        5593 :         if (pool.GetAncestorCount(mempool_parents[0]) + 1 > TRUC_ANCESTOR_LIMIT) {
     218                 :        1154 :             return std::make_pair(strprintf("tx %s (wtxid=%s) would have too many ancestors",
     219   [ +  -  +  -  :        1154 :                              ptx->GetHash().ToString(), ptx->GetWitnessHash().ToString()),
                   +  - ]
     220         [ +  - ]:        1154 :                 nullptr);
     221                 :             :         }
     222                 :             :         // If this transaction spends TRUC parents, it cannot be too large.
     223         [ +  + ]:        5016 :         if (vsize > TRUC_CHILD_MAX_VSIZE) {
     224                 :        5180 :             return std::make_pair(strprintf("version=3 child tx %s (wtxid=%s) is too big: %u > %u virtual bytes",
     225   [ +  -  +  -  :        5180 :                              ptx->GetHash().ToString(), ptx->GetWitnessHash().ToString(), vsize, TRUC_CHILD_MAX_VSIZE),
                   +  - ]
     226         [ +  - ]:        5180 :                 nullptr);
     227                 :             :         }
     228                 :             : 
     229                 :             :         // Check the descendant counts of in-mempool parents.
     230         [ +  - ]:        2426 :         const auto& parent_entry = mempool_parents[0].get();
     231                 :             :         // If there are any parents, this is the only child allowed. The parent cannot have any
     232                 :             :         // other descendants. We handle the possibility of multiple children as that case is
     233                 :             :         // possible through a reorg.
     234         [ +  - ]:        2426 :         CTxMemPool::setEntries descendants;
     235         [ +  - ]:        2426 :         auto parent_it = pool.CalculateDescendants(parent_entry, descendants);
     236                 :        2426 :         descendants.erase(parent_it);
     237                 :             :         // Don't double-count a transaction that is going to be replaced. This logic assumes that
     238                 :             :         // any descendant of the TRUC transaction is a direct child, which makes sense because a
     239                 :             :         // TRUC transaction can only have 1 descendant.
     240   [ +  +  +  + ]:        3774 :         const bool child_will_be_replaced = !descendants.empty() &&
     241                 :        1348 :             std::any_of(descendants.cbegin(), descendants.cend(),
     242                 :        1348 :                 [&direct_conflicts](const CTxMemPool::txiter& child){return direct_conflicts.count(child->GetTx().GetHash()) > 0;});
     243   [ +  -  +  +  :        2426 :         if (pool.GetDescendantCount(parent_entry) + 1 > TRUC_DESCENDANT_LIMIT && !child_will_be_replaced) {
                   +  + ]
     244                 :             :             // Allow sibling eviction for TRUC transaction: if another child already exists, even if
     245                 :             :             // we don't conflict inputs with it, consider evicting it under RBF rules. We rely on TRUC rules
     246                 :             :             // only permitting 1 descendant, as otherwise we would need to have logic for deciding
     247                 :             :             // which descendant to evict. Skip if this isn't true, e.g. if the transaction has
     248                 :             :             // multiple children or the sibling also has descendants due to a reorg.
     249   [ +  -  +  -  :        1192 :             const bool consider_sibling_eviction{pool.GetDescendantCount(parent_entry) == 2 &&
                   -  + ]
     250         [ +  - ]:         596 :                 pool.GetAncestorCount(**descendants.begin()) == 2};
     251                 :             : 
     252                 :             :             // Return the sibling if its eviction can be considered. Provide the "descendant count
     253                 :             :             // limit" string either way, as the caller may decide not to do sibling eviction.
     254         [ +  - ]:        1788 :             return std::make_pair(strprintf("tx %u (wtxid=%s) would exceed descendant count limit",
     255   [ +  -  +  -  :        1788 :                                             parent_entry.GetSharedTx()->GetHash().ToString(),
                   +  - ]
     256   [ +  -  +  -  :        1788 :                                             parent_entry.GetSharedTx()->GetWitnessHash().ToString()),
                   +  - ]
     257   [ +  -  +  - ]:        2384 :                                   consider_sibling_eviction ? (*descendants.begin())->GetSharedTx() : nullptr);
     258                 :             :         }
     259                 :        2426 :     }
     260                 :       16146 :     return std::nullopt;
     261                 :      300702 : }
        

Generated by: LCOV version 2.0-1