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/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
48namespace wallet {
49namespace {
50const TestingSetup* g_setup;
51
52void initialize_setup()
53{
54 static const auto testing_setup = MakeNoLogFileContext<const TestingSetup>();
55 g_setup = testing_setup.get();
56}
57
58FUZZ_TARGET(wallet_notifications, .init = initialize_setup)
59{
61 FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
62 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 const auto total_amount{ConsumeMoney(fuzzed_data_provider, /*max=*/MAX_MONEY / 100000)};
67 FuzzedWallet a{
68 *g_setup->m_node.chain,
69 "fuzzed_wallet_a",
70 "tprv8ZgxMBicQKsPd1QwsGgzfu2pcPYbBosZhJknqreRHgsWx32nNEhMjGQX2cgFL8n6wz9xdDYwLcs78N4nsCo32cxEX8RBtwGsEGgybLiQJfk",
71 };
72 FuzzedWallet b{
73 *g_setup->m_node.chain,
74 "fuzzed_wallet_b",
75 "tprv8ZgxMBicQKsPfCunYTF18sEmEyjz8TfhGnZ3BoVAhkqLv7PLkQgmoG2Ecsp4JuqciWnkopuEwShit7st743fdmB9cMD4tznUkcs33vK51K9",
76 };
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 using Coins = std::set<std::tuple<CAmount, COutPoint>>;
83 std::vector<std::tuple<Coins, CBlock>> chain;
84 {
85 // Add the initial entry
86 chain.emplace_back();
87 auto& [coins, block]{chain.back()};
88 coins.emplace(total_amount, COutPoint{Txid::FromUint256(uint256::ONE), 1});
89 }
90 LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 20)
91 {
93 fuzzed_data_provider,
94 [&] {
95 auto& [coins_orig, block]{chain.back()};
96 // Copy the coins for this block and consume all of them
97 Coins coins = coins_orig;
98 while (!coins.empty()) {
99 // Create a new tx
100 CMutableTransaction tx{};
101 // Add some coins as inputs to it
102 auto num_inputs{fuzzed_data_provider.ConsumeIntegralInRange<int>(1, coins.size())};
103 CAmount in{0};
104 while (num_inputs-- > 0) {
105 const auto& [coin_amt, coin_outpoint]{*coins.begin()};
106 in += coin_amt;
107 tx.vin.emplace_back(coin_outpoint);
108 coins.erase(coins.begin());
109 }
110 // Create some outputs spending all inputs, without fee
111 LIMITED_WHILE(in > 0 && fuzzed_data_provider.ConsumeBool(), 10)
112 {
113 const auto out_value{ConsumeMoney(fuzzed_data_provider, in)};
114 in -= out_value;
115 auto& wallet{fuzzed_data_provider.ConsumeBool() ? a : b};
116 tx.vout.emplace_back(out_value, wallet.GetScriptPubKey(fuzzed_data_provider));
117 }
118 // Spend the remaining input value, if any
119 auto& wallet{fuzzed_data_provider.ConsumeBool() ? a : b};
120 tx.vout.emplace_back(in, wallet.GetScriptPubKey(fuzzed_data_provider));
121 // Add tx to block
122 block.vtx.emplace_back(MakeTransactionRef(tx));
123 // Check that funding the tx doesn't crash the wallet
124 a.FundTx(fuzzed_data_provider, tx);
125 b.FundTx(fuzzed_data_provider, tx);
126 }
127 // Mine block
128 const uint256& hash = block.GetHash();
129 interfaces::BlockInfo info{hash};
130 info.prev_hash = &block.hashPrevBlock;
131 info.height = chain.size();
132 info.data = &block;
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 info.chain_time_max = std::numeric_limits<unsigned int>::max();
137 a.wallet->blockConnected(ChainstateRole::NORMAL, info);
138 b.wallet->blockConnected(ChainstateRole::NORMAL, info);
139 // Store the coins for the next block
140 Coins coins_new;
141 for (const auto& tx : block.vtx) {
142 uint32_t i{0};
143 for (const auto& out : tx->vout) {
144 coins_new.emplace(out.nValue, COutPoint{tx->GetHash(), i++});
145 }
146 }
147 chain.emplace_back(coins_new, CBlock{});
148 },
149 [&] {
150 if (chain.size() <= 1) return; // The first entry can't be removed
151 auto& [coins, block]{chain.back()};
152 if (block.vtx.empty()) return; // Can only disconnect if the block was submitted first
153 // Disconnect block
154 const uint256& hash = block.GetHash();
155 interfaces::BlockInfo info{hash};
156 info.prev_hash = &block.hashPrevBlock;
157 info.height = chain.size() - 1;
158 info.data = &block;
159 a.wallet->blockDisconnected(info);
160 b.wallet->blockDisconnected(info);
161 chain.pop_back();
162 });
163 auto& [coins, first_block]{chain.front()};
164 if (!first_block.vtx.empty()) {
165 // Only check balance when at least one block was submitted
166 const auto bal_a{GetBalance(*a.wallet).m_mine_trusted};
167 const auto bal_b{GetBalance(*b.wallet).m_mine_trusted};
168 assert(total_amount == bal_a + bal_b);
169 }
170 }
171}
172} // namespace
173} // 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:121
static transaction_identifier FromUint256(const uint256 &id)
256-bit opaque blob.
Definition: uint256.h:201
static const uint256 ONE
Definition: uint256.h:210
#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:66
Testing setup that configures a complete environment.
Definition: setup_common.h:121
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
int64_t ConsumeTime(FuzzedDataProvider &fuzzed_data_provider, const std::optional< int64_t > &min, const std::optional< int64_t > &max) noexcept
Definition: util.cpp:34
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.
void SetMockTime(int64_t nMockTimeIn)
DEPRECATED Use SetMockTime with chrono type.
Definition: time.cpp:39
assert(!tx.IsCoinBase())