scriptpubkeyman.cpp
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 { 47 static const auto testing_setup{MakeNoLogFileContext<const TestingSetup>()}; 48 g_setup = testing_setup.get(); 49 SelectParams(ChainType::MAIN); 50 MOCKED_DESC_CONVERTER.Init(); 51 } 52 53 static std::optional<std::pair<WalletDescriptor, FlatSigningProvider>> CreateWalletDescriptor(FuzzedDataProvider& fuzzed_data_provider) 54 { 55 const std::string mocked_descriptor{fuzzed_data_provider.ConsumeRandomLengthString()}; 56 const auto desc_str{MOCKED_DESC_CONVERTER.GetDescriptor(mocked_descriptor)}; 57 if (!desc_str.has_value()) return std::nullopt; 58 if (IsTooExpensive(MakeUCharSpan(*desc_str))) return {}; 59 60 FlatSigningProvider keys; 61 std::string error; 62 std::vector<std::unique_ptr<Descriptor>> parsed_descs = Parse(desc_str.value(), keys, error, false); 63 if (parsed_descs.empty()) return std::nullopt; 64 65 WalletDescriptor w_desc{std::move(parsed_descs.at(0)), /*creation_time=*/0, /*range_start=*/0, /*range_end=*/1, /*next_index=*/1}; 66 return std::make_pair(w_desc, keys); 67 } 68 69 static DescriptorScriptPubKeyMan* CreateDescriptor(WalletDescriptor& wallet_desc, FlatSigningProvider& keys, CWallet& keystore) 70 { 71 LOCK(keystore.cs_wallet); 72 auto spk_manager_res = keystore.AddWalletDescriptor(wallet_desc, keys, /*label=*/"", /*internal=*/false); 73 if (!spk_manager_res) return nullptr; 74 return &spk_manager_res.value().get(); 75 }; 76 77 FUZZ_TARGET(scriptpubkeyman, .init = initialize_spkm) 78 { 79 SeedRandomStateForTest(SeedRand::ZEROS); 80 FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()}; 81 SetMockTime(ConsumeTime(fuzzed_data_provider)); 82 const auto& node{g_setup->m_node}; 83 Chainstate& chainstate{node.chainman->ActiveChainstate()}; 84 std::unique_ptr<CWallet> wallet_ptr{std::make_unique<CWallet>(node.chain.get(), "", CreateMockableWalletDatabase())}; 85 CWallet& wallet{*wallet_ptr}; 86 { 87 LOCK(wallet.cs_wallet); 88 wallet.SetWalletFlag(WALLET_FLAG_DESCRIPTORS); 89 wallet.SetLastBlockProcessed(chainstate.m_chain.Height(), chainstate.m_chain.Tip()->GetBlockHash()); 90 wallet.m_keypool_size = 1; 91 } 92 93 auto wallet_desc{CreateWalletDescriptor(fuzzed_data_provider)}; 94 if (!wallet_desc.has_value()) return; 95 auto spk_manager{CreateDescriptor(wallet_desc->first, wallet_desc->second, wallet)}; 96 if (spk_manager == nullptr) return; 97 98 if (fuzzed_data_provider.ConsumeBool()) { 99 auto wallet_desc{CreateWalletDescriptor(fuzzed_data_provider)}; 100 if (!wallet_desc.has_value()) { 101 return; 102 } 103 std::string error; 104 if (spk_manager->CanUpdateToWalletDescriptor(wallet_desc->first, error)) { 105 auto new_spk_manager{CreateDescriptor(wallet_desc->first, wallet_desc->second, wallet)}; 106 if (new_spk_manager != nullptr) spk_manager = new_spk_manager; 107 } 108 } 109 110 bool good_data{true}; 111 LIMITED_WHILE(good_data && fuzzed_data_provider.ConsumeBool(), 20) { 112 CallOneOf( 113 fuzzed_data_provider, 114 [&] { 115 const CScript script{ConsumeScript(fuzzed_data_provider)}; 116 if (spk_manager->IsMine(script)) { 117 assert(spk_manager->GetScriptPubKeys().contains(script)); 118 } 119 }, 120 [&] { 121 auto spks{spk_manager->GetScriptPubKeys()}; 122 for (const CScript& spk : spks) { 123 assert(spk_manager->IsMine(spk)); 124 CTxDestination dest; 125 bool extract_dest{ExtractDestination(spk, dest)}; 126 if (extract_dest) { 127 const std::string msg{fuzzed_data_provider.ConsumeRandomLengthString()}; 128 PKHash pk_hash{std::get_if<PKHash>(&dest) && fuzzed_data_provider.ConsumeBool() ? 129 *std::get_if<PKHash>(&dest) : 130 PKHash{ConsumeUInt160(fuzzed_data_provider)}}; 131 std::string str_sig; 132 (void)spk_manager->SignMessage(msg, pk_hash, str_sig); 133 (void)spk_manager->GetMetadata(dest); 134 } 135 } 136 }, 137 [&] { 138 auto spks{spk_manager->GetScriptPubKeys()}; 139 if (!spks.empty()) { 140 auto& spk{PickValue(fuzzed_data_provider, spks)}; 141 (void)spk_manager->MarkUnusedAddresses(spk); 142 } 143 }, 144 [&] { 145 LOCK(spk_manager->cs_desc_man); 146 auto wallet_desc{spk_manager->GetWalletDescriptor()}; 147 if (wallet_desc.descriptor->IsSingleType()) { 148 auto output_type{wallet_desc.descriptor->GetOutputType()}; 149 if (output_type.has_value()) { 150 auto dest{spk_manager->GetNewDestination(*output_type)}; 151 if (dest) { 152 assert(IsValidDestination(*dest)); 153 assert(spk_manager->IsHDEnabled()); 154 } 155 } 156 } 157 }, 158 [&] { 159 CMutableTransaction tx_to; 160 const std::optional<CMutableTransaction> opt_tx_to{ConsumeDeserializable<CMutableTransaction>(fuzzed_data_provider, TX_WITH_WITNESS)}; 161 if (!opt_tx_to) { 162 good_data = false; 163 return; 164 } 165 tx_to = *opt_tx_to; 166 167 std::map<COutPoint, Coin> coins{ConsumeCoins(fuzzed_data_provider)}; 168 const int sighash{fuzzed_data_provider.ConsumeIntegral<int>()}; 169 std::map<int, bilingual_str> input_errors; 170 (void)spk_manager->SignTransaction(tx_to, coins, sighash, input_errors); 171 }, 172 [&] { 173 std::optional<PartiallySignedTransaction> opt_psbt{ConsumeDeserializable<PartiallySignedTransaction>(fuzzed_data_provider)}; 174 if (!opt_psbt) { 175 good_data = false; 176 return; 177 } 178 auto psbt{*opt_psbt}; 179 const PrecomputedTransactionData txdata{PrecomputePSBTData(psbt)}; 180 std::optional<int> sighash_type{fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 151)}; 181 if (sighash_type == 151) sighash_type = std::nullopt; 182 auto sign = fuzzed_data_provider.ConsumeBool(); 183 auto bip32derivs = fuzzed_data_provider.ConsumeBool(); 184 auto finalize = fuzzed_data_provider.ConsumeBool(); 185 (void)spk_manager->FillPSBT(psbt, txdata, sighash_type, sign, bip32derivs, nullptr, finalize); 186 } 187 ); 188 } 189 190 std::string descriptor; 191 (void)spk_manager->GetDescriptorString(descriptor, /*priv=*/fuzzed_data_provider.ConsumeBool()); 192 (void)spk_manager->GetEndRange(); 193 (void)spk_manager->GetKeyPoolSize(); 194 } 195 196 } // namespace 197 } // namespace wallet