LCOV - code coverage report
Current view: top level - src/wallet/test/fuzz - spend.cpp (source / functions) Hit Total Coverage
Test: fuzz_coverage.info Lines: 166 174 95.4 %
Date: 2023-11-10 23:46:46 Functions: 24 34 70.6 %
Branches: 133 222 59.9 %

           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 <key_io.h>
       6                 :            : #include <outputtype.h>
       7                 :            : #include <script/script.h>
       8                 :            : #include <test/fuzz/FuzzedDataProvider.h>
       9                 :            : #include <test/fuzz/fuzz.h>
      10                 :            : #include <test/fuzz/util.h>
      11                 :            : #include <test/util/setup_common.h>
      12                 :            : #include <validation.h>
      13                 :            : #include <wallet/coincontrol.h>
      14                 :            : #include <wallet/spend.h>
      15                 :            : #include <wallet/test/util.h>
      16                 :            : #include <wallet/wallet.h>
      17         [ +  - ]:          2 : 
      18         [ +  - ]:          2 : namespace wallet {
      19                 :            : namespace {
      20                 :            : 
      21                 :            : TestChain100Setup* g_setup;
      22                 :            : 
      23                 :            : /**
      24                 :            :  * Singleton wallet class ensures that only one
      25                 :            :  * instance of CWallet is created and reused as required.
      26                 :            :  * This increases Fuzzing efficiency by reducing the expense
      27                 :          2 :  * of creating a new `Descriptor Wallet` each time and deletes
      28                 :            :  * the pointer safely using the destructor.
      29                 :            :  */
      30                 :            : class WalletSingleton
      31                 :            : {
      32                 :            : public:
      33                 :       2046 :     static WalletSingleton& GetInstance()
      34                 :            :     {
      35 [ +  - ][ +  + ]:       2048 :         static WalletSingleton instance;
         [ -  + ][ +  - ]
      36                 :       2046 :         return instance;
      37                 :          0 :     }
      38                 :            : 
      39                 :       1023 :     CWallet& GetWallet()
      40                 :            :     {
      41         [ +  - ]:       1023 :         if (!wallet) {
      42                 :          0 :             InitializeWallet();
      43                 :          0 :         }
      44                 :       1023 :         return *wallet;
      45                 :            :     }
      46                 :       1023 :     CoinsResult& GetCoins()
      47                 :            :     {
      48         [ +  - ]:       1023 :         if (!wallet) {
      49                 :          0 :             InitializeWallet();
      50                 :          0 :         }
      51                 :       1023 :         return available_coins;
      52                 :            :     }
      53         [ #  # ]:          0 : 
      54                 :            : private:
      55                 :          1 :     WalletSingleton()
      56                 :            :     {
      57         [ +  - ]:          1 :         InitializeWallet();
      58                 :          1 :     }
      59                 :            : 
      60                 :          1 :     ~WalletSingleton()
      61                 :            :     {
      62                 :          1 :         wallet.reset();
      63                 :          1 :         available_coins.coins.clear();
      64                 :          1 :     }
      65                 :            : 
      66                 :            :     WalletSingleton(const WalletSingleton&) = delete;
      67                 :            :     WalletSingleton& operator=(const WalletSingleton&) = delete;
      68                 :            : 
      69                 :          1 :     void InitializeWallet()
      70                 :            :     {
      71                 :          1 :         auto& node{g_setup->m_node};
      72         [ +  - ]:          2 :         wallet = CreateSyncedWallet(*node.chain, WITH_LOCK(Assert(node.chainman)->GetMutex(), return node.chainman->ActiveChain()), g_setup->coinbaseKey);
      73                 :            : 
      74                 :            :         // This is placed here because Available Coins will never be updated while fuzzing.
      75                 :          1 :         LOCK(wallet->cs_wallet);
      76         [ +  - ]:          1 :         available_coins = AvailableCoins(*wallet);
      77                 :          1 :     }
      78                 :            : 
      79                 :            :     std::unique_ptr<CWallet> wallet;
      80                 :            :     CoinsResult available_coins;
      81                 :            : };
      82                 :            : 
      83                 :        505 : CCoinControl GetNewCoinControl(FuzzedDataProvider& fuzzed_data_provider, CWallet& wallet, std::vector<COutput>& coins)
      84                 :            : {
      85                 :        505 :     CCoinControl new_coin_control;
      86 [ +  - ][ +  + ]:        505 :     if (fuzzed_data_provider.ConsumeBool()) {
      87                 :        329 :         const CTxDestination tx_destination{ConsumeTxDestination(fuzzed_data_provider)};
      88         [ +  - ]:        329 :         new_coin_control.destChange = tx_destination;
      89                 :        329 :     }
      90 [ +  - ][ +  + ]:        505 :     if (fuzzed_data_provider.ConsumeBool()) {
      91         [ +  - ]:        270 :         new_coin_control.m_change_type = fuzzed_data_provider.PickValueInArray(OUTPUT_TYPES);
      92                 :        268 :     }
      93         [ +  - ]:        505 :     new_coin_control.m_include_unsafe_inputs = fuzzed_data_provider.ConsumeBool();
      94         [ +  - ]:        505 :     new_coin_control.m_allow_other_inputs = fuzzed_data_provider.ConsumeBool();
      95 [ +  - ][ +  + ]:        505 :     if (fuzzed_data_provider.ConsumeBool()) {
      96         [ +  - ]:        387 :         new_coin_control.m_feerate = CFeeRate{ConsumeMoney(fuzzed_data_provider, /*max=*/COIN)};
      97                 :        387 :     }
      98         [ +  - ]:        505 :     new_coin_control.m_signal_bip125_rbf = fuzzed_data_provider.ConsumeBool();
      99         [ +  - ]:        507 :     new_coin_control.m_avoid_partial_spends = fuzzed_data_provider.ConsumeBool();
     100                 :            : 
     101                 :            :     // Taking a random sub-sequence from available coins.
     102         [ +  + ]:      25755 :     for (const COutput& coin : coins) {
     103 [ +  - ][ +  + ]:      25250 :         if (fuzzed_data_provider.ConsumeBool()) {
     104         [ +  - ]:      10434 :             new_coin_control.Select(coin.outpoint);
     105                 :      10434 :         }
     106                 :            :     }
     107                 :            : 
     108                 :            :     // Occasionally, some random `CCoutPoint` are also selected.
     109 [ +  - ][ +  + ]:        505 :     if (fuzzed_data_provider.ConsumeBool()) {
     110         [ +  + ]:        770 :         for (int i = 0; i < 10; ++i) {
     111                 :        700 :             std::optional<COutPoint> optional_out_point = ConsumeDeserializable<COutPoint>(fuzzed_data_provider);
     112         [ +  + ]:        700 :             if (!optional_out_point) {
     113                 :        519 :                 continue;
     114                 :            :             }
     115         [ +  - ]:        181 :             new_coin_control.Select(*optional_out_point);
     116                 :        181 :         }
     117                 :         70 :     }
     118                 :        505 :     return new_coin_control;
     119         [ +  - ]:        505 : }
     120                 :            : 
     121                 :          1 : void initialize_spend()
     122                 :            : {
     123 [ +  - ][ -  + ]:          1 :     static auto testing_setup = MakeNoLogFileContext<TestChain100Setup>();
                 [ +  - ]
     124                 :          1 :     g_setup = testing_setup.get();
     125                 :            : 
     126                 :            :     // Add 50 spendable UTXO, 50 BTC each, to the wallet (total balance 5000 BTC)
     127                 :            :     // Because none of the UTXO will ever be used (marked as spent), mining this many should be sufficient.
     128         [ +  + ]:         51 :     for (int i = 0; i < 50; ++i) {
     129 [ +  - ][ +  - ]:         50 :         g_setup->CreateAndProcessBlock({}, GetScriptForRawPubKey(g_setup->coinbaseKey.GetPubKey()));
                 [ +  - ]
     130                 :         50 :     }
     131                 :          1 : }
     132                 :            : 
     133         [ +  - ]:       1027 : FUZZ_TARGET(spend, .init = initialize_spend)
     134                 :            : {
     135                 :       1023 :     FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
     136                 :       1023 :     CWallet& wallet = WalletSingleton::GetInstance().GetWallet();
     137                 :       1023 :     CoinsResult& available_coins = WalletSingleton::GetInstance().GetCoins();
     138                 :            : 
     139                 :            :     // Asserting if the `wallet` has no funds.
     140                 :       1023 :     std::vector<COutput> coins = available_coins.All();
     141         [ +  - ]:       1023 :     assert(!coins.empty());
     142                 :            : 
     143         [ +  - ]:       1023 :     CCoinControl coin_control;
     144         [ -  + ]:       1023 :     CMutableTransaction mtx;
     145                 :            : 
     146 [ +  - ][ +  + ]:      29793 :     LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 505)
                 [ +  + ]
     147                 :            :     {
     148         [ +  - ]:      28770 :         CallOneOf(
     149                 :            :             fuzzed_data_provider,
     150                 :      29275 :             [&] {
     151                 :            :                 // Creates a new `CoinControl`.
     152                 :        505 :                 coin_control = GetNewCoinControl(fuzzed_data_provider, wallet, coins);
     153                 :        505 :             },
     154                 :      31448 :             [&] {
     155                 :            :                 // Creates a new Transaction using `CreateTransaction`.
     156                 :       2678 :                 std::vector<CRecipient> recipients;
     157         [ +  - ]:       2678 :                 unsigned int recipients_size = fuzzed_data_provider.ConsumeIntegralInRange<unsigned int>(0, 100);
     158         [ +  + ]:      16560 :                 for (unsigned int i = 0; i < recipients_size; ++i) {
     159         [ +  - ]:      27764 :                     recipients.push_back({/*dest=*/ConsumeTxDestination(fuzzed_data_provider),
     160                 :      13882 :                                           /*nAmount=*/ConsumeMoney(fuzzed_data_provider),
     161         [ +  - ]:      13882 :                                           /*fSubtractFeeFromAmount=*/fuzzed_data_provider.ConsumeBool()});
     162                 :      13882 :                 }
     163         [ +  - ]:          2 :                 // Equally likely to choose one of the three values.
     164 [ +  - ][ +  - ]:       2680 :                 std::vector<int> random_positions = {-1, fuzzed_data_provider.ConsumeIntegralInRange<int>(0, recipients_size), fuzzed_data_provider.ConsumeIntegral<int>()};
         [ +  - ][ +  - ]
     165 [ +  - ][ +  - ]:       2680 :                 int change_pos = PickValue(fuzzed_data_provider, random_positions);
     166         [ +  - ]:          2 : 
     167 [ +  - ][ +  - ]:       2680 :                 auto res = CreateTransaction(wallet, recipients, change_pos, coin_control, /*sign=*/fuzzed_data_provider.ConsumeBool());
                 [ +  - ]
     168 [ +  - ][ +  + ]:       2680 :                 if (!res) return;
     169 [ +  - ][ +  - ]:        427 :                 mtx = CMutableTransaction(*(res->tx));
                 [ +  - ]
     170 [ +  - ][ -  + ]:       2680 :             },
     171                 :      29559 :             [&] {
     172                 :            :                 // Occasionally, creating a random Transaction for Fuzzing.
     173                 :        789 :                 auto new_mtx = ConsumeDeserializable<CMutableTransaction>(fuzzed_data_provider);
     174         [ +  + ]:        789 :                 if (!new_mtx) return;
     175         [ +  - ]:        391 :                 mtx = *new_mtx;
     176         [ -  + ]:        789 :             },
     177                 :      31298 :             [&] {
     178                 :       2528 :                 LOCK(wallet.cs_wallet);
     179 [ +  - ][ +  + ]:       2528 :                 if (fuzzed_data_provider.ConsumeBool()) {
     180 [ +  - ][ +  - ]:       2406 :                     (void)CalculateMaximumSignedTxSize(CTransaction(mtx), &wallet);
     181                 :       2406 :                 } else {
     182 [ +  - ][ +  - ]:        122 :                     (void)CalculateMaximumSignedTxSize(CTransaction(mtx), &wallet, &coin_control);
     183                 :            :                 }
     184                 :       2528 :             },
     185                 :      28909 :             [&] {
     186         [ +  - ]:        139 :                 const CTxOut tx_out{ConsumeMoney(fuzzed_data_provider), ConsumeScript(fuzzed_data_provider)};
     187         [ -  + ]:        139 :                 CalculateMaximumSignedInputSize(tx_out, &wallet, &coin_control);
     188                 :        139 :             },
     189                 :      50901 :             [&] {
     190                 :            :                 CAmount fee;
     191         [ +  - ]:      22131 :                 std::vector<int> random_positions = {-1, fuzzed_data_provider.ConsumeIntegralInRange<int>(0, mtx.vout.size()), fuzzed_data_provider.ConsumeIntegral<int>()};
     192         [ +  - ]:      22131 :                 int change_pos = PickValue(fuzzed_data_provider, random_positions);
     193                 :      22131 :                 bilingual_str error;
     194                 :      22131 :                 std::set<int> setSubtractFeeFromOutputs;
     195         [ +  + ]:      58154 :                 for (size_t i = 0; i < mtx.vout.size(); ++i) {
     196 [ +  - ][ +  + ]:      36023 :                     if (fuzzed_data_provider.ConsumeBool()) {
     197         [ +  - ]:      34795 :                         setSubtractFeeFromOutputs.insert(i);
     198                 :      34795 :                     }
     199                 :      36023 :                 }
     200 [ +  - ][ -  + ]:      22131 :                 FundTransaction(wallet, mtx, fee, change_pos, error, false, setSubtractFeeFromOutputs, coin_control);
     201                 :      22131 :             });
     202                 :      28770 :     }
     203                 :            : 
     204                 :            :     // Plays with the `CoinsResult`
     205                 :       1023 :     CoinsResult wallet_coins;
     206                 :            : 
     207 [ +  - ][ +  + ]:       3607 :     LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 50)
                 [ +  + ]
     208                 :            :     {
     209         [ +  - ]:       2584 :         CallOneOf(
     210                 :            :             fuzzed_data_provider,
     211                 :       3202 :             [&] {
     212                 :            :                 // Re-initializing the wallet_coins.
     213                 :        618 :                 wallet_coins = available_coins;
     214                 :        618 :             },
     215                 :       2936 :             [&] {
     216                 :        352 :                 std::optional<COutPoint> optional_out_point = ConsumeDeserializable<COutPoint>(fuzzed_data_provider);
     217         [ +  + ]:        352 :                 if (!optional_out_point) {
     218                 :        137 :                     return;
     219                 :            :                 }
     220         [ +  - ]:        430 :                 COutput out_put = COutput{/*outpoint=*/*optional_out_point,
     221         [ +  - ]:        215 :                                           /*txout=*/CTxOut{ConsumeMoney(fuzzed_data_provider), ConsumeScript(fuzzed_data_provider)},
     222                 :        215 :                                           /*depth=*/fuzzed_data_provider.ConsumeIntegral<int>(),
     223                 :        215 :                                           /*input_bytes=*/fuzzed_data_provider.ConsumeIntegral<int>(),
     224         [ +  - ]:        215 :                                           /*spendable=*/fuzzed_data_provider.ConsumeBool(),
     225         [ +  - ]:        215 :                                           /*solvable=*/fuzzed_data_provider.ConsumeBool(),
     226         [ +  - ]:        215 :                                           /*safe=*/fuzzed_data_provider.ConsumeBool(),
     227         [ +  - ]:        215 :                                           /*time=*/fuzzed_data_provider.ConsumeIntegral<int64_t>(),
     228         [ +  - ]:        215 :                                           /*from_me=*/fuzzed_data_provider.ConsumeBool()};
     229                 :        215 :                 OutputType type = fuzzed_data_provider.PickValueInArray(OUTPUT_TYPES);
     230         [ -  + ]:        215 :                 wallet_coins.Add(type, out_put);
     231                 :        352 :             },
     232                 :       3628 :             [&] {
     233                 :       1044 :                 std::unordered_set<COutPoint, SaltedOutpointHasher> outs_to_remove;
     234         [ +  - ]:       1044 :                 std::vector<COutput> coins = wallet_coins.All();
     235                 :            :                 // Taking a random sub-sequence from available coins
     236         [ +  + ]:       5565 :                 for (const COutput& coin : coins) {
     237 [ +  - ][ +  + ]:       4521 :                     if (fuzzed_data_provider.ConsumeBool()) {
     238         [ +  - ]:       2880 :                         outs_to_remove.emplace(coin.outpoint);
     239                 :       2880 :                     }
     240                 :            :                 }
     241         [ +  - ]:       1044 :                 wallet_coins.Erase(outs_to_remove);
     242                 :       1044 :             },
     243                 :       2840 :             [&] {
     244                 :        256 :                 FastRandomContext random_context{ConsumeUInt256(fuzzed_data_provider)};
     245         [ +  - ]:        256 :                 wallet_coins.Shuffle(random_context);
     246                 :        256 :             },
     247                 :       2674 :             [&] {
     248                 :         90 :                 wallet_coins.Clear();
     249                 :         90 :             },
     250                 :       2669 :             [&] {
     251                 :         85 :                 (void)wallet_coins.GetTotalAmount();
     252                 :         85 :             },
     253                 :       2723 :             [&] {
     254                 :        139 :                 (void)wallet_coins.GetEffectiveTotalAmount();
     255                 :        139 :             });
     256                 :       2584 :     }
     257                 :       1023 : }
     258                 :            : } // namespace
     259                 :            : } // namespace wallet

Generated by: LCOV version 1.14