Bitcoin Core 28.0.0
P2P Digital Currency
Loading...
Searching...
No Matches
scriptpubkeyman.cpp
Go to the documentation of this file.
1// Copyright (c) 2023-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 <chainparams.h>
7#include <coins.h>
8#include <key.h>
10#include <psbt.h>
11#include <script/descriptor.h>
12#include <script/interpreter.h>
13#include <script/script.h>
15#include <sync.h>
17#include <test/fuzz/fuzz.h>
18#include <test/fuzz/util.h>
21#include <util/check.h>
22#include <util/translation.h>
23#include <validation.h>
25#include <wallet/test/util.h>
26#include <wallet/types.h>
27#include <wallet/wallet.h>
28#include <wallet/walletutil.h>
29
30#include <map>
31#include <memory>
32#include <optional>
33#include <string>
34#include <utility>
35#include <variant>
36
37namespace wallet {
38namespace {
39const TestingSetup* g_setup;
40
43
44void initialize_spkm()
45{
46 static const auto testing_setup{MakeNoLogFileContext<const TestingSetup>()};
47 g_setup = testing_setup.get();
50}
51
57static bool TooDeepDerivPath(std::string_view desc)
58{
59 const FuzzBufferType desc_buf{reinterpret_cast<const unsigned char *>(desc.data()), desc.size()};
60 return HasDeepDerivPath(desc_buf);
61}
62
63static std::optional<std::pair<WalletDescriptor, FlatSigningProvider>> CreateWalletDescriptor(FuzzedDataProvider& fuzzed_data_provider)
64{
65 const std::string mocked_descriptor{fuzzed_data_provider.ConsumeRandomLengthString()};
66 if (TooDeepDerivPath(mocked_descriptor)) return {};
67 const auto desc_str{MOCKED_DESC_CONVERTER.GetDescriptor(mocked_descriptor)};
68 if (!desc_str.has_value()) return std::nullopt;
69
71 std::string error;
72 std::unique_ptr<Descriptor> parsed_desc{Parse(desc_str.value(), keys, error, false)};
73 if (!parsed_desc) return std::nullopt;
74
75 WalletDescriptor w_desc{std::move(parsed_desc), /*creation_time=*/0, /*range_start=*/0, /*range_end=*/1, /*next_index=*/1};
76 return std::make_pair(w_desc, keys);
77}
78
79static DescriptorScriptPubKeyMan* CreateDescriptor(WalletDescriptor& wallet_desc, FlatSigningProvider& keys, CWallet& keystore)
80{
81 LOCK(keystore.cs_wallet);
82 keystore.AddWalletDescriptor(wallet_desc, keys, /*label=*/"", /*internal=*/false);
83 return keystore.GetDescriptorScriptPubKeyMan(wallet_desc);
84};
85
86FUZZ_TARGET(scriptpubkeyman, .init = initialize_spkm)
87{
88 FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
89 const auto& node{g_setup->m_node};
90 Chainstate& chainstate{node.chainman->ActiveChainstate()};
91 std::unique_ptr<CWallet> wallet_ptr{std::make_unique<CWallet>(node.chain.get(), "", CreateMockableWalletDatabase())};
92 CWallet& wallet{*wallet_ptr};
93 {
94 LOCK(wallet.cs_wallet);
95 wallet.SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
96 wallet.SetLastBlockProcessed(chainstate.m_chain.Height(), chainstate.m_chain.Tip()->GetBlockHash());
97 wallet.m_keypool_size = 1;
98 }
99
100 auto wallet_desc{CreateWalletDescriptor(fuzzed_data_provider)};
101 if (!wallet_desc.has_value()) return;
102 auto spk_manager{CreateDescriptor(wallet_desc->first, wallet_desc->second, wallet)};
103 if (spk_manager == nullptr) return;
104
105 if (fuzzed_data_provider.ConsumeBool()) {
106 auto wallet_desc{CreateWalletDescriptor(fuzzed_data_provider)};
107 if (!wallet_desc.has_value()) {
108 return;
109 }
110 std::string error;
111 if (spk_manager->CanUpdateToWalletDescriptor(wallet_desc->first, error)) {
112 auto new_spk_manager{CreateDescriptor(wallet_desc->first, wallet_desc->second, wallet)};
113 if (new_spk_manager != nullptr) spk_manager = new_spk_manager;
114 }
115 }
116
117 bool good_data{true};
118 LIMITED_WHILE(good_data && fuzzed_data_provider.ConsumeBool(), 20) {
119 CallOneOf(
120 fuzzed_data_provider,
121 [&] {
122 const CScript script{ConsumeScript(fuzzed_data_provider)};
123 auto is_mine{spk_manager->IsMine(script)};
124 if (is_mine == isminetype::ISMINE_SPENDABLE) {
125 assert(spk_manager->GetScriptPubKeys().count(script));
126 }
127 },
128 [&] {
129 auto spks{spk_manager->GetScriptPubKeys()};
130 for (const CScript& spk : spks) {
131 assert(spk_manager->IsMine(spk) == ISMINE_SPENDABLE);
132 CTxDestination dest;
133 bool extract_dest{ExtractDestination(spk, dest)};
134 if (extract_dest) {
135 const std::string msg{fuzzed_data_provider.ConsumeRandomLengthString()};
136 PKHash pk_hash{std::get_if<PKHash>(&dest) && fuzzed_data_provider.ConsumeBool() ?
137 *std::get_if<PKHash>(&dest) :
138 PKHash{ConsumeUInt160(fuzzed_data_provider)}};
139 std::string str_sig;
140 (void)spk_manager->SignMessage(msg, pk_hash, str_sig);
141 (void)spk_manager->GetMetadata(dest);
142 }
143 }
144 },
145 [&] {
146 auto spks{spk_manager->GetScriptPubKeys()};
147 if (!spks.empty()) {
148 auto& spk{PickValue(fuzzed_data_provider, spks)};
149 (void)spk_manager->MarkUnusedAddresses(spk);
150 }
151 },
152 [&] {
153 LOCK(spk_manager->cs_desc_man);
154 auto wallet_desc{spk_manager->GetWalletDescriptor()};
155 if (wallet_desc.descriptor->IsSingleType()) {
156 auto output_type{wallet_desc.descriptor->GetOutputType()};
157 if (output_type.has_value()) {
158 auto dest{spk_manager->GetNewDestination(*output_type)};
159 if (dest) {
161 assert(spk_manager->IsHDEnabled());
162 }
163 }
164 }
165 },
166 [&] {
168 const std::optional<CMutableTransaction> opt_tx_to{ConsumeDeserializable<CMutableTransaction>(fuzzed_data_provider, TX_WITH_WITNESS)};
169 if (!opt_tx_to) {
170 good_data = false;
171 return;
172 }
173 tx_to = *opt_tx_to;
174
175 std::map<COutPoint, Coin> coins{ConsumeCoins(fuzzed_data_provider)};
176 const int sighash{fuzzed_data_provider.ConsumeIntegral<int>()};
177 std::map<int, bilingual_str> input_errors;
178 (void)spk_manager->SignTransaction(tx_to, coins, sighash, input_errors);
179 },
180 [&] {
181 std::optional<PartiallySignedTransaction> opt_psbt{ConsumeDeserializable<PartiallySignedTransaction>(fuzzed_data_provider)};
182 if (!opt_psbt) {
183 good_data = false;
184 return;
185 }
186 auto psbt{*opt_psbt};
188 const int sighash_type{fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 150)};
189 (void)spk_manager->FillPSBT(psbt, txdata, sighash_type, fuzzed_data_provider.ConsumeBool(), fuzzed_data_provider.ConsumeBool(), nullptr, fuzzed_data_provider.ConsumeBool());
190 }
191 );
192 }
193
194 std::string descriptor;
195 (void)spk_manager->GetDescriptorString(descriptor, /*priv=*/fuzzed_data_provider.ConsumeBool());
196 (void)spk_manager->GetEndRange();
197 (void)spk_manager->GetKeyPoolSize();
198}
199
200} // namespace
201} // namespace wallet
bool ExtractDestination(const CScript &scriptPubKey, CTxDestination &addressRet)
Parse a scriptPubKey for the destination.
bool IsValidDestination(const CTxDestination &dest)
Check whether a CTxDestination corresponds to one with an address.
std::variant< CNoDestination, PubKeyDestination, PKHash, ScriptHash, WitnessV0ScriptHash, WitnessV0KeyHash, WitnessV1Taproot, PayToAnchor, WitnessUnknown > CTxDestination
A txout script categorized into standard templates.
void SelectParams(const ChainType chain)
Sets the params returned by Params() to those for the given chain type.
Serialized script, used inside transaction inputs and outputs.
Definition script.h:414
Chainstate stores and provides an API to update our local knowledge of the current best chain.
Definition validation.h:513
std::string ConsumeRandomLengthString(size_t max_length)
T ConsumeIntegralInRange(T min, T max)
Converts a mocked descriptor string to a valid one.
Definition descriptor.h:21
void Init()
When initializing the target, populate the list of keys.
std::optional< std::string > GetDescriptor(std::string_view mocked_desc) const
Get an actual descriptor string from a descriptor string whose keys were mocked.
static UniValue Parse(std::string_view raw)
Parse string to UniValue or throw runtime_error if string contains invalid JSON.
Definition client.cpp:321
MockedDescriptorConverter MOCKED_DESC_CONVERTER
The converter of mocked descriptors, needs to be initialized when the target is.
#define FUZZ_TARGET(...)
Definition fuzz.h:35
std::span< const uint8_t > FuzzBufferType
Definition fuzz.h:25
#define LIMITED_WHILE(condition, limit)
Can be used to limit a theoretically unbounded loop.
Definition fuzz.h:22
wallet::ScriptPubKeyMan * CreateDescriptor(CWallet &keystore, const std::string &desc_str, const bool success)
@ ISMINE_SPENDABLE
Definition types.h:44
static constexpr TransactionSerParams TX_WITH_WITNESS
PrecomputedTransactionData PrecomputePSBTData(const PartiallySignedTransaction &psbt)
Compute a PrecomputedTransactionData object from a psbt.
Definition psbt.cpp:358
std::unique_ptr< T > MakeNoLogFileContext(const ChainType chain_type=ChainType::REGTEST, TestOpts opts={})
Make a test setup that has disk access to the debug.log file disabled.
node::NodeContext m_node
A mutable version of CTransaction.
Testing setup that configures a complete environment.
#define LOCK(cs)
Definition sync.h:257
bool HasDeepDerivPath(const FuzzBufferType &buff, const int max_depth)
Whether the buffer, if it represents a valid descriptor, contains a derivation path deeper than a giv...
CScript ConsumeScript(FuzzedDataProvider &fuzzed_data_provider, const bool maybe_p2wsh) noexcept
Definition util.cpp:93
std::map< COutPoint, Coin > ConsumeCoins(FuzzedDataProvider &fuzzed_data_provider) noexcept
Definition util.cpp:166
auto & PickValue(FuzzedDataProvider &fuzzed_data_provider, Collection &col)
Definition util.h:47
size_t CallOneOf(FuzzedDataProvider &fuzzed_data_provider, Callables... callables)
Definition util.h:35
std::optional< T > ConsumeDeserializable(FuzzedDataProvider &fuzzed_data_provider, const P &params, const std::optional< size_t > &max_length=std::nullopt) noexcept
Definition util.h:103
uint160 ConsumeUInt160(FuzzedDataProvider &fuzzed_data_provider) noexcept
Definition util.h:160
assert(!tx.IsCoinBase())
is a home for public enum and struct type definitions that are used by internally by wallet code,...