Branch data Line data Source code
1 : : // Copyright (c) 2021-present 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 <addresstype.h>
6 : : #include <consensus/amount.h>
7 : : #include <interfaces/chain.h>
8 : : #include <kernel/chain.h>
9 : : #include <outputtype.h>
10 : : #include <policy/feerate.h>
11 : : #include <policy/policy.h>
12 : : #include <primitives/block.h>
13 : : #include <primitives/transaction.h>
14 : : #include <script/descriptor.h>
15 : : #include <script/script.h>
16 : : #include <script/signingprovider.h>
17 : : #include <sync.h>
18 : : #include <test/fuzz/FuzzedDataProvider.h>
19 : : #include <test/fuzz/fuzz.h>
20 : : #include <test/fuzz/util.h>
21 : : #include <test/fuzz/util/wallet.h>
22 : : #include <test/util/setup_common.h>
23 : : #include <tinyformat.h>
24 : : #include <uint256.h>
25 : : #include <util/check.h>
26 : : #include <util/result.h>
27 : : #include <util/translation.h>
28 : : #include <wallet/coincontrol.h>
29 : : #include <wallet/context.h>
30 : : #include <wallet/fees.h>
31 : : #include <wallet/receive.h>
32 : : #include <wallet/spend.h>
33 : : #include <wallet/test/util.h>
34 : : #include <wallet/wallet.h>
35 : : #include <wallet/walletutil.h>
36 : :
37 : : #include <cstddef>
38 : : #include <cstdint>
39 : : #include <limits>
40 : : #include <numeric>
41 : : #include <set>
42 : : #include <string>
43 : : #include <tuple>
44 : : #include <utility>
45 : : #include <vector>
46 : :
47 : : namespace wallet {
48 : : namespace {
49 : : const TestingSetup* g_setup;
50 : :
51 : 1 : void initialize_setup()
52 : : {
53 [ + - + - ]: 2 : static const auto testing_setup = MakeNoLogFileContext<const TestingSetup>();
54 : 1 : g_setup = testing_setup.get();
55 [ + - ]: 2 : }
56 : :
57 [ + - ]: 1822 : FUZZ_TARGET(wallet_notifications, .init = initialize_setup)
58 : : {
59 : 1410 : FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
60 : : // The total amount, to be distributed to the wallets a and b in txs
61 : : // without fee. Thus, the balance of the wallets should always equal the
62 : : // total amount.
63 : 1410 : const auto total_amount{ConsumeMoney(fuzzed_data_provider, /*max=*/MAX_MONEY / 100000)};
64 : 1410 : FuzzedWallet a{
65 : 1410 : *g_setup->m_node.chain,
66 : : "fuzzed_wallet_a",
67 : : "tprv8ZgxMBicQKsPd1QwsGgzfu2pcPYbBosZhJknqreRHgsWx32nNEhMjGQX2cgFL8n6wz9xdDYwLcs78N4nsCo32cxEX8RBtwGsEGgybLiQJfk",
68 [ + - + - ]: 2820 : };
69 : 1410 : FuzzedWallet b{
70 [ + - ]: 1410 : *g_setup->m_node.chain,
71 : : "fuzzed_wallet_b",
72 : : "tprv8ZgxMBicQKsPfCunYTF18sEmEyjz8TfhGnZ3BoVAhkqLv7PLkQgmoG2Ecsp4JuqciWnkopuEwShit7st743fdmB9cMD4tznUkcs33vK51K9",
73 [ + - + - : 2820 : };
+ - ]
74 : :
75 : : // Keep track of all coins in this test.
76 : : // Each tuple in the chain represents the coins and the block created with
77 : : // those coins. Once the block is mined, the next tuple will have an empty
78 : : // block and the freshly mined coins.
79 : 1410 : using Coins = std::set<std::tuple<CAmount, COutPoint>>;
80 : 1410 : std::vector<std::tuple<Coins, CBlock>> chain;
81 : 1410 : {
82 : : // Add the initial entry
83 [ + - ]: 1410 : chain.emplace_back();
84 [ + - ]: 1410 : auto& [coins, block]{chain.back()};
85 [ + - ]: 1410 : coins.emplace(total_amount, COutPoint{Txid::FromUint256(uint256::ONE), 1});
86 : : }
87 [ + + + + ]: 22984 : LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 20)
88 : : {
89 [ + - ]: 21574 : CallOneOf(
90 : : fuzzed_data_provider,
91 : 8032 : [&] {
92 : 8032 : auto& [coins_orig, block]{chain.back()};
93 : : // Copy the coins for this block and consume all of them
94 : 8032 : Coins coins = coins_orig;
95 [ + + ]: 50352 : while (!coins.empty()) {
96 : : // Create a new tx
97 [ + - ]: 42320 : CMutableTransaction tx{};
98 : : // Add some coins as inputs to it
99 : 42320 : auto num_inputs{fuzzed_data_provider.ConsumeIntegralInRange<int>(1, coins.size())};
100 : 42320 : CAmount in{0};
101 [ + + ]: 163370 : while (num_inputs-- > 0) {
102 [ + - ]: 121050 : const auto& [coin_amt, coin_outpoint]{*coins.begin()};
103 : 121050 : in += coin_amt;
104 [ + - ]: 121050 : tx.vin.emplace_back(coin_outpoint);
105 : 121050 : coins.erase(coins.begin());
106 : : }
107 : : // Create some outputs spending all inputs, without fee
108 [ + + + + : 170006 : LIMITED_WHILE(in > 0 && fuzzed_data_provider.ConsumeBool(), 10)
+ + ]
109 : : {
110 : 127686 : const auto out_value{ConsumeMoney(fuzzed_data_provider, in)};
111 : 127686 : in -= out_value;
112 [ + + ]: 127686 : auto& wallet{fuzzed_data_provider.ConsumeBool() ? a : b};
113 [ + - + - ]: 255372 : tx.vout.emplace_back(out_value, wallet.GetScriptPubKey(fuzzed_data_provider));
114 : : }
115 : : // Spend the remaining input value, if any
116 [ + + ]: 42320 : auto& wallet{fuzzed_data_provider.ConsumeBool() ? a : b};
117 [ + - + - ]: 42320 : tx.vout.emplace_back(in, wallet.GetScriptPubKey(fuzzed_data_provider));
118 : : // Add tx to block
119 [ + - + - ]: 84640 : block.vtx.emplace_back(MakeTransactionRef(tx));
120 : : // Check that funding the tx doesn't crash the wallet
121 [ + - + - ]: 42320 : a.FundTx(fuzzed_data_provider, tx);
122 [ + - + - ]: 84640 : b.FundTx(fuzzed_data_provider, tx);
123 : 42320 : }
124 : : // Mine block
125 [ + - ]: 8032 : const uint256& hash = block.GetHash();
126 [ + - ]: 8032 : interfaces::BlockInfo info{hash};
127 : 8032 : info.prev_hash = &block.hashPrevBlock;
128 [ + - ]: 8032 : info.height = chain.size();
129 : 8032 : info.data = █
130 : : // Ensure that no blocks are skipped by the wallet by setting the chain's accumulated
131 : : // time to the maximum value. This ensures that the wallet's birth time is always
132 : : // earlier than this maximum time.
133 : 8032 : info.chain_time_max = std::numeric_limits<unsigned int>::max();
134 [ + - ]: 8032 : a.wallet->blockConnected(ChainstateRole::NORMAL, info);
135 [ + - ]: 8032 : b.wallet->blockConnected(ChainstateRole::NORMAL, info);
136 : : // Store the coins for the next block
137 : 8032 : Coins coins_new;
138 [ + + ]: 50352 : for (const auto& tx : block.vtx) {
139 : 42320 : uint32_t i{0};
140 [ + + ]: 212326 : for (const auto& out : tx->vout) {
141 [ + - ]: 170006 : coins_new.emplace(out.nValue, COutPoint{tx->GetHash(), i++});
142 : : }
143 : : }
144 [ + - ]: 16064 : chain.emplace_back(coins_new, CBlock{});
145 : 8032 : },
146 : 13542 : [&] {
147 [ + + ]: 13542 : if (chain.size() <= 1) return; // The first entry can't be removed
148 [ - + ]: 9900 : auto& [coins, block]{chain.back()};
149 [ - + ]: 9900 : if (block.vtx.empty()) return; // Can only disconnect if the block was submitted first
150 : : // Disconnect block
151 : 0 : const uint256& hash = block.GetHash();
152 : 0 : interfaces::BlockInfo info{hash};
153 : 0 : info.prev_hash = &block.hashPrevBlock;
154 : 0 : info.height = chain.size() - 1;
155 : 0 : info.data = █
156 : 0 : a.wallet->blockDisconnected(info);
157 : 0 : b.wallet->blockDisconnected(info);
158 : 0 : chain.pop_back();
159 : : });
160 [ + + ]: 21574 : auto& [coins, first_block]{chain.front()};
161 [ + + ]: 21574 : if (!first_block.vtx.empty()) {
162 : : // Only check balance when at least one block was submitted
163 [ + - ]: 17932 : const auto bal_a{GetBalance(*a.wallet).m_mine_trusted};
164 [ + - ]: 17932 : const auto bal_b{GetBalance(*b.wallet).m_mine_trusted};
165 [ - + ]: 17932 : assert(total_amount == bal_a + bal_b);
166 : : }
167 : : }
168 [ + - + - ]: 4230 : }
169 : : } // namespace
170 : : } // namespace wallet
|