Skip to content

Commit 3db68e2

Browse files
committed
wallet: move ImportDescriptors/FuzzedWallet to util
1 parent d812cf1 commit 3db68e2

File tree

2 files changed

+137
-115
lines changed

2 files changed

+137
-115
lines changed

src/test/fuzz/util/wallet.h

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
// Copyright (c) 2024-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+
#ifndef BITCOIN_TEST_FUZZ_UTIL_WALLET_H
6+
#define BITCOIN_TEST_FUZZ_UTIL_WALLET_H
7+
8+
#include <test/fuzz/FuzzedDataProvider.h>
9+
#include <test/fuzz/fuzz.h>
10+
#include <test/fuzz/util.h>
11+
#include <policy/policy.h>
12+
#include <wallet/coincontrol.h>
13+
#include <wallet/fees.h>
14+
#include <wallet/spend.h>
15+
#include <wallet/test/util.h>
16+
#include <wallet/wallet.h>
17+
18+
namespace wallet {
19+
20+
/**
21+
* Wraps a descriptor wallet for fuzzing.
22+
*/
23+
struct FuzzedWallet {
24+
std::shared_ptr<CWallet> wallet;
25+
FuzzedWallet(interfaces::Chain& chain, const std::string& name, const std::string& seed_insecure)
26+
{
27+
wallet = std::make_shared<CWallet>(&chain, name, CreateMockableWalletDatabase());
28+
{
29+
LOCK(wallet->cs_wallet);
30+
wallet->SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
31+
auto height{*Assert(chain.getHeight())};
32+
wallet->SetLastBlockProcessed(height, chain.getBlockHash(height));
33+
}
34+
wallet->m_keypool_size = 1; // Avoid timeout in TopUp()
35+
assert(wallet->IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS));
36+
ImportDescriptors(seed_insecure);
37+
}
38+
void ImportDescriptors(const std::string& seed_insecure)
39+
{
40+
const std::vector<std::string> DESCS{
41+
"pkh(%s/%s/*)",
42+
"sh(wpkh(%s/%s/*))",
43+
"tr(%s/%s/*)",
44+
"wpkh(%s/%s/*)",
45+
};
46+
47+
for (const std::string& desc_fmt : DESCS) {
48+
for (bool internal : {true, false}) {
49+
const auto descriptor{(strprintf)(desc_fmt, "[5aa9973a/66h/4h/2h]" + seed_insecure, int{internal})};
50+
51+
FlatSigningProvider keys;
52+
std::string error;
53+
auto parsed_desc = std::move(Parse(descriptor, keys, error, /*require_checksum=*/false).at(0));
54+
assert(parsed_desc);
55+
assert(error.empty());
56+
assert(parsed_desc->IsRange());
57+
assert(parsed_desc->IsSingleType());
58+
assert(!keys.keys.empty());
59+
WalletDescriptor w_desc{std::move(parsed_desc), /*creation_time=*/0, /*range_start=*/0, /*range_end=*/1, /*next_index=*/0};
60+
assert(!wallet->GetDescriptorScriptPubKeyMan(w_desc));
61+
LOCK(wallet->cs_wallet);
62+
auto spk_manager{wallet->AddWalletDescriptor(w_desc, keys, /*label=*/"", internal)};
63+
assert(spk_manager);
64+
wallet->AddActiveScriptPubKeyMan(spk_manager->GetID(), *Assert(w_desc.descriptor->GetOutputType()), internal);
65+
}
66+
}
67+
}
68+
CTxDestination GetDestination(FuzzedDataProvider& fuzzed_data_provider)
69+
{
70+
auto type{fuzzed_data_provider.PickValueInArray(OUTPUT_TYPES)};
71+
if (fuzzed_data_provider.ConsumeBool()) {
72+
return *Assert(wallet->GetNewDestination(type, ""));
73+
} else {
74+
return *Assert(wallet->GetNewChangeDestination(type));
75+
}
76+
}
77+
CScript GetScriptPubKey(FuzzedDataProvider& fuzzed_data_provider) { return GetScriptForDestination(GetDestination(fuzzed_data_provider)); }
78+
void FundTx(FuzzedDataProvider& fuzzed_data_provider, CMutableTransaction tx)
79+
{
80+
// The fee of "tx" is 0, so this is the total input and output amount
81+
const CAmount total_amt{
82+
std::accumulate(tx.vout.begin(), tx.vout.end(), CAmount{}, [](CAmount t, const CTxOut& out) { return t + out.nValue; })};
83+
const uint32_t tx_size(GetVirtualTransactionSize(CTransaction{tx}));
84+
std::set<int> subtract_fee_from_outputs;
85+
if (fuzzed_data_provider.ConsumeBool()) {
86+
for (size_t i{}; i < tx.vout.size(); ++i) {
87+
if (fuzzed_data_provider.ConsumeBool()) {
88+
subtract_fee_from_outputs.insert(i);
89+
}
90+
}
91+
}
92+
std::vector<CRecipient> recipients;
93+
for (size_t idx = 0; idx < tx.vout.size(); idx++) {
94+
const CTxOut& tx_out = tx.vout[idx];
95+
CTxDestination dest;
96+
ExtractDestination(tx_out.scriptPubKey, dest);
97+
CRecipient recipient = {dest, tx_out.nValue, subtract_fee_from_outputs.count(idx) == 1};
98+
recipients.push_back(recipient);
99+
}
100+
CCoinControl coin_control;
101+
coin_control.m_allow_other_inputs = fuzzed_data_provider.ConsumeBool();
102+
CallOneOf(
103+
fuzzed_data_provider, [&] { coin_control.destChange = GetDestination(fuzzed_data_provider); },
104+
[&] { coin_control.m_change_type.emplace(fuzzed_data_provider.PickValueInArray(OUTPUT_TYPES)); },
105+
[&] { /* no op (leave uninitialized) */ });
106+
coin_control.fAllowWatchOnly = fuzzed_data_provider.ConsumeBool();
107+
coin_control.m_include_unsafe_inputs = fuzzed_data_provider.ConsumeBool();
108+
{
109+
auto& r{coin_control.m_signal_bip125_rbf};
110+
CallOneOf(
111+
fuzzed_data_provider, [&] { r = true; }, [&] { r = false; }, [&] { r = std::nullopt; });
112+
}
113+
coin_control.m_feerate = CFeeRate{
114+
// A fee of this range should cover all cases
115+
fuzzed_data_provider.ConsumeIntegralInRange<CAmount>(0, 2 * total_amt),
116+
tx_size,
117+
};
118+
if (fuzzed_data_provider.ConsumeBool()) {
119+
*coin_control.m_feerate += GetMinimumFeeRate(*wallet, coin_control, nullptr);
120+
}
121+
coin_control.fOverrideFeeRate = fuzzed_data_provider.ConsumeBool();
122+
// Add solving data (m_external_provider and SelectExternal)?
123+
124+
int change_position{fuzzed_data_provider.ConsumeIntegralInRange<int>(-1, tx.vout.size() - 1)};
125+
bilingual_str error;
126+
// Clear tx.vout since it is not meant to be used now that we are passing outputs directly.
127+
// This sets us up for a future PR to completely remove tx from the function signature in favor of passing inputs directly
128+
tx.vout.clear();
129+
(void)FundTransaction(*wallet, tx, recipients, change_position, /*lockUnspents=*/false, coin_control);
130+
}
131+
};
132+
}
133+
134+
#endif // BITCOIN_TEST_FUZZ_UTIL_WALLET_H

src/wallet/test/fuzz/notifications.cpp

Lines changed: 3 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include <test/fuzz/FuzzedDataProvider.h>
1919
#include <test/fuzz/fuzz.h>
2020
#include <test/fuzz/util.h>
21+
#include <test/fuzz/util/wallet.h>
2122
#include <test/util/setup_common.h>
2223
#include <tinyformat.h>
2324
#include <uint256.h>
@@ -53,121 +54,6 @@ void initialize_setup()
5354
g_setup = testing_setup.get();
5455
}
5556

56-
void ImportDescriptors(CWallet& wallet, const std::string& seed_insecure)
57-
{
58-
const std::vector<std::string> DESCS{
59-
"pkh(%s/%s/*)",
60-
"sh(wpkh(%s/%s/*))",
61-
"tr(%s/%s/*)",
62-
"wpkh(%s/%s/*)",
63-
};
64-
65-
for (const std::string& desc_fmt : DESCS) {
66-
for (bool internal : {true, false}) {
67-
const auto descriptor{(strprintf)(desc_fmt, "[5aa9973a/66h/4h/2h]" + seed_insecure, int{internal})};
68-
69-
FlatSigningProvider keys;
70-
std::string error;
71-
auto parsed_desc = std::move(Parse(descriptor, keys, error, /*require_checksum=*/false).at(0));
72-
assert(parsed_desc);
73-
assert(error.empty());
74-
assert(parsed_desc->IsRange());
75-
assert(parsed_desc->IsSingleType());
76-
assert(!keys.keys.empty());
77-
WalletDescriptor w_desc{std::move(parsed_desc), /*creation_time=*/0, /*range_start=*/0, /*range_end=*/1, /*next_index=*/0};
78-
assert(!wallet.GetDescriptorScriptPubKeyMan(w_desc));
79-
LOCK(wallet.cs_wallet);
80-
auto spk_manager{wallet.AddWalletDescriptor(w_desc, keys, /*label=*/"", internal)};
81-
assert(spk_manager);
82-
wallet.AddActiveScriptPubKeyMan(spk_manager->GetID(), *Assert(w_desc.descriptor->GetOutputType()), internal);
83-
}
84-
}
85-
}
86-
87-
/**
88-
* Wraps a descriptor wallet for fuzzing.
89-
*/
90-
struct FuzzedWallet {
91-
std::shared_ptr<CWallet> wallet;
92-
FuzzedWallet(const std::string& name, const std::string& seed_insecure)
93-
{
94-
auto& chain{*Assert(g_setup->m_node.chain)};
95-
wallet = std::make_shared<CWallet>(&chain, name, CreateMockableWalletDatabase());
96-
{
97-
LOCK(wallet->cs_wallet);
98-
wallet->SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
99-
auto height{*Assert(chain.getHeight())};
100-
wallet->SetLastBlockProcessed(height, chain.getBlockHash(height));
101-
}
102-
wallet->m_keypool_size = 1; // Avoid timeout in TopUp()
103-
assert(wallet->IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS));
104-
ImportDescriptors(*wallet, seed_insecure);
105-
}
106-
CTxDestination GetDestination(FuzzedDataProvider& fuzzed_data_provider)
107-
{
108-
auto type{fuzzed_data_provider.PickValueInArray(OUTPUT_TYPES)};
109-
if (fuzzed_data_provider.ConsumeBool()) {
110-
return *Assert(wallet->GetNewDestination(type, ""));
111-
} else {
112-
return *Assert(wallet->GetNewChangeDestination(type));
113-
}
114-
}
115-
CScript GetScriptPubKey(FuzzedDataProvider& fuzzed_data_provider) { return GetScriptForDestination(GetDestination(fuzzed_data_provider)); }
116-
void FundTx(FuzzedDataProvider& fuzzed_data_provider, CMutableTransaction tx)
117-
{
118-
// The fee of "tx" is 0, so this is the total input and output amount
119-
const CAmount total_amt{
120-
std::accumulate(tx.vout.begin(), tx.vout.end(), CAmount{}, [](CAmount t, const CTxOut& out) { return t + out.nValue; })};
121-
const uint32_t tx_size(GetVirtualTransactionSize(CTransaction{tx}));
122-
std::set<int> subtract_fee_from_outputs;
123-
if (fuzzed_data_provider.ConsumeBool()) {
124-
for (size_t i{}; i < tx.vout.size(); ++i) {
125-
if (fuzzed_data_provider.ConsumeBool()) {
126-
subtract_fee_from_outputs.insert(i);
127-
}
128-
}
129-
}
130-
std::vector<CRecipient> recipients;
131-
for (size_t idx = 0; idx < tx.vout.size(); idx++) {
132-
const CTxOut& tx_out = tx.vout[idx];
133-
CTxDestination dest;
134-
ExtractDestination(tx_out.scriptPubKey, dest);
135-
CRecipient recipient = {dest, tx_out.nValue, subtract_fee_from_outputs.count(idx) == 1};
136-
recipients.push_back(recipient);
137-
}
138-
CCoinControl coin_control;
139-
coin_control.m_allow_other_inputs = fuzzed_data_provider.ConsumeBool();
140-
CallOneOf(
141-
fuzzed_data_provider, [&] { coin_control.destChange = GetDestination(fuzzed_data_provider); },
142-
[&] { coin_control.m_change_type.emplace(fuzzed_data_provider.PickValueInArray(OUTPUT_TYPES)); },
143-
[&] { /* no op (leave uninitialized) */ });
144-
coin_control.fAllowWatchOnly = fuzzed_data_provider.ConsumeBool();
145-
coin_control.m_include_unsafe_inputs = fuzzed_data_provider.ConsumeBool();
146-
{
147-
auto& r{coin_control.m_signal_bip125_rbf};
148-
CallOneOf(
149-
fuzzed_data_provider, [&] { r = true; }, [&] { r = false; }, [&] { r = std::nullopt; });
150-
}
151-
coin_control.m_feerate = CFeeRate{
152-
// A fee of this range should cover all cases
153-
fuzzed_data_provider.ConsumeIntegralInRange<CAmount>(0, 2 * total_amt),
154-
tx_size,
155-
};
156-
if (fuzzed_data_provider.ConsumeBool()) {
157-
*coin_control.m_feerate += GetMinimumFeeRate(*wallet, coin_control, nullptr);
158-
}
159-
coin_control.fOverrideFeeRate = fuzzed_data_provider.ConsumeBool();
160-
// Add solving data (m_external_provider and SelectExternal)?
161-
162-
int change_position{fuzzed_data_provider.ConsumeIntegralInRange<int>(-1, tx.vout.size() - 1)};
163-
bilingual_str error;
164-
// Clear tx.vout since it is not meant to be used now that we are passing outputs directly.
165-
// This sets us up for a future PR to completely remove tx from the function signature in favor of passing inputs directly
166-
tx.vout.clear();
167-
(void)FundTransaction(*wallet, tx, recipients, change_position, /*lockUnspents=*/false, coin_control);
168-
}
169-
};
170-
17157
FUZZ_TARGET(wallet_notifications, .init = initialize_setup)
17258
{
17359
FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
@@ -176,10 +62,12 @@ FUZZ_TARGET(wallet_notifications, .init = initialize_setup)
17662
// total amount.
17763
const auto total_amount{ConsumeMoney(fuzzed_data_provider, /*max=*/MAX_MONEY / 100000)};
17864
FuzzedWallet a{
65+
*g_setup->m_node.chain,
17966
"fuzzed_wallet_a",
18067
"tprv8ZgxMBicQKsPd1QwsGgzfu2pcPYbBosZhJknqreRHgsWx32nNEhMjGQX2cgFL8n6wz9xdDYwLcs78N4nsCo32cxEX8RBtwGsEGgybLiQJfk",
18168
};
18269
FuzzedWallet b{
70+
*g_setup->m_node.chain,
18371
"fuzzed_wallet_b",
18472
"tprv8ZgxMBicQKsPfCunYTF18sEmEyjz8TfhGnZ3BoVAhkqLv7PLkQgmoG2Ecsp4JuqciWnkopuEwShit7st743fdmB9cMD4tznUkcs33vK51K9",
18573
};

0 commit comments

Comments
 (0)