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