LCOV - code coverage report
Current view: top level - src/test/fuzz - rbf.cpp (source / functions) Coverage Total Hit
Test: fuzz_coverage.info Lines: 90.3 % 124 112
Test Date: 2024-09-01 05:20:30 Functions: 66.7 % 6 4
Branches: 58.1 % 248 144

             Branch data     Line data    Source code
       1                 :             : // Copyright (c) 2020-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 <node/mempool_args.h>
       6                 :             : #include <policy/rbf.h>
       7                 :             : #include <primitives/transaction.h>
       8                 :             : #include <sync.h>
       9                 :             : #include <test/fuzz/FuzzedDataProvider.h>
      10                 :             : #include <test/fuzz/fuzz.h>
      11                 :             : #include <test/fuzz/util.h>
      12                 :             : #include <test/fuzz/util/mempool.h>
      13                 :             : #include <test/util/setup_common.h>
      14                 :             : #include <test/util/txmempool.h>
      15                 :             : #include <txmempool.h>
      16                 :             : #include <util/check.h>
      17                 :             : #include <util/translation.h>
      18                 :             : 
      19                 :             : #include <cstdint>
      20                 :             : #include <optional>
      21                 :             : #include <string>
      22                 :             : #include <vector>
      23                 :             : 
      24                 :             : namespace {
      25                 :             : const BasicTestingSetup* g_setup;
      26                 :             : } // namespace
      27                 :             : 
      28                 :             : const int NUM_ITERS = 10000;
      29                 :             : 
      30                 :             : std::vector<COutPoint> g_outpoints;
      31                 :             : 
      32                 :           0 : void initialize_rbf()
      33                 :             : {
      34   [ #  #  #  #  :           0 :     static const auto testing_setup = MakeNoLogFileContext<>();
                   #  # ]
      35                 :           0 :     g_setup = testing_setup.get();
      36                 :           0 : }
      37                 :             : 
      38                 :           0 : void initialize_package_rbf()
      39                 :             : {
      40   [ #  #  #  #  :           0 :     static const auto testing_setup = MakeNoLogFileContext<>();
                   #  # ]
      41                 :           0 :     g_setup = testing_setup.get();
      42                 :             : 
      43                 :             :     // Create a fixed set of unique "UTXOs" to source parents from
      44                 :             :     // to avoid fuzzer giving circular references
      45         [ #  # ]:           0 :     for (int i = 0; i < NUM_ITERS; ++i) {
      46                 :           0 :         g_outpoints.emplace_back();
      47                 :           0 :         g_outpoints.back().n = i;
      48                 :           0 :     }
      49                 :             : 
      50                 :           0 : }
      51                 :             : 
      52         [ +  - ]:        1030 : FUZZ_TARGET(rbf, .init = initialize_rbf)
      53                 :             : {
      54                 :        1028 :     FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
      55                 :        1028 :     SetMockTime(ConsumeTime(fuzzed_data_provider));
      56                 :        1028 :     std::optional<CMutableTransaction> mtx = ConsumeDeserializable<CMutableTransaction>(fuzzed_data_provider, TX_WITH_WITNESS);
      57         [ +  + ]:        1028 :     if (!mtx) {
      58                 :          56 :         return;
      59                 :             :     }
      60                 :             : 
      61                 :         972 :     bilingual_str error;
      62   [ +  -  +  - ]:         972 :     CTxMemPool pool{MemPoolOptionsForTest(g_setup->m_node), error};
      63   [ +  -  +  - ]:         972 :     Assert(error.empty());
      64                 :             : 
      65   [ -  +  +  +  :      326597 :     LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), NUM_ITERS)
                   +  + ]
      66                 :             :     {
      67                 :      325625 :         const std::optional<CMutableTransaction> another_mtx = ConsumeDeserializable<CMutableTransaction>(fuzzed_data_provider, TX_WITH_WITNESS);
      68         [ +  + ]:      325625 :         if (!another_mtx) {
      69                 :         310 :             break;
      70                 :             :         }
      71         [ +  - ]:      325315 :         const CTransaction another_tx{*another_mtx};
      72   [ -  +  +  +  :      325315 :         if (fuzzed_data_provider.ConsumeBool() && !mtx->vin.empty()) {
                   +  + ]
      73   [ +  -  +  - ]:        8165 :             mtx->vin[0].prevout = COutPoint{another_tx.GetHash(), 0};
      74                 :        8165 :         }
      75   [ +  -  +  -  :      325315 :         LOCK2(cs_main, pool.cs);
             +  -  +  - ]
      76         [ +  - ]:      325315 :         pool.addUnchecked(ConsumeTxMemPoolEntry(fuzzed_data_provider, another_tx));
      77         [ +  + ]:      325625 :     }
      78         [ +  - ]:         972 :     const CTransaction tx{*mtx};
      79   [ +  -  +  + ]:         972 :     if (fuzzed_data_provider.ConsumeBool()) {
      80   [ +  -  +  -  :         332 :         LOCK2(cs_main, pool.cs);
             +  -  +  - ]
      81         [ +  - ]:         332 :         pool.addUnchecked(ConsumeTxMemPoolEntry(fuzzed_data_provider, tx));
      82                 :         332 :     }
      83                 :             :     {
      84   [ +  -  +  - ]:         972 :         LOCK(pool.cs);
      85         [ +  - ]:         972 :         (void)IsRBFOptIn(tx, pool);
      86                 :         972 :     }
      87         [ -  + ]:        1028 : }
      88                 :             : 
      89         [ +  - ]:        1640 : FUZZ_TARGET(package_rbf, .init = initialize_package_rbf)
      90                 :             : {
      91                 :        1638 :     FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
      92                 :        1638 :     SetMockTime(ConsumeTime(fuzzed_data_provider));
      93                 :             : 
      94                 :             :     // "Real" virtual size is not important for this test since ConsumeTxMemPoolEntry generates its own virtual size values
      95                 :             :     // so we construct small transactions for performance reasons. Child simply needs an input for later to perhaps connect to parent.
      96                 :        1638 :     CMutableTransaction child;
      97         [ +  - ]:        1638 :     child.vin.resize(1);
      98                 :             : 
      99                 :        1638 :     bilingual_str error;
     100   [ +  -  +  - ]:        1638 :     CTxMemPool pool{MemPoolOptionsForTest(g_setup->m_node), error};
     101   [ +  -  +  - ]:        1638 :     Assert(error.empty());
     102                 :             : 
     103                 :             :     // Add a bunch of parent-child pairs to the mempool, and remember them.
     104                 :        1638 :     std::vector<CTransaction> mempool_txs;
     105                 :        1638 :     size_t iter{0};
     106                 :             : 
     107         [ +  - ]:        1638 :     int32_t replacement_vsize = fuzzed_data_provider.ConsumeIntegralInRange<int32_t>(1, 1000000);
     108                 :             : 
     109                 :             :     // Keep track of the total vsize of CTxMemPoolEntry's being added to the mempool to avoid overflow
     110                 :             :     // Add replacement_vsize since this is added to new diagram during RBF check
     111                 :        1638 :     int64_t running_vsize_total{replacement_vsize};
     112                 :             : 
     113   [ +  -  +  -  :        1638 :     LOCK2(cs_main, pool.cs);
             +  -  +  - ]
     114                 :             : 
     115   [ +  -  +  +  :      544605 :     LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), NUM_ITERS)
                   +  + ]
     116                 :             :     {
     117                 :             :         // Make sure txns only have one input, and that a unique input is given to avoid circular references
     118         [ -  + ]:      542967 :         CMutableTransaction parent;
     119         [ -  + ]:      542967 :         assert(iter <= g_outpoints.size());
     120         [ +  - ]:      542967 :         parent.vin.resize(1);
     121                 :      542967 :         parent.vin[0].prevout = g_outpoints[iter++];
     122         [ +  - ]:      542967 :         parent.vout.emplace_back(0, CScript());
     123                 :             : 
     124         [ +  - ]:      542967 :         mempool_txs.emplace_back(parent);
     125                 :      542967 :         const auto parent_entry = ConsumeTxMemPoolEntry(fuzzed_data_provider, mempool_txs.back());
     126         [ +  - ]:      542967 :         running_vsize_total += parent_entry.GetTxSize();
     127         [ +  + ]:      542967 :         if (running_vsize_total > std::numeric_limits<int32_t>::max()) {
     128                 :             :             // We aren't adding this final tx to mempool, so we don't want to conflict with it
     129                 :          22 :             mempool_txs.pop_back();
     130                 :          22 :             break;
     131                 :             :         }
     132         [ +  - ]:      542945 :         pool.addUnchecked(parent_entry);
     133   [ +  -  +  + ]:      542945 :         if (fuzzed_data_provider.ConsumeBool()) {
     134   [ +  -  +  - ]:      527669 :             child.vin[0].prevout = COutPoint{mempool_txs.back().GetHash(), 0};
     135                 :      527669 :         }
     136         [ +  - ]:      542945 :         mempool_txs.emplace_back(child);
     137                 :      542945 :         const auto child_entry = ConsumeTxMemPoolEntry(fuzzed_data_provider, mempool_txs.back());
     138         [ +  - ]:      542945 :         running_vsize_total += child_entry.GetTxSize();
     139         [ +  + ]:      542945 :         if (running_vsize_total > std::numeric_limits<int32_t>::max()) {
     140                 :             :             // We aren't adding this final tx to mempool, so we don't want to conflict with it
     141                 :          30 :             mempool_txs.pop_back();
     142                 :          30 :             break;
     143                 :             :         }
     144         [ +  - ]:      542915 :         pool.addUnchecked(child_entry);
     145                 :             : 
     146   [ +  -  +  + ]:      542915 :         if (fuzzed_data_provider.ConsumeBool()) {
     147   [ +  -  +  -  :      540826 :             pool.PrioritiseTransaction(mempool_txs.back().GetHash().ToUint256(), fuzzed_data_provider.ConsumeIntegralInRange<int32_t>(-100000, 100000));
             +  -  +  - ]
     148                 :      540826 :         }
     149         [ +  + ]:      542967 :     }
     150                 :             : 
     151                 :             :     // Pick some transactions at random to be the direct conflicts
     152                 :        1638 :     CTxMemPool::setEntries direct_conflicts;
     153         [ +  + ]:     1087498 :     for (auto& tx : mempool_txs) {
     154   [ +  -  +  + ]:     1085860 :         if (fuzzed_data_provider.ConsumeBool()) {
     155   [ +  -  +  -  :      723741 :             direct_conflicts.insert(*pool.GetIter(tx.GetHash()));
             +  -  +  - ]
     156                 :      723741 :         }
     157                 :     1085860 :     }
     158                 :             : 
     159                 :             :     // Calculate all conflicts:
     160                 :        1638 :     CTxMemPool::setEntries all_conflicts;
     161         [ +  + ]:      721311 :     for (auto& txiter : direct_conflicts) {
     162         [ +  - ]:      719673 :         pool.CalculateDescendants(txiter, all_conflicts);
     163                 :      719673 :     }
     164                 :             : 
     165                 :             :     // Calculate the chunks for a replacement.
     166                 :        1638 :     CAmount replacement_fees = ConsumeMoney(fuzzed_data_provider);
     167         [ +  - ]:        1638 :     auto calc_results{pool.CalculateChunksForRBF(replacement_fees, replacement_vsize, direct_conflicts, all_conflicts)};
     168                 :             : 
     169         [ +  + ]:        1638 :     if (calc_results.has_value()) {
     170                 :             :         // Sanity checks on the chunks.
     171                 :             : 
     172                 :             :         // Feerates are monotonically decreasing.
     173                 :        1184 :         FeeFrac first_sum;
     174   [ +  -  +  + ]:      311230 :         for (size_t i = 0; i < calc_results->first.size(); ++i) {
     175         [ +  - ]:      310046 :             first_sum += calc_results->first[i];
     176   [ +  +  +  -  :      310046 :             if (i) assert(!(calc_results->first[i - 1] << calc_results->first[i]));
             +  -  -  + ]
     177                 :      310046 :         }
     178                 :        1184 :         FeeFrac second_sum;
     179   [ +  -  +  + ]:       46047 :         for (size_t i = 0; i < calc_results->second.size(); ++i) {
     180         [ +  - ]:       44863 :             second_sum += calc_results->second[i];
     181   [ +  +  +  -  :       44863 :             if (i) assert(!(calc_results->second[i - 1] << calc_results->second[i]));
             +  -  +  - ]
     182                 :       44863 :         }
     183                 :             : 
     184                 :        1184 :         FeeFrac replaced;
     185         [ +  + ]:      399734 :         for (auto txiter : all_conflicts) {
     186   [ +  -  +  - ]:      398550 :             replaced.fee += txiter->GetModifiedFee();
     187   [ +  -  +  - ]:      398550 :             replaced.size += txiter->GetTxSize();
     188                 :      398550 :         }
     189                 :             :         // The total fee & size of the new diagram minus replaced fee & size should be the total
     190                 :             :         // fee & size of the old diagram minus replacement fee & size.
     191         [ +  - ]:        1184 :         assert((first_sum - replaced) == (second_sum - FeeFrac{replacement_fees, replacement_vsize}));
     192                 :        1184 :     }
     193                 :             : 
     194                 :             :     // If internals report error, wrapper should too
     195         [ +  - ]:        1638 :     auto err_tuple{ImprovesFeerateDiagram(pool, direct_conflicts, all_conflicts, replacement_fees, replacement_vsize)};
     196         [ +  + ]:        1638 :     if (!calc_results.has_value()) {
     197   [ +  -  +  - ]:         454 :          assert(err_tuple.value().first == DiagramCheckError::UNCALCULABLE);
     198                 :         454 :     } else {
     199                 :             :         // Diagram check succeeded
     200   [ +  -  +  -  :        1184 :         auto old_sum = std::accumulate(calc_results->first.begin(), calc_results->first.end(), FeeFrac{});
                   +  - ]
     201   [ +  -  +  -  :        1184 :         auto new_sum = std::accumulate(calc_results->second.begin(), calc_results->second.end(), FeeFrac{});
                   +  - ]
     202         [ +  + ]:        1184 :         if (!err_tuple.has_value()) {
     203                 :             :             // New diagram's final fee should always match or exceed old diagram's
     204         [ +  - ]:         478 :             assert(old_sum.fee <= new_sum.fee);
     205         [ +  + ]:        1184 :         } else if (old_sum.fee > new_sum.fee) {
     206                 :             :             // Or it failed, and if old diagram had higher fees, it should be a failure
     207   [ +  -  +  - ]:         398 :             assert(err_tuple.value().first == DiagramCheckError::FAILURE);
     208                 :         398 :         }
     209                 :        1184 :     }
     210                 :        1638 : }
        

Generated by: LCOV version 2.0-1