Coverage Report

Created: 2025-02-21 14:37

/root/bitcoin/src/wallet/test/fuzz/scriptpubkeyman.cpp
Line
Count
Source (jump to first uncovered line)
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>
9
#include <primitives/transaction.h>
10
#include <psbt.h>
11
#include <script/descriptor.h>
12
#include <script/interpreter.h>
13
#include <script/script.h>
14
#include <script/signingprovider.h>
15
#include <sync.h>
16
#include <test/fuzz/FuzzedDataProvider.h>
17
#include <test/fuzz/fuzz.h>
18
#include <test/fuzz/util.h>
19
#include <test/fuzz/util/descriptor.h>
20
#include <test/util/setup_common.h>
21
#include <util/check.h>
22
#include <util/time.h>
23
#include <util/translation.h>
24
#include <validation.h>
25
#include <wallet/scriptpubkeyman.h>
26
#include <wallet/test/util.h>
27
#include <wallet/types.h>
28
#include <wallet/wallet.h>
29
#include <wallet/walletutil.h>
30
31
#include <map>
32
#include <memory>
33
#include <optional>
34
#include <string>
35
#include <utility>
36
#include <variant>
37
38
namespace wallet {
39
namespace {
40
const TestingSetup* g_setup;
41
42
//! The converter of mocked descriptors, needs to be initialized when the target is.
43
MockedDescriptorConverter MOCKED_DESC_CONVERTER;
44
45
void initialize_spkm()
46
0
{
47
0
    static const auto testing_setup{MakeNoLogFileContext<const TestingSetup>()};
48
0
    g_setup = testing_setup.get();
49
0
    SelectParams(ChainType::MAIN);
50
0
    MOCKED_DESC_CONVERTER.Init();
51
0
}
52
53
/**
54
 * Key derivation is expensive. Deriving deep derivation paths take a lot of compute and we'd rather spend time
55
 * elsewhere in this target, like on actually fuzzing the DescriptorScriptPubKeyMan. So rule out strings which could
56
 * correspond to a descriptor containing a too large derivation path.
57
 */
58
static bool TooDeepDerivPath(std::string_view desc)
59
0
{
60
0
    const FuzzBufferType desc_buf{reinterpret_cast<const unsigned char *>(desc.data()), desc.size()};
61
0
    return HasDeepDerivPath(desc_buf);
62
0
}
63
64
static std::optional<std::pair<WalletDescriptor, FlatSigningProvider>> CreateWalletDescriptor(FuzzedDataProvider& fuzzed_data_provider)
65
0
{
66
0
    const std::string mocked_descriptor{fuzzed_data_provider.ConsumeRandomLengthString()};
67
0
    if (TooDeepDerivPath(mocked_descriptor)) return {};
68
0
    const auto desc_str{MOCKED_DESC_CONVERTER.GetDescriptor(mocked_descriptor)};
69
0
    if (!desc_str.has_value()) return std::nullopt;
70
71
0
    FlatSigningProvider keys;
72
0
    std::string error;
73
0
    std::vector<std::unique_ptr<Descriptor>> parsed_descs = Parse(desc_str.value(), keys, error, false);
74
0
    if (parsed_descs.empty()) return std::nullopt;
75
76
0
    WalletDescriptor w_desc{std::move(parsed_descs.at(0)), /*creation_time=*/0, /*range_start=*/0, /*range_end=*/1, /*next_index=*/1};
77
0
    return std::make_pair(w_desc, keys);
78
0
}
79
80
static DescriptorScriptPubKeyMan* CreateDescriptor(WalletDescriptor& wallet_desc, FlatSigningProvider& keys, CWallet& keystore)
81
0
{
82
0
    LOCK(keystore.cs_wallet);
83
0
    keystore.AddWalletDescriptor(wallet_desc, keys, /*label=*/"", /*internal=*/false);
84
0
    return keystore.GetDescriptorScriptPubKeyMan(wallet_desc);
85
0
};
86
87
FUZZ_TARGET(scriptpubkeyman, .init = initialize_spkm)
88
0
{
89
0
    SeedRandomStateForTest(SeedRand::ZEROS);
90
0
    FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
91
0
    SetMockTime(ConsumeTime(fuzzed_data_provider));
92
0
    const auto& node{g_setup->m_node};
93
0
    Chainstate& chainstate{node.chainman->ActiveChainstate()};
94
0
    std::unique_ptr<CWallet> wallet_ptr{std::make_unique<CWallet>(node.chain.get(), "", CreateMockableWalletDatabase())};
95
0
    CWallet& wallet{*wallet_ptr};
96
0
    {
97
0
        LOCK(wallet.cs_wallet);
98
0
        wallet.SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
99
0
        wallet.SetLastBlockProcessed(chainstate.m_chain.Height(), chainstate.m_chain.Tip()->GetBlockHash());
100
0
        wallet.m_keypool_size = 1;
101
0
    }
102
103
0
    auto wallet_desc{CreateWalletDescriptor(fuzzed_data_provider)};
104
0
    if (!wallet_desc.has_value()) return;
105
0
    auto spk_manager{CreateDescriptor(wallet_desc->first, wallet_desc->second, wallet)};
106
0
    if (spk_manager == nullptr) return;
107
108
0
    if (fuzzed_data_provider.ConsumeBool()) {
109
0
        auto wallet_desc{CreateWalletDescriptor(fuzzed_data_provider)};
110
0
        if (!wallet_desc.has_value()) {
111
0
            return;
112
0
        }
113
0
        std::string error;
114
0
        if (spk_manager->CanUpdateToWalletDescriptor(wallet_desc->first, error)) {
115
0
            auto new_spk_manager{CreateDescriptor(wallet_desc->first, wallet_desc->second, wallet)};
116
0
            if (new_spk_manager != nullptr) spk_manager = new_spk_manager;
117
0
        }
118
0
    }
119
120
0
    bool good_data{true};
121
0
    LIMITED_WHILE(good_data && fuzzed_data_provider.ConsumeBool(), 20) {
122
0
        CallOneOf(
123
0
            fuzzed_data_provider,
124
0
            [&] {
125
0
                const CScript script{ConsumeScript(fuzzed_data_provider)};
126
0
                auto is_mine{spk_manager->IsMine(script)};
127
0
                if (is_mine == isminetype::ISMINE_SPENDABLE) {
128
0
                    assert(spk_manager->GetScriptPubKeys().count(script));
129
0
                }
130
0
            },
131
0
            [&] {
132
0
                auto spks{spk_manager->GetScriptPubKeys()};
133
0
                for (const CScript& spk : spks) {
134
0
                    assert(spk_manager->IsMine(spk) == ISMINE_SPENDABLE);
135
0
                    CTxDestination dest;
136
0
                    bool extract_dest{ExtractDestination(spk, dest)};
137
0
                    if (extract_dest) {
138
0
                        const std::string msg{fuzzed_data_provider.ConsumeRandomLengthString()};
139
0
                        PKHash pk_hash{std::get_if<PKHash>(&dest) && fuzzed_data_provider.ConsumeBool() ?
140
0
                                           *std::get_if<PKHash>(&dest) :
141
0
                                           PKHash{ConsumeUInt160(fuzzed_data_provider)}};
142
0
                        std::string str_sig;
143
0
                        (void)spk_manager->SignMessage(msg, pk_hash, str_sig);
144
0
                        (void)spk_manager->GetMetadata(dest);
145
0
                    }
146
0
                }
147
0
            },
148
0
            [&] {
149
0
                auto spks{spk_manager->GetScriptPubKeys()};
150
0
                if (!spks.empty()) {
151
0
                    auto& spk{PickValue(fuzzed_data_provider, spks)};
152
0
                    (void)spk_manager->MarkUnusedAddresses(spk);
153
0
                }
154
0
            },
155
0
            [&] {
156
0
                LOCK(spk_manager->cs_desc_man);
157
0
                auto wallet_desc{spk_manager->GetWalletDescriptor()};
158
0
                if (wallet_desc.descriptor->IsSingleType()) {
159
0
                    auto output_type{wallet_desc.descriptor->GetOutputType()};
160
0
                    if (output_type.has_value()) {
161
0
                        auto dest{spk_manager->GetNewDestination(*output_type)};
162
0
                        if (dest) {
163
0
                            assert(IsValidDestination(*dest));
164
0
                            assert(spk_manager->IsHDEnabled());
165
0
                        }
166
0
                    }
167
0
                }
168
0
            },
169
0
            [&] {
170
0
                CMutableTransaction tx_to;
171
0
                const std::optional<CMutableTransaction> opt_tx_to{ConsumeDeserializable<CMutableTransaction>(fuzzed_data_provider, TX_WITH_WITNESS)};
172
0
                if (!opt_tx_to) {
173
0
                    good_data = false;
174
0
                    return;
175
0
                }
176
0
                tx_to = *opt_tx_to;
177
178
0
                std::map<COutPoint, Coin> coins{ConsumeCoins(fuzzed_data_provider)};
179
0
                const int sighash{fuzzed_data_provider.ConsumeIntegral<int>()};
180
0
                std::map<int, bilingual_str> input_errors;
181
0
                (void)spk_manager->SignTransaction(tx_to, coins, sighash, input_errors);
182
0
            },
183
0
            [&] {
184
0
                std::optional<PartiallySignedTransaction> opt_psbt{ConsumeDeserializable<PartiallySignedTransaction>(fuzzed_data_provider)};
185
0
                if (!opt_psbt) {
186
0
                    good_data = false;
187
0
                    return;
188
0
                }
189
0
                auto psbt{*opt_psbt};
190
0
                const PrecomputedTransactionData txdata{PrecomputePSBTData(psbt)};
191
0
                const int sighash_type{fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 150)};
192
0
                auto sign  = fuzzed_data_provider.ConsumeBool();
193
0
                auto bip32derivs = fuzzed_data_provider.ConsumeBool();
194
0
                auto finalize = fuzzed_data_provider.ConsumeBool();
195
0
                (void)spk_manager->FillPSBT(psbt, txdata, sighash_type, sign, bip32derivs, nullptr, finalize);
196
0
            }
197
0
        );
198
0
    }
199
200
0
    std::string descriptor;
201
0
    (void)spk_manager->GetDescriptorString(descriptor, /*priv=*/fuzzed_data_provider.ConsumeBool());
202
0
    (void)spk_manager->GetEndRange();
203
0
    (void)spk_manager->GetKeyPoolSize();
204
0
}
205
206
} // namespace
207
} // namespace wallet