LCOV - code coverage report
Current view: top level - src/policy - truc_policy.cpp (source / functions) Coverage Total Hit
Test: test_bitcoin_coverage.info Lines: 94.1 % 118 111
Test Date: 2024-11-04 04:45:35 Functions: 100.0 % 4 4
Branches: 58.6 % 268 157

             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                 :          27 : std::vector<size_t> FindInPackageParents(const Package& package, const CTransactionRef& ptx)
      20                 :             : {
      21                 :          27 :     std::vector<size_t> in_package_parents;
      22                 :             : 
      23                 :          27 :     std::set<Txid> possible_parents;
      24         [ +  + ]:         170 :     for (auto &input : ptx->vin) {
      25         [ +  - ]:         143 :         possible_parents.insert(input.prevout.hash);
      26                 :             :     }
      27                 :             : 
      28         [ +  - ]:          52 :     for (size_t i{0}; i < package.size(); ++i) {
      29         [ +  - ]:          52 :         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         [ +  + ]:          52 :         if (&(*tx) == &(*ptx)) break;
      33         [ +  + ]:          25 :         if (possible_parents.count(tx->GetHash())) {
      34         [ +  - ]:          23 :             in_package_parents.push_back(i);
      35                 :             :         }
      36                 :             :     }
      37                 :          27 :     return in_package_parents;
      38                 :          27 : }
      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                 :           7 :     ParentInfo(const Txid& txid, const Wtxid& wtxid, decltype(CTransaction::version) version, bool has_mempool_descendant) :
      53                 :           7 :         m_txid{txid}, m_wtxid{wtxid}, m_version{version},
      54                 :           7 :         m_has_mempool_descendant{has_mempool_descendant}
      55                 :           1 :     {}
      56                 :             : };
      57                 :             : 
      58                 :          27 : 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                 :          27 :     static_assert(TRUC_ANCESTOR_LIMIT == 2);
      64                 :          27 :     static_assert(TRUC_DESCENDANT_LIMIT == 2);
      65                 :             : 
      66                 :          27 :     const auto in_package_parents{FindInPackageParents(package, ptx)};
      67                 :             : 
      68                 :             :     // Now we have all ancestors, so we can start checking TRUC rules.
      69         [ +  + ]:          27 :     if (ptx->version == TRUC_VERSION) {
      70                 :             :         // SingleTRUCChecks should have checked this already.
      71   [ +  -  -  + ]:          11 :         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         [ +  + ]:          11 :         if (mempool_ancestors.size() + in_package_parents.size() + 1 > TRUC_ANCESTOR_LIMIT) {
      77         [ +  - ]:           4 :             return strprintf("tx %s (wtxid=%s) would have too many ancestors",
      78   [ +  -  +  -  :           6 :                              ptx->GetHash().ToString(), ptx->GetWitnessHash().ToString());
                   +  - ]
      79                 :             :         }
      80                 :             : 
      81         [ +  - ]:           9 :         const bool has_parent{mempool_ancestors.size() + in_package_parents.size() > 0};
      82         [ +  - ]:           9 :         if (has_parent) {
      83                 :             :             // A TRUC child cannot be too large.
      84         [ +  + ]:           9 :             if (vsize > TRUC_CHILD_MAX_VSIZE) {
      85         [ +  - ]:           4 :                 return strprintf("version=3 child tx %s (wtxid=%s) is too big: %u > %u virtual bytes",
      86   [ +  -  +  -  :           4 :                                  ptx->GetHash().ToString(), ptx->GetWitnessHash().ToString(),
                   +  - ]
      87                 :           2 :                                  vsize, TRUC_CHILD_MAX_VSIZE);
      88                 :             :             }
      89                 :             : 
      90                 :             :             // Exactly 1 parent exists, either in mempool or package. Find it.
      91                 :          14 :             const auto parent_info = [&] {
      92         [ +  + ]:           7 :                 if (mempool_ancestors.size() > 0) {
      93                 :           1 :                     auto& mempool_parent = *mempool_ancestors.begin();
      94                 :           1 :                     return ParentInfo{mempool_parent->GetTx().GetHash(),
      95                 :           1 :                                       mempool_parent->GetTx().GetWitnessHash(),
      96                 :           1 :                                       mempool_parent->GetTx().version,
      97                 :           1 :                                       /*has_mempool_descendant=*/mempool_parent->GetCountWithDescendants() > 1};
      98                 :             :                 } else {
      99                 :           6 :                     auto& parent_index = in_package_parents.front();
     100                 :           6 :                     auto& package_parent = package.at(parent_index);
     101                 :           6 :                     return ParentInfo{package_parent->GetHash(),
     102                 :             :                                       package_parent->GetWitnessHash(),
     103                 :           6 :                                       package_parent->version,
     104                 :           6 :                                       /*has_mempool_descendant=*/false};
     105                 :             :                 }
     106         [ +  - ]:           7 :             }();
     107                 :             : 
     108                 :             :             // If there is a parent, it must have the right version.
     109         [ +  + ]:           7 :             if (parent_info.m_version != TRUC_VERSION) {
     110                 :           2 :                 return strprintf("version=3 tx %s (wtxid=%s) cannot spend from non-version=3 tx %s (wtxid=%s)",
     111   [ +  -  +  -  :           4 :                                  ptx->GetHash().ToString(), ptx->GetWitnessHash().ToString(),
                   +  - ]
     112   [ +  -  +  -  :           6 :                                  parent_info.m_txid.ToString(), parent_info.m_wtxid.ToString());
                   +  - ]
     113                 :             :             }
     114                 :             : 
     115         [ +  + ]:          15 :             for (const auto& package_tx : package) {
     116                 :             :                 // Skip same tx.
     117         [ +  + ]:          12 :                 if (&(*package_tx) == &(*ptx)) continue;
     118                 :             : 
     119         [ +  + ]:          14 :                 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         [ +  + ]:           8 :                     if (input.prevout.hash == parent_info.m_txid) {
     124                 :           1 :                         return strprintf("tx %s (wtxid=%s) would exceed descendant count limit",
     125         [ +  - ]:           2 :                                          parent_info.m_txid.ToString(),
     126   [ +  -  +  - ]:           3 :                                          parent_info.m_wtxid.ToString());
     127                 :             :                     }
     128                 :             : 
     129                 :             :                     // This tx can't have both a parent and an in-package child.
     130         [ +  + ]:           7 :                     if (input.prevout.hash == ptx->GetHash()) {
     131         [ +  - ]:           2 :                         return strprintf("tx %s (wtxid=%s) would have too many ancestors",
     132   [ +  -  +  -  :           3 :                                          package_tx->GetHash().ToString(), package_tx->GetWitnessHash().ToString());
                   +  - ]
     133                 :             :                     }
     134                 :             :                 }
     135                 :             :             }
     136                 :             : 
     137         [ -  + ]:           3 :             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                 :             :         }
     142                 :             :     } else {
     143                 :             :         // Non-TRUC transactions cannot have TRUC parents.
     144         [ +  + ]:          19 :         for (auto it : mempool_ancestors) {
     145         [ +  + ]:           4 :             if (it->GetTx().version == TRUC_VERSION) {
     146         [ +  - ]:           2 :                 return strprintf("non-version=3 tx %s (wtxid=%s) cannot spend from version=3 tx %s (wtxid=%s)",
     147   [ +  -  +  -  :           2 :                                  ptx->GetHash().ToString(), ptx->GetWitnessHash().ToString(),
                   +  - ]
     148   [ +  -  +  -  :           7 :                                  it->GetSharedTx()->GetHash().ToString(), it->GetSharedTx()->GetWitnessHash().ToString());
          +  -  +  -  +  
             -  +  -  +  
                      - ]
     149                 :             :             }
     150                 :             :         }
     151         [ +  + ]:          22 :         for (const auto& index: in_package_parents) {
     152   [ +  -  +  + ]:           9 :             if (package.at(index)->version == TRUC_VERSION) {
     153                 :           2 :                 return strprintf("non-version=3 tx %s (wtxid=%s) cannot spend from version=3 tx %s (wtxid=%s)",
     154   [ +  -  +  - ]:           4 :                                  ptx->GetHash().ToString(),
     155   [ +  -  +  - ]:           4 :                                  ptx->GetWitnessHash().ToString(),
     156   [ +  -  +  - ]:           4 :                                  package.at(index)->GetHash().ToString(),
     157   [ +  -  +  -  :           6 :                                  package.at(index)->GetWitnessHash().ToString());
                   +  - ]
     158                 :             :             }
     159                 :             :         }
     160                 :             :     }
     161                 :          16 :     return std::nullopt;
     162                 :          27 : }
     163                 :             : 
     164                 :         126 : 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         [ +  + ]:         154 :     for (const auto& entry : mempool_ancestors) {
     171   [ +  +  +  + ]:          32 :         if (ptx->version != TRUC_VERSION && entry->GetTx().version == TRUC_VERSION) {
     172                 :           4 :             return std::make_pair(strprintf("non-version=3 tx %s (wtxid=%s) cannot spend from version=3 tx %s (wtxid=%s)",
     173   [ +  -  +  -  :           4 :                              ptx->GetHash().ToString(), ptx->GetWitnessHash().ToString(),
                   +  - ]
     174   [ +  -  +  -  :          12 :                              entry->GetSharedTx()->GetHash().ToString(), entry->GetSharedTx()->GetWitnessHash().ToString()),
          +  -  +  -  +  
             -  +  -  +  
                      - ]
     175         [ +  - ]:           4 :                 nullptr);
     176   [ +  +  +  + ]:          30 :         } else if (ptx->version == TRUC_VERSION && entry->GetTx().version != TRUC_VERSION) {
     177                 :           4 :             return std::make_pair(strprintf("version=3 tx %s (wtxid=%s) cannot spend from non-version=3 tx %s (wtxid=%s)",
     178   [ +  -  +  -  :           4 :                              ptx->GetHash().ToString(), ptx->GetWitnessHash().ToString(),
                   +  - ]
     179   [ +  -  +  -  :          12 :                              entry->GetSharedTx()->GetHash().ToString(), entry->GetSharedTx()->GetWitnessHash().ToString()),
          +  -  +  -  +  
             -  +  -  +  
                      - ]
     180         [ +  - ]:           4 :                 nullptr);
     181                 :             :         }
     182                 :             :     }
     183                 :             : 
     184                 :             :     // This function is specialized for these limits, and must be reimplemented if they ever change.
     185                 :         122 :     static_assert(TRUC_ANCESTOR_LIMIT == 2);
     186                 :         122 :     static_assert(TRUC_DESCENDANT_LIMIT == 2);
     187                 :             : 
     188                 :             :     // The rest of the rules only apply to transactions with version=3.
     189         [ +  + ]:         122 :     if (ptx->version != TRUC_VERSION) return std::nullopt;
     190                 :             : 
     191         [ -  + ]:          11 :     if (vsize > TRUC_MAX_VSIZE) {
     192                 :           0 :         return std::make_pair(strprintf("version=3 tx %s (wtxid=%s) is too big: %u > %u virtual bytes",
     193   [ #  #  #  # ]:           0 :                          ptx->GetHash().ToString(), ptx->GetWitnessHash().ToString(), vsize, TRUC_MAX_VSIZE),
     194                 :           0 :             nullptr);
     195                 :             :     }
     196                 :             : 
     197                 :             :     // Check that TRUC_ANCESTOR_LIMIT would not be violated.
     198         [ +  + ]:          11 :     if (mempool_ancestors.size() + 1 > TRUC_ANCESTOR_LIMIT) {
     199                 :           4 :         return std::make_pair(strprintf("tx %s (wtxid=%s) would have too many ancestors",
     200   [ +  -  +  - ]:           4 :                          ptx->GetHash().ToString(), ptx->GetWitnessHash().ToString()),
     201                 :           4 :             nullptr);
     202                 :             :     }
     203                 :             : 
     204                 :             :     // Remaining checks only pertain to transactions with unconfirmed ancestors.
     205         [ +  - ]:           9 :     if (mempool_ancestors.size() > 0) {
     206                 :             :         // If this transaction spends TRUC parents, it cannot be too large.
     207         [ +  + ]:           9 :         if (vsize > TRUC_CHILD_MAX_VSIZE) {
     208                 :           4 :             return std::make_pair(strprintf("version=3 child tx %s (wtxid=%s) is too big: %u > %u virtual bytes",
     209   [ +  -  +  - ]:           4 :                              ptx->GetHash().ToString(), ptx->GetWitnessHash().ToString(), vsize, TRUC_CHILD_MAX_VSIZE),
     210                 :           4 :                 nullptr);
     211                 :             :         }
     212                 :             : 
     213                 :             :         // Check the descendant counts of in-mempool ancestors.
     214         [ +  + ]:           7 :         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         [ +  + ]:           7 :         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   [ +  +  +  + ]:          11 :         const bool child_will_be_replaced = !children.empty() &&
     223                 :           4 :             std::any_of(children.cbegin(), children.cend(),
     224                 :           5 :                 [&direct_conflicts](const CTxMemPoolEntry& child){return direct_conflicts.count(child.GetTx().GetHash()) > 0;});
     225   [ +  +  +  + ]:           7 :         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   [ +  +  -  + ]:           3 :             const bool consider_sibling_eviction{parent_entry->GetCountWithDescendants() == 2 &&
     232         [ -  + ]:           1 :                 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         [ +  - ]:           9 :             return std::make_pair(strprintf("tx %u (wtxid=%s) would exceed descendant count limit",
     237   [ +  -  +  -  :          12 :                                             parent_entry->GetSharedTx()->GetHash().ToString(),
             +  -  +  - ]
     238   [ +  -  +  -  :          12 :                                             parent_entry->GetSharedTx()->GetWitnessHash().ToString()),
             +  -  +  - ]
     239   [ +  -  +  - ]:          10 :                                   consider_sibling_eviction ?  children.begin()->get().GetSharedTx() : nullptr);
     240                 :             :         }
     241                 :             :     }
     242                 :           4 :     return std::nullopt;
     243                 :             : }
        

Generated by: LCOV version 2.0-1