Bitcoin Core 28.99.0
P2P Digital Currency
notifications.cpp
Go to the documentation of this file.
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>
14#include <script/descriptor.h>
15#include <script/script.h>
17#include <sync.h>
19#include <test/fuzz/fuzz.h>
20#include <test/fuzz/util.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
47namespace wallet {
48namespace {
49const TestingSetup* g_setup;
50
51void initialize_setup()
52{
53 static const auto testing_setup = MakeNoLogFileContext<const TestingSetup>();
54 g_setup = testing_setup.get();
55}
56
57FUZZ_TARGET(wallet_notifications, .init = initialize_setup)
58{
60 FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
61 // The total amount, to be distributed to the wallets a and b in txs
62 // without fee. Thus, the balance of the wallets should always equal the
63 // total amount.
64 const auto total_amount{ConsumeMoney(fuzzed_data_provider, /*max=*/MAX_MONEY / 100000)};
65 FuzzedWallet a{
66 *g_setup->m_node.chain,
67 "fuzzed_wallet_a",
68 "tprv8ZgxMBicQKsPd1QwsGgzfu2pcPYbBosZhJknqreRHgsWx32nNEhMjGQX2cgFL8n6wz9xdDYwLcs78N4nsCo32cxEX8RBtwGsEGgybLiQJfk",
69 };
70 FuzzedWallet b{
71 *g_setup->m_node.chain,
72 "fuzzed_wallet_b",
73 "tprv8ZgxMBicQKsPfCunYTF18sEmEyjz8TfhGnZ3BoVAhkqLv7PLkQgmoG2Ecsp4JuqciWnkopuEwShit7st743fdmB9cMD4tznUkcs33vK51K9",
74 };
75
76 // Keep track of all coins in this test.
77 // Each tuple in the chain represents the coins and the block created with
78 // those coins. Once the block is mined, the next tuple will have an empty
79 // block and the freshly mined coins.
80 using Coins = std::set<std::tuple<CAmount, COutPoint>>;
81 std::vector<std::tuple<Coins, CBlock>> chain;
82 {
83 // Add the initial entry
84 chain.emplace_back();
85 auto& [coins, block]{chain.back()};
86 coins.emplace(total_amount, COutPoint{Txid::FromUint256(uint256::ONE), 1});
87 }
88 LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 20)
89 {
91 fuzzed_data_provider,
92 [&] {
93 auto& [coins_orig, block]{chain.back()};
94 // Copy the coins for this block and consume all of them
95 Coins coins = coins_orig;
96 while (!coins.empty()) {
97 // Create a new tx
98 CMutableTransaction tx{};
99 // Add some coins as inputs to it
100 auto num_inputs{fuzzed_data_provider.ConsumeIntegralInRange<int>(1, coins.size())};
101 CAmount in{0};
102 while (num_inputs-- > 0) {
103 const auto& [coin_amt, coin_outpoint]{*coins.begin()};
104 in += coin_amt;
105 tx.vin.emplace_back(coin_outpoint);
106 coins.erase(coins.begin());
107 }
108 // Create some outputs spending all inputs, without fee
109 LIMITED_WHILE(in > 0 && fuzzed_data_provider.ConsumeBool(), 10)
110 {
111 const auto out_value{ConsumeMoney(fuzzed_data_provider, in)};
112 in -= out_value;
113 auto& wallet{fuzzed_data_provider.ConsumeBool() ? a : b};
114 tx.vout.emplace_back(out_value, wallet.GetScriptPubKey(fuzzed_data_provider));
115 }
116 // Spend the remaining input value, if any
117 auto& wallet{fuzzed_data_provider.ConsumeBool() ? a : b};
118 tx.vout.emplace_back(in, wallet.GetScriptPubKey(fuzzed_data_provider));
119 // Add tx to block
120 block.vtx.emplace_back(MakeTransactionRef(tx));
121 // Check that funding the tx doesn't crash the wallet
122 a.FundTx(fuzzed_data_provider, tx);
123 b.FundTx(fuzzed_data_provider, tx);
124 }
125 // Mine block
126 const uint256& hash = block.GetHash();
127 interfaces::BlockInfo info{hash};
128 info.prev_hash = &block.hashPrevBlock;
129 info.height = chain.size();
130 info.data = &block;
131 // Ensure that no blocks are skipped by the wallet by setting the chain's accumulated
132 // time to the maximum value. This ensures that the wallet's birth time is always
133 // earlier than this maximum time.
134 info.chain_time_max = std::numeric_limits<unsigned int>::max();
135 a.wallet->blockConnected(ChainstateRole::NORMAL, info);
136 b.wallet->blockConnected(ChainstateRole::NORMAL, info);
137 // Store the coins for the next block
138 Coins coins_new;
139 for (const auto& tx : block.vtx) {
140 uint32_t i{0};
141 for (const auto& out : tx->vout) {
142 coins_new.emplace(out.nValue, COutPoint{tx->GetHash(), i++});
143 }
144 }
145 chain.emplace_back(coins_new, CBlock{});
146 },
147 [&] {
148 if (chain.size() <= 1) return; // The first entry can't be removed
149 auto& [coins, block]{chain.back()};
150 if (block.vtx.empty()) return; // Can only disconnect if the block was submitted first
151 // Disconnect block
152 const uint256& hash = block.GetHash();
153 interfaces::BlockInfo info{hash};
154 info.prev_hash = &block.hashPrevBlock;
155 info.height = chain.size() - 1;
156 info.data = &block;
157 a.wallet->blockDisconnected(info);
158 b.wallet->blockDisconnected(info);
159 chain.pop_back();
160 });
161 auto& [coins, first_block]{chain.front()};
162 if (!first_block.vtx.empty()) {
163 // Only check balance when at least one block was submitted
164 const auto bal_a{GetBalance(*a.wallet).m_mine_trusted};
165 const auto bal_b{GetBalance(*b.wallet).m_mine_trusted};
166 assert(total_amount == bal_a + bal_b);
167 }
168 }
169}
170} // namespace
171} // namespace wallet
static constexpr CAmount MAX_MONEY
No amount larger than this (in satoshi) is valid.
Definition: amount.h:26
int64_t CAmount
Amount in satoshis (Can be negative)
Definition: amount.h:12
Definition: block.h:69
An outpoint - a combination of a transaction hash and an index n into its vout.
Definition: transaction.h:29
static constexpr unsigned int size()
Definition: uint256.h:110
static transaction_identifier FromUint256(const uint256 &id)
256-bit opaque blob.
Definition: uint256.h:190
static const uint256 ONE
Definition: uint256.h:199
#define LIMITED_WHILE(condition, limit)
Can be used to limit a theoretically unbounded loop.
Definition: fuzz.h:22
Balance GetBalance(const CWallet &wallet, const int min_depth, bool avoid_reuse)
Definition: receive.cpp:293
FUZZ_TARGET(coin_grinder)
static CTransactionRef MakeTransactionRef(Tx &&txIn)
Definition: transaction.h:424
node::NodeContext m_node
Definition: setup_common.h:65
Testing setup that configures a complete environment.
Definition: setup_common.h:120
Block data sent with blockConnected, blockDisconnected notifications.
Definition: chain.h:78
std::unique_ptr< interfaces::Chain > chain
Definition: context.h:76
CAmount m_mine_trusted
Trusted, at depth=GetBalance.min_depth or more.
Definition: receive.h:52
CAmount ConsumeMoney(FuzzedDataProvider &fuzzed_data_provider, const std::optional< CAmount > &max) noexcept
Definition: util.cpp:29
size_t CallOneOf(FuzzedDataProvider &fuzzed_data_provider, Callables... callables)
Definition: util.h:35
void SeedRandomStateForTest(SeedRand seedtype)
Seed the global RNG state for testing and log the seed value.
Definition: random.cpp:19
@ ZEROS
Seed with a compile time constant of zeros.
assert(!tx.IsCoinBase())