Bitcoin Core 22.99.0
P2P Digital Currency
rpcdump.cpp
Go to the documentation of this file.
1// Copyright (c) 2009-2020 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 <chain.h>
6#include <clientversion.h>
7#include <core_io.h>
8#include <interfaces/chain.h>
9#include <key_io.h>
10#include <merkleblock.h>
11#include <rpc/util.h>
12#include <script/descriptor.h>
13#include <script/script.h>
14#include <script/standard.h>
15#include <sync.h>
16#include <util/bip32.h>
17#include <util/system.h>
18#include <util/time.h>
19#include <util/translation.h>
20#include <wallet/rpcwallet.h>
21#include <wallet/wallet.h>
22
23#include <stdint.h>
24#include <tuple>
25
26#include <boost/algorithm/string.hpp>
27
28#include <univalue.h>
29
30
31
33
34std::string static EncodeDumpString(const std::string &str) {
35 std::stringstream ret;
36 for (const unsigned char c : str) {
37 if (c <= 32 || c >= 128 || c == '%') {
38 ret << '%' << HexStr(Span<const unsigned char>(&c, 1));
39 } else {
40 ret << c;
41 }
42 }
43 return ret.str();
44}
45
46static std::string DecodeDumpString(const std::string &str) {
47 std::stringstream ret;
48 for (unsigned int pos = 0; pos < str.length(); pos++) {
49 unsigned char c = str[pos];
50 if (c == '%' && pos+2 < str.length()) {
51 c = (((str[pos+1]>>6)*9+((str[pos+1]-'0')&15)) << 4) |
52 ((str[pos+2]>>6)*9+((str[pos+2]-'0')&15));
53 pos += 2;
54 }
55 ret << c;
56 }
57 return ret.str();
58}
59
60static bool GetWalletAddressesForKey(LegacyScriptPubKeyMan* spk_man, const CWallet& wallet, const CKeyID& keyid, std::string& strAddr, std::string& strLabel) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet)
61{
62 bool fLabelFound = false;
63 CKey key;
64 spk_man->GetKey(keyid, key);
65 for (const auto& dest : GetAllDestinationsForKey(key.GetPubKey())) {
66 const auto* address_book_entry = wallet.FindAddressBookEntry(dest);
67 if (address_book_entry) {
68 if (!strAddr.empty()) {
69 strAddr += ",";
70 }
71 strAddr += EncodeDestination(dest);
72 strLabel = EncodeDumpString(address_book_entry->GetLabel());
73 fLabelFound = true;
74 }
75 }
76 if (!fLabelFound) {
77 strAddr = EncodeDestination(GetDestinationForKey(key.GetPubKey(), wallet.m_default_address_type));
78 }
79 return fLabelFound;
80}
81
82static const int64_t TIMESTAMP_MIN = 0;
83
84static void RescanWallet(CWallet& wallet, const WalletRescanReserver& reserver, int64_t time_begin = TIMESTAMP_MIN, bool update = true)
85{
86 int64_t scanned_time = wallet.RescanFromTime(time_begin, reserver, update);
87 if (wallet.IsAbortingRescan()) {
88 throw JSONRPCError(RPC_MISC_ERROR, "Rescan aborted by user.");
89 } else if (scanned_time > time_begin) {
90 throw JSONRPCError(RPC_WALLET_ERROR, "Rescan was unable to fully rescan the blockchain. Some transactions may be missing.");
91 }
92}
93
95{
96 return RPCHelpMan{"importprivkey",
97 "\nAdds a private key (as returned by dumpprivkey) to your wallet. Requires a new wallet backup.\n"
98 "Hint: use importmulti to import more than one private key.\n"
99 "\nNote: This call can take over an hour to complete if rescan is true, during that time, other rpc calls\n"
100 "may report that the imported key exists but related transactions are still missing, leading to temporarily incorrect/bogus balances and unspent outputs until rescan completes.\n"
101 "Note: Use \"getwalletinfo\" to query the scanning progress.\n",
102 {
103 {"privkey", RPCArg::Type::STR, RPCArg::Optional::NO, "The private key (see dumpprivkey)"},
104 {"label", RPCArg::Type::STR, RPCArg::DefaultHint{"current label if address exists, otherwise \"\""}, "An optional label"},
105 {"rescan", RPCArg::Type::BOOL, RPCArg::Default{true}, "Rescan the wallet for transactions"},
106 },
109 "\nDump a private key\n"
110 + HelpExampleCli("dumpprivkey", "\"myaddress\"") +
111 "\nImport the private key with rescan\n"
112 + HelpExampleCli("importprivkey", "\"mykey\"") +
113 "\nImport using a label and without rescan\n"
114 + HelpExampleCli("importprivkey", "\"mykey\" \"testing\" false") +
115 "\nImport using default blank label and without rescan\n"
116 + HelpExampleCli("importprivkey", "\"mykey\" \"\" false") +
117 "\nAs a JSON-RPC call\n"
118 + HelpExampleRpc("importprivkey", "\"mykey\", \"testing\", false")
119 },
120 [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
121{
122 std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
123 if (!pwallet) return NullUniValue;
124
125 if (pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
126 throw JSONRPCError(RPC_WALLET_ERROR, "Cannot import private keys to a wallet with private keys disabled");
127 }
128
129 EnsureLegacyScriptPubKeyMan(*pwallet, true);
130
131 WalletRescanReserver reserver(*pwallet);
132 bool fRescan = true;
133 {
134 LOCK(pwallet->cs_wallet);
135
136 EnsureWalletIsUnlocked(*pwallet);
137
138 std::string strSecret = request.params[0].get_str();
139 std::string strLabel = "";
140 if (!request.params[1].isNull())
141 strLabel = request.params[1].get_str();
142
143 // Whether to perform rescan after import
144 if (!request.params[2].isNull())
145 fRescan = request.params[2].get_bool();
146
147 if (fRescan && pwallet->chain().havePruned()) {
148 // Exit early and print an error.
149 // If a block is pruned after this check, we will import the key(s),
150 // but fail the rescan with a generic error.
151 throw JSONRPCError(RPC_WALLET_ERROR, "Rescan is disabled when blocks are pruned");
152 }
153
154 if (fRescan && !reserver.reserve()) {
155 throw JSONRPCError(RPC_WALLET_ERROR, "Wallet is currently rescanning. Abort existing rescan or wait.");
156 }
157
158 CKey key = DecodeSecret(strSecret);
159 if (!key.IsValid()) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key encoding");
160
161 CPubKey pubkey = key.GetPubKey();
162 CHECK_NONFATAL(key.VerifyPubKey(pubkey));
163 CKeyID vchAddress = pubkey.GetID();
164 {
165 pwallet->MarkDirty();
166
167 // We don't know which corresponding address will be used;
168 // label all new addresses, and label existing addresses if a
169 // label was passed.
170 for (const auto& dest : GetAllDestinationsForKey(pubkey)) {
171 if (!request.params[1].isNull() || !pwallet->FindAddressBookEntry(dest)) {
172 pwallet->SetAddressBook(dest, strLabel, "receive");
173 }
174 }
175
176 // Use timestamp of 1 to scan the whole chain
177 if (!pwallet->ImportPrivKeys({{vchAddress, key}}, 1)) {
178 throw JSONRPCError(RPC_WALLET_ERROR, "Error adding key to wallet");
179 }
180
181 // Add the wpkh script for this key if possible
182 if (pubkey.IsCompressed()) {
183 pwallet->ImportScripts({GetScriptForDestination(WitnessV0KeyHash(vchAddress))}, 0 /* timestamp */);
184 }
185 }
186 }
187 if (fRescan) {
188 RescanWallet(*pwallet, reserver);
189 }
190
191 return NullUniValue;
192},
193 };
194}
195
197{
198 return RPCHelpMan{"abortrescan",
199 "\nStops current wallet rescan triggered by an RPC call, e.g. by an importprivkey call.\n"
200 "Note: Use \"getwalletinfo\" to query the scanning progress.\n",
201 {},
202 RPCResult{RPCResult::Type::BOOL, "", "Whether the abort was successful"},
204 "\nImport a private key\n"
205 + HelpExampleCli("importprivkey", "\"mykey\"") +
206 "\nAbort the running wallet rescan\n"
207 + HelpExampleCli("abortrescan", "") +
208 "\nAs a JSON-RPC call\n"
209 + HelpExampleRpc("abortrescan", "")
210 },
211 [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
212{
213 std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
214 if (!pwallet) return NullUniValue;
215
216 if (!pwallet->IsScanning() || pwallet->IsAbortingRescan()) return false;
217 pwallet->AbortRescan();
218 return true;
219},
220 };
221}
222
224{
225 return RPCHelpMan{"importaddress",
226 "\nAdds an address or script (in hex) that can be watched as if it were in your wallet but cannot be used to spend. Requires a new wallet backup.\n"
227 "\nNote: This call can take over an hour to complete if rescan is true, during that time, other rpc calls\n"
228 "may report that the imported address exists but related transactions are still missing, leading to temporarily incorrect/bogus balances and unspent outputs until rescan completes.\n"
229 "If you have the full public key, you should call importpubkey instead of this.\n"
230 "Hint: use importmulti to import more than one address.\n"
231 "\nNote: If you import a non-standard raw script in hex form, outputs sending to it will be treated\n"
232 "as change, and not show up in many RPCs.\n"
233 "Note: Use \"getwalletinfo\" to query the scanning progress.\n",
234 {
235 {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The Bitcoin address (or hex-encoded script)"},
236 {"label", RPCArg::Type::STR, RPCArg::Default{""}, "An optional label"},
237 {"rescan", RPCArg::Type::BOOL, RPCArg::Default{true}, "Rescan the wallet for transactions"},
238 {"p2sh", RPCArg::Type::BOOL, RPCArg::Default{false}, "Add the P2SH version of the script as well"},
239 },
242 "\nImport an address with rescan\n"
243 + HelpExampleCli("importaddress", "\"myaddress\"") +
244 "\nImport using a label without rescan\n"
245 + HelpExampleCli("importaddress", "\"myaddress\" \"testing\" false") +
246 "\nAs a JSON-RPC call\n"
247 + HelpExampleRpc("importaddress", "\"myaddress\", \"testing\", false")
248 },
249 [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
250{
251 std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
252 if (!pwallet) return NullUniValue;
253
254 EnsureLegacyScriptPubKeyMan(*pwallet, true);
255
256 std::string strLabel;
257 if (!request.params[1].isNull())
258 strLabel = request.params[1].get_str();
259
260 // Whether to perform rescan after import
261 bool fRescan = true;
262 if (!request.params[2].isNull())
263 fRescan = request.params[2].get_bool();
264
265 if (fRescan && pwallet->chain().havePruned()) {
266 // Exit early and print an error.
267 // If a block is pruned after this check, we will import the key(s),
268 // but fail the rescan with a generic error.
269 throw JSONRPCError(RPC_WALLET_ERROR, "Rescan is disabled when blocks are pruned");
270 }
271
272 WalletRescanReserver reserver(*pwallet);
273 if (fRescan && !reserver.reserve()) {
274 throw JSONRPCError(RPC_WALLET_ERROR, "Wallet is currently rescanning. Abort existing rescan or wait.");
275 }
276
277 // Whether to import a p2sh version, too
278 bool fP2SH = false;
279 if (!request.params[3].isNull())
280 fP2SH = request.params[3].get_bool();
281
282 {
283 LOCK(pwallet->cs_wallet);
284
285 CTxDestination dest = DecodeDestination(request.params[0].get_str());
286 if (IsValidDestination(dest)) {
287 if (fP2SH) {
288 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Cannot use the p2sh flag with an address - use a script instead");
289 }
291 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Bech32m addresses cannot be imported into legacy wallets");
292 }
293
294 pwallet->MarkDirty();
295
296 pwallet->ImportScriptPubKeys(strLabel, {GetScriptForDestination(dest)}, false /* have_solving_data */, true /* apply_label */, 1 /* timestamp */);
297 } else if (IsHex(request.params[0].get_str())) {
298 std::vector<unsigned char> data(ParseHex(request.params[0].get_str()));
299 CScript redeem_script(data.begin(), data.end());
300
301 std::set<CScript> scripts = {redeem_script};
302 pwallet->ImportScripts(scripts, 0 /* timestamp */);
303
304 if (fP2SH) {
305 scripts.insert(GetScriptForDestination(ScriptHash(redeem_script)));
306 }
307
308 pwallet->ImportScriptPubKeys(strLabel, scripts, false /* have_solving_data */, true /* apply_label */, 1 /* timestamp */);
309 } else {
310 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address or script");
311 }
312 }
313 if (fRescan)
314 {
315 RescanWallet(*pwallet, reserver);
316 {
317 LOCK(pwallet->cs_wallet);
318 pwallet->ReacceptWalletTransactions();
319 }
320 }
321
322 return NullUniValue;
323},
324 };
325}
326
328{
329 return RPCHelpMan{"importprunedfunds",
330 "\nImports funds without rescan. Corresponding address or script must previously be included in wallet. Aimed towards pruned wallets. The end-user is responsible to import additional transactions that subsequently spend the imported outputs or rescan after the point in the blockchain the transaction is included.\n",
331 {
332 {"rawtransaction", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "A raw transaction in hex funding an already-existing address in wallet"},
333 {"txoutproof", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The hex output from gettxoutproof that contains the transaction"},
334 },
336 RPCExamples{""},
337 [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
338{
339 std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
340 if (!pwallet) return NullUniValue;
341
343 if (!DecodeHexTx(tx, request.params[0].get_str())) {
344 throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed. Make sure the tx has at least one input.");
345 }
346 uint256 hashTx = tx.GetHash();
347
348 CDataStream ssMB(ParseHexV(request.params[1], "proof"), SER_NETWORK, PROTOCOL_VERSION);
349 CMerkleBlock merkleBlock;
350 ssMB >> merkleBlock;
351
352 //Search partial merkle tree in proof for our transaction and index in valid block
353 std::vector<uint256> vMatch;
354 std::vector<unsigned int> vIndex;
355 if (merkleBlock.txn.ExtractMatches(vMatch, vIndex) != merkleBlock.header.hashMerkleRoot) {
356 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Something wrong with merkleblock");
357 }
358
359 LOCK(pwallet->cs_wallet);
360 int height;
361 if (!pwallet->chain().findAncestorByHash(pwallet->GetLastBlockHash(), merkleBlock.header.GetHash(), FoundBlock().height(height))) {
362 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found in chain");
363 }
364
365 std::vector<uint256>::const_iterator it;
366 if ((it = std::find(vMatch.begin(), vMatch.end(), hashTx)) == vMatch.end()) {
367 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction given doesn't exist in proof");
368 }
369
370 unsigned int txnIndex = vIndex[it - vMatch.begin()];
371
372 CWalletTx::Confirmation confirm(CWalletTx::Status::CONFIRMED, height, merkleBlock.header.GetHash(), txnIndex);
373
375 if (pwallet->IsMine(*tx_ref)) {
376 pwallet->AddToWallet(std::move(tx_ref), confirm);
377 return NullUniValue;
378 }
379
380 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No addresses in wallet correspond to included transaction");
381},
382 };
383}
384
386{
387 return RPCHelpMan{"removeprunedfunds",
388 "\nDeletes the specified transaction from the wallet. Meant for use with pruned wallets and as a companion to importprunedfunds. This will affect wallet balances.\n",
389 {
390 {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The hex-encoded id of the transaction you are deleting"},
391 },
394 HelpExampleCli("removeprunedfunds", "\"a8d0c0184dde994a09ec054286f1ce581bebf46446a512166eae7628734ea0a5\"") +
395 "\nAs a JSON-RPC call\n"
396 + HelpExampleRpc("removeprunedfunds", "\"a8d0c0184dde994a09ec054286f1ce581bebf46446a512166eae7628734ea0a5\"")
397 },
398 [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
399{
400 std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
401 if (!pwallet) return NullUniValue;
402
403 LOCK(pwallet->cs_wallet);
404
405 uint256 hash(ParseHashV(request.params[0], "txid"));
406 std::vector<uint256> vHash;
407 vHash.push_back(hash);
408 std::vector<uint256> vHashOut;
409
410 if (pwallet->ZapSelectTx(vHash, vHashOut) != DBErrors::LOAD_OK) {
411 throw JSONRPCError(RPC_WALLET_ERROR, "Could not properly delete the transaction.");
412 }
413
414 if(vHashOut.empty()) {
415 throw JSONRPCError(RPC_INVALID_PARAMETER, "Transaction does not exist in wallet.");
416 }
417
418 return NullUniValue;
419},
420 };
421}
422
424{
425 return RPCHelpMan{"importpubkey",
426 "\nAdds a public key (in hex) that can be watched as if it were in your wallet but cannot be used to spend. Requires a new wallet backup.\n"
427 "Hint: use importmulti to import more than one public key.\n"
428 "\nNote: This call can take over an hour to complete if rescan is true, during that time, other rpc calls\n"
429 "may report that the imported pubkey exists but related transactions are still missing, leading to temporarily incorrect/bogus balances and unspent outputs until rescan completes.\n"
430 "Note: Use \"getwalletinfo\" to query the scanning progress.\n",
431 {
432 {"pubkey", RPCArg::Type::STR, RPCArg::Optional::NO, "The hex-encoded public key"},
433 {"label", RPCArg::Type::STR, RPCArg::Default{""}, "An optional label"},
434 {"rescan", RPCArg::Type::BOOL, RPCArg::Default{true}, "Rescan the wallet for transactions"},
435 },
438 "\nImport a public key with rescan\n"
439 + HelpExampleCli("importpubkey", "\"mypubkey\"") +
440 "\nImport using a label without rescan\n"
441 + HelpExampleCli("importpubkey", "\"mypubkey\" \"testing\" false") +
442 "\nAs a JSON-RPC call\n"
443 + HelpExampleRpc("importpubkey", "\"mypubkey\", \"testing\", false")
444 },
445 [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
446{
447 std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
448 if (!pwallet) return NullUniValue;
449
450 EnsureLegacyScriptPubKeyMan(*pwallet, true);
451
452 std::string strLabel;
453 if (!request.params[1].isNull())
454 strLabel = request.params[1].get_str();
455
456 // Whether to perform rescan after import
457 bool fRescan = true;
458 if (!request.params[2].isNull())
459 fRescan = request.params[2].get_bool();
460
461 if (fRescan && pwallet->chain().havePruned()) {
462 // Exit early and print an error.
463 // If a block is pruned after this check, we will import the key(s),
464 // but fail the rescan with a generic error.
465 throw JSONRPCError(RPC_WALLET_ERROR, "Rescan is disabled when blocks are pruned");
466 }
467
468 WalletRescanReserver reserver(*pwallet);
469 if (fRescan && !reserver.reserve()) {
470 throw JSONRPCError(RPC_WALLET_ERROR, "Wallet is currently rescanning. Abort existing rescan or wait.");
471 }
472
473 if (!IsHex(request.params[0].get_str()))
474 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Pubkey must be a hex string");
475 std::vector<unsigned char> data(ParseHex(request.params[0].get_str()));
476 CPubKey pubKey(data);
477 if (!pubKey.IsFullyValid())
478 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Pubkey is not a valid public key");
479
480 {
481 LOCK(pwallet->cs_wallet);
482
483 std::set<CScript> script_pub_keys;
484 for (const auto& dest : GetAllDestinationsForKey(pubKey)) {
485 script_pub_keys.insert(GetScriptForDestination(dest));
486 }
487
488 pwallet->MarkDirty();
489
490 pwallet->ImportScriptPubKeys(strLabel, script_pub_keys, true /* have_solving_data */, true /* apply_label */, 1 /* timestamp */);
491
492 pwallet->ImportPubKeys({pubKey.GetID()}, {{pubKey.GetID(), pubKey}} , {} /* key_origins */, false /* add_keypool */, false /* internal */, 1 /* timestamp */);
493 }
494 if (fRescan)
495 {
496 RescanWallet(*pwallet, reserver);
497 {
498 LOCK(pwallet->cs_wallet);
499 pwallet->ReacceptWalletTransactions();
500 }
501 }
502
503 return NullUniValue;
504},
505 };
506}
507
508
510{
511 return RPCHelpMan{"importwallet",
512 "\nImports keys from a wallet dump file (see dumpwallet). Requires a new wallet backup to include imported keys.\n"
513 "Note: Use \"getwalletinfo\" to query the scanning progress.\n",
514 {
515 {"filename", RPCArg::Type::STR, RPCArg::Optional::NO, "The wallet file"},
516 },
519 "\nDump the wallet\n"
520 + HelpExampleCli("dumpwallet", "\"test\"") +
521 "\nImport the wallet\n"
522 + HelpExampleCli("importwallet", "\"test\"") +
523 "\nImport using the json rpc call\n"
524 + HelpExampleRpc("importwallet", "\"test\"")
525 },
526 [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
527{
528 std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
529 if (!pwallet) return NullUniValue;
530
531 EnsureLegacyScriptPubKeyMan(*pwallet, true);
532
533 if (pwallet->chain().havePruned()) {
534 // Exit early and print an error.
535 // If a block is pruned after this check, we will import the key(s),
536 // but fail the rescan with a generic error.
537 throw JSONRPCError(RPC_WALLET_ERROR, "Importing wallets is disabled when blocks are pruned");
538 }
539
540 WalletRescanReserver reserver(*pwallet);
541 if (!reserver.reserve()) {
542 throw JSONRPCError(RPC_WALLET_ERROR, "Wallet is currently rescanning. Abort existing rescan or wait.");
543 }
544
545 int64_t nTimeBegin = 0;
546 bool fGood = true;
547 {
548 LOCK(pwallet->cs_wallet);
549
550 EnsureWalletIsUnlocked(*pwallet);
551
553 file.open(fs::u8path(request.params[0].get_str()), std::ios::in | std::ios::ate);
554 if (!file.is_open()) {
555 throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot open wallet dump file");
556 }
557 CHECK_NONFATAL(pwallet->chain().findBlock(pwallet->GetLastBlockHash(), FoundBlock().time(nTimeBegin)));
558
559 int64_t nFilesize = std::max((int64_t)1, (int64_t)file.tellg());
560 file.seekg(0, file.beg);
561
562 // Use uiInterface.ShowProgress instead of pwallet.ShowProgress because pwallet.ShowProgress has a cancel button tied to AbortRescan which
563 // we don't want for this progress bar showing the import progress. uiInterface.ShowProgress does not have a cancel button.
564 pwallet->chain().showProgress(strprintf("%s " + _("Importing…").translated, pwallet->GetDisplayName()), 0, false); // show progress dialog in GUI
565 std::vector<std::tuple<CKey, int64_t, bool, std::string>> keys;
566 std::vector<std::pair<CScript, int64_t>> scripts;
567 while (file.good()) {
568 pwallet->chain().showProgress("", std::max(1, std::min(50, (int)(((double)file.tellg() / (double)nFilesize) * 100))), false);
569 std::string line;
570 std::getline(file, line);
571 if (line.empty() || line[0] == '#')
572 continue;
573
574 std::vector<std::string> vstr;
575 boost::split(vstr, line, boost::is_any_of(" "));
576 if (vstr.size() < 2)
577 continue;
578 CKey key = DecodeSecret(vstr[0]);
579 if (key.IsValid()) {
580 int64_t nTime = ParseISO8601DateTime(vstr[1]);
581 std::string strLabel;
582 bool fLabel = true;
583 for (unsigned int nStr = 2; nStr < vstr.size(); nStr++) {
584 if (vstr[nStr].front() == '#')
585 break;
586 if (vstr[nStr] == "change=1")
587 fLabel = false;
588 if (vstr[nStr] == "reserve=1")
589 fLabel = false;
590 if (vstr[nStr].substr(0,6) == "label=") {
591 strLabel = DecodeDumpString(vstr[nStr].substr(6));
592 fLabel = true;
593 }
594 }
595 keys.push_back(std::make_tuple(key, nTime, fLabel, strLabel));
596 } else if(IsHex(vstr[0])) {
597 std::vector<unsigned char> vData(ParseHex(vstr[0]));
598 CScript script = CScript(vData.begin(), vData.end());
599 int64_t birth_time = ParseISO8601DateTime(vstr[1]);
600 scripts.push_back(std::pair<CScript, int64_t>(script, birth_time));
601 }
602 }
603 file.close();
604 // We now know whether we are importing private keys, so we can error if private keys are disabled
605 if (keys.size() > 0 && pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
606 pwallet->chain().showProgress("", 100, false); // hide progress dialog in GUI
607 throw JSONRPCError(RPC_WALLET_ERROR, "Importing wallets is disabled when private keys are disabled");
608 }
609 double total = (double)(keys.size() + scripts.size());
610 double progress = 0;
611 for (const auto& key_tuple : keys) {
612 pwallet->chain().showProgress("", std::max(50, std::min(75, (int)((progress / total) * 100) + 50)), false);
613 const CKey& key = std::get<0>(key_tuple);
614 int64_t time = std::get<1>(key_tuple);
615 bool has_label = std::get<2>(key_tuple);
616 std::string label = std::get<3>(key_tuple);
617
618 CPubKey pubkey = key.GetPubKey();
619 CHECK_NONFATAL(key.VerifyPubKey(pubkey));
620 CKeyID keyid = pubkey.GetID();
621
622 pwallet->WalletLogPrintf("Importing %s...\n", EncodeDestination(PKHash(keyid)));
623
624 if (!pwallet->ImportPrivKeys({{keyid, key}}, time)) {
625 pwallet->WalletLogPrintf("Error importing key for %s\n", EncodeDestination(PKHash(keyid)));
626 fGood = false;
627 continue;
628 }
629
630 if (has_label)
631 pwallet->SetAddressBook(PKHash(keyid), label, "receive");
632
633 nTimeBegin = std::min(nTimeBegin, time);
634 progress++;
635 }
636 for (const auto& script_pair : scripts) {
637 pwallet->chain().showProgress("", std::max(50, std::min(75, (int)((progress / total) * 100) + 50)), false);
638 const CScript& script = script_pair.first;
639 int64_t time = script_pair.second;
640
641 if (!pwallet->ImportScripts({script}, time)) {
642 pwallet->WalletLogPrintf("Error importing script %s\n", HexStr(script));
643 fGood = false;
644 continue;
645 }
646 if (time > 0) {
647 nTimeBegin = std::min(nTimeBegin, time);
648 }
649
650 progress++;
651 }
652 pwallet->chain().showProgress("", 100, false); // hide progress dialog in GUI
653 }
654 pwallet->chain().showProgress("", 100, false); // hide progress dialog in GUI
655 RescanWallet(*pwallet, reserver, nTimeBegin, false /* update */);
656 pwallet->MarkDirty();
657
658 if (!fGood)
659 throw JSONRPCError(RPC_WALLET_ERROR, "Error adding some keys/scripts to wallet");
660
661 return NullUniValue;
662},
663 };
664}
665
667{
668 return RPCHelpMan{"dumpprivkey",
669 "\nReveals the private key corresponding to 'address'.\n"
670 "Then the importprivkey can be used with this output\n",
671 {
672 {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The bitcoin address for the private key"},
673 },
674 RPCResult{
675 RPCResult::Type::STR, "key", "The private key"
676 },
678 HelpExampleCli("dumpprivkey", "\"myaddress\"")
679 + HelpExampleCli("importprivkey", "\"mykey\"")
680 + HelpExampleRpc("dumpprivkey", "\"myaddress\"")
681 },
682 [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
683{
684 std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
685 if (!pwallet) return NullUniValue;
686
688
689 LOCK2(pwallet->cs_wallet, spk_man.cs_KeyStore);
690
691 EnsureWalletIsUnlocked(*pwallet);
692
693 std::string strAddress = request.params[0].get_str();
694 CTxDestination dest = DecodeDestination(strAddress);
695 if (!IsValidDestination(dest)) {
696 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address");
697 }
698 auto keyid = GetKeyForDestination(spk_man, dest);
699 if (keyid.IsNull()) {
700 throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to a key");
701 }
702 CKey vchSecret;
703 if (!spk_man.GetKey(keyid, vchSecret)) {
704 throw JSONRPCError(RPC_WALLET_ERROR, "Private key for address " + strAddress + " is not known");
705 }
706 return EncodeSecret(vchSecret);
707},
708 };
709}
710
711
713{
714 return RPCHelpMan{"dumpwallet",
715 "\nDumps all wallet keys in a human-readable format to a server-side file. This does not allow overwriting existing files.\n"
716 "Imported scripts are included in the dumpfile, but corresponding BIP173 addresses, etc. may not be added automatically by importwallet.\n"
717 "Note that if your wallet contains keys which are not derived from your HD seed (e.g. imported keys), these are not covered by\n"
718 "only backing up the seed itself, and must be backed up too (e.g. ensure you back up the whole dumpfile).\n",
719 {
720 {"filename", RPCArg::Type::STR, RPCArg::Optional::NO, "The filename with path (absolute path recommended)"},
721 },
722 RPCResult{
723 RPCResult::Type::OBJ, "", "",
724 {
725 {RPCResult::Type::STR, "filename", "The filename with full absolute path"},
726 }
727 },
729 HelpExampleCli("dumpwallet", "\"test\"")
730 + HelpExampleRpc("dumpwallet", "\"test\"")
731 },
732 [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
733{
734 std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
735 if (!pwallet) return NullUniValue;
736
737 CWallet& wallet = *pwallet;
739
740 // Make sure the results are valid at least up to the most recent block
741 // the user could have gotten from another RPC command prior to now
742 wallet.BlockUntilSyncedToCurrentChain();
743
744 LOCK(wallet.cs_wallet);
745
747
748 fs::path filepath = fs::u8path(request.params[0].get_str());
749 filepath = fs::absolute(filepath);
750
751 /* Prevent arbitrary files from being overwritten. There have been reports
752 * that users have overwritten wallet files this way:
753 * https://github.com/bitcoin/bitcoin/issues/9934
754 * It may also avoid other security issues.
755 */
756 if (fs::exists(filepath)) {
757 throw JSONRPCError(RPC_INVALID_PARAMETER, filepath.u8string() + " already exists. If you are sure this is what you want, move it out of the way first");
758 }
759
761 file.open(filepath);
762 if (!file.is_open())
763 throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot open wallet dump file");
764
765 std::map<CKeyID, int64_t> mapKeyBirth;
766 wallet.GetKeyBirthTimes(mapKeyBirth);
767
768 int64_t block_time = 0;
769 CHECK_NONFATAL(wallet.chain().findBlock(wallet.GetLastBlockHash(), FoundBlock().time(block_time)));
770
771 // Note: To avoid a lock order issue, access to cs_main must be locked before cs_KeyStore.
772 // So we do the two things in this function that lock cs_main first: GetKeyBirthTimes, and findBlock.
773 LOCK(spk_man.cs_KeyStore);
774
775 const std::map<CKeyID, int64_t>& mapKeyPool = spk_man.GetAllReserveKeys();
776 std::set<CScriptID> scripts = spk_man.GetCScripts();
777
778 // sort time/key pairs
779 std::vector<std::pair<int64_t, CKeyID> > vKeyBirth;
780 for (const auto& entry : mapKeyBirth) {
781 vKeyBirth.push_back(std::make_pair(entry.second, entry.first));
782 }
783 mapKeyBirth.clear();
784 std::sort(vKeyBirth.begin(), vKeyBirth.end());
785
786 // produce output
787 file << strprintf("# Wallet dump created by %s %s\n", PACKAGE_NAME, FormatFullVersion());
788 file << strprintf("# * Created on %s\n", FormatISO8601DateTime(GetTime()));
789 file << strprintf("# * Best block at time of backup was %i (%s),\n", wallet.GetLastBlockHeight(), wallet.GetLastBlockHash().ToString());
790 file << strprintf("# mined on %s\n", FormatISO8601DateTime(block_time));
791 file << "\n";
792
793 // add the base58check encoded extended master if the wallet uses HD
794 CKeyID seed_id = spk_man.GetHDChain().seed_id;
795 if (!seed_id.IsNull())
796 {
797 CKey seed;
798 if (spk_man.GetKey(seed_id, seed)) {
799 CExtKey masterKey;
800 masterKey.SetSeed(seed);
801
802 file << "# extended private masterkey: " << EncodeExtKey(masterKey) << "\n\n";
803 }
804 }
805 for (std::vector<std::pair<int64_t, CKeyID> >::const_iterator it = vKeyBirth.begin(); it != vKeyBirth.end(); it++) {
806 const CKeyID &keyid = it->second;
807 std::string strTime = FormatISO8601DateTime(it->first);
808 std::string strAddr;
809 std::string strLabel;
810 CKey key;
811 if (spk_man.GetKey(keyid, key)) {
812 file << strprintf("%s %s ", EncodeSecret(key), strTime);
813 if (GetWalletAddressesForKey(&spk_man, wallet, keyid, strAddr, strLabel)) {
814 file << strprintf("label=%s", strLabel);
815 } else if (keyid == seed_id) {
816 file << "hdseed=1";
817 } else if (mapKeyPool.count(keyid)) {
818 file << "reserve=1";
819 } else if (spk_man.mapKeyMetadata[keyid].hdKeypath == "s") {
820 file << "inactivehdseed=1";
821 } else {
822 file << "change=1";
823 }
824 file << strprintf(" # addr=%s%s\n", strAddr, (spk_man.mapKeyMetadata[keyid].has_key_origin ? " hdkeypath="+WriteHDKeypath(spk_man.mapKeyMetadata[keyid].key_origin.path) : ""));
825 }
826 }
827 file << "\n";
828 for (const CScriptID &scriptid : scripts) {
829 CScript script;
830 std::string create_time = "0";
831 std::string address = EncodeDestination(ScriptHash(scriptid));
832 // get birth times for scripts with metadata
833 auto it = spk_man.m_script_metadata.find(scriptid);
834 if (it != spk_man.m_script_metadata.end()) {
835 create_time = FormatISO8601DateTime(it->second.nCreateTime);
836 }
837 if(spk_man.GetCScript(scriptid, script)) {
838 file << strprintf("%s %s script=1", HexStr(script), create_time);
839 file << strprintf(" # addr=%s\n", address);
840 }
841 }
842 file << "\n";
843 file << "# End of dump\n";
844 file.close();
845
847 reply.pushKV("filename", filepath.u8string());
848
849 return reply;
850},
851 };
852}
853
855{
856 // Input data
857 std::unique_ptr<CScript> redeemscript;
858 std::unique_ptr<CScript> witnessscript;
859
860 // Output data
861 std::set<CScript> import_scripts;
862 std::map<CKeyID, bool> used_keys;
863 std::map<CKeyID, std::pair<CPubKey, KeyOriginInfo>> key_origins;
864};
865
867{
868 TOP,
869 P2SH,
870 WITNESS_V0,
871};
872
873// Analyse the provided scriptPubKey, determining which keys and which redeem scripts from the ImportData struct are needed to spend it, and mark them as used.
874// Returns an error string, or the empty string for success.
875static std::string RecurseImportData(const CScript& script, ImportData& import_data, const ScriptContext script_ctx)
876{
877 // Use Solver to obtain script type and parsed pubkeys or hashes:
878 std::vector<std::vector<unsigned char>> solverdata;
879 TxoutType script_type = Solver(script, solverdata);
880
881 switch (script_type) {
882 case TxoutType::PUBKEY: {
883 CPubKey pubkey(solverdata[0]);
884 import_data.used_keys.emplace(pubkey.GetID(), false);
885 return "";
886 }
888 CKeyID id = CKeyID(uint160(solverdata[0]));
889 import_data.used_keys[id] = true;
890 return "";
891 }
893 if (script_ctx == ScriptContext::P2SH) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Trying to nest P2SH inside another P2SH");
894 if (script_ctx == ScriptContext::WITNESS_V0) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Trying to nest P2SH inside a P2WSH");
895 CHECK_NONFATAL(script_ctx == ScriptContext::TOP);
896 CScriptID id = CScriptID(uint160(solverdata[0]));
897 auto subscript = std::move(import_data.redeemscript); // Remove redeemscript from import_data to check for superfluous script later.
898 if (!subscript) return "missing redeemscript";
899 if (CScriptID(*subscript) != id) return "redeemScript does not match the scriptPubKey";
900 import_data.import_scripts.emplace(*subscript);
901 return RecurseImportData(*subscript, import_data, ScriptContext::P2SH);
902 }
903 case TxoutType::MULTISIG: {
904 for (size_t i = 1; i + 1< solverdata.size(); ++i) {
905 CPubKey pubkey(solverdata[i]);
906 import_data.used_keys.emplace(pubkey.GetID(), false);
907 }
908 return "";
909 }
911 if (script_ctx == ScriptContext::WITNESS_V0) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Trying to nest P2WSH inside another P2WSH");
912 uint256 fullid(solverdata[0]);
914 CRIPEMD160().Write(fullid.begin(), fullid.size()).Finalize(id.begin());
915 auto subscript = std::move(import_data.witnessscript); // Remove redeemscript from import_data to check for superfluous script later.
916 if (!subscript) return "missing witnessscript";
917 if (CScriptID(*subscript) != id) return "witnessScript does not match the scriptPubKey or redeemScript";
918 if (script_ctx == ScriptContext::TOP) {
919 import_data.import_scripts.emplace(script); // Special rule for IsMine: native P2WSH requires the TOP script imported (see script/ismine.cpp)
920 }
921 import_data.import_scripts.emplace(*subscript);
922 return RecurseImportData(*subscript, import_data, ScriptContext::WITNESS_V0);
923 }
925 if (script_ctx == ScriptContext::WITNESS_V0) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Trying to nest P2WPKH inside P2WSH");
926 CKeyID id = CKeyID(uint160(solverdata[0]));
927 import_data.used_keys[id] = true;
928 if (script_ctx == ScriptContext::TOP) {
929 import_data.import_scripts.emplace(script); // Special rule for IsMine: native P2WPKH requires the TOP script imported (see script/ismine.cpp)
930 }
931 return "";
932 }
934 return "unspendable script";
938 return "unrecognized script";
939 } // no default case, so the compiler can warn about missing cases
940 CHECK_NONFATAL(false);
941}
942
943static UniValue ProcessImportLegacy(ImportData& import_data, std::map<CKeyID, CPubKey>& pubkey_map, std::map<CKeyID, CKey>& privkey_map, std::set<CScript>& script_pub_keys, bool& have_solving_data, const UniValue& data, std::vector<CKeyID>& ordered_pubkeys)
944{
945 UniValue warnings(UniValue::VARR);
946
947 // First ensure scriptPubKey has either a script or JSON with "address" string
948 const UniValue& scriptPubKey = data["scriptPubKey"];
949 bool isScript = scriptPubKey.getType() == UniValue::VSTR;
950 if (!isScript && !(scriptPubKey.getType() == UniValue::VOBJ && scriptPubKey.exists("address"))) {
951 throw JSONRPCError(RPC_INVALID_PARAMETER, "scriptPubKey must be string with script or JSON with address string");
952 }
953 const std::string& output = isScript ? scriptPubKey.get_str() : scriptPubKey["address"].get_str();
954
955 // Optional fields.
956 const std::string& strRedeemScript = data.exists("redeemscript") ? data["redeemscript"].get_str() : "";
957 const std::string& witness_script_hex = data.exists("witnessscript") ? data["witnessscript"].get_str() : "";
958 const UniValue& pubKeys = data.exists("pubkeys") ? data["pubkeys"].get_array() : UniValue();
959 const UniValue& keys = data.exists("keys") ? data["keys"].get_array() : UniValue();
960 const bool internal = data.exists("internal") ? data["internal"].get_bool() : false;
961 const bool watchOnly = data.exists("watchonly") ? data["watchonly"].get_bool() : false;
962
963 if (data.exists("range")) {
964 throw JSONRPCError(RPC_INVALID_PARAMETER, "Range should not be specified for a non-descriptor import");
965 }
966
967 // Generate the script and destination for the scriptPubKey provided
968 CScript script;
969 if (!isScript) {
970 CTxDestination dest = DecodeDestination(output);
971 if (!IsValidDestination(dest)) {
972 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address \"" + output + "\"");
973 }
975 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Bech32m addresses cannot be imported into legacy wallets");
976 }
977 script = GetScriptForDestination(dest);
978 } else {
979 if (!IsHex(output)) {
980 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid scriptPubKey \"" + output + "\"");
981 }
982 std::vector<unsigned char> vData(ParseHex(output));
983 script = CScript(vData.begin(), vData.end());
984 CTxDestination dest;
985 if (!ExtractDestination(script, dest) && !internal) {
986 throw JSONRPCError(RPC_INVALID_PARAMETER, "Internal must be set to true for nonstandard scriptPubKey imports.");
987 }
988 }
989 script_pub_keys.emplace(script);
990
991 // Parse all arguments
992 if (strRedeemScript.size()) {
993 if (!IsHex(strRedeemScript)) {
994 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid redeem script \"" + strRedeemScript + "\": must be hex string");
995 }
996 auto parsed_redeemscript = ParseHex(strRedeemScript);
997 import_data.redeemscript = std::make_unique<CScript>(parsed_redeemscript.begin(), parsed_redeemscript.end());
998 }
999 if (witness_script_hex.size()) {
1000 if (!IsHex(witness_script_hex)) {
1001 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid witness script \"" + witness_script_hex + "\": must be hex string");
1002 }
1003 auto parsed_witnessscript = ParseHex(witness_script_hex);
1004 import_data.witnessscript = std::make_unique<CScript>(parsed_witnessscript.begin(), parsed_witnessscript.end());
1005 }
1006 for (size_t i = 0; i < pubKeys.size(); ++i) {
1007 const auto& str = pubKeys[i].get_str();
1008 if (!IsHex(str)) {
1009 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Pubkey \"" + str + "\" must be a hex string");
1010 }
1011 auto parsed_pubkey = ParseHex(str);
1012 CPubKey pubkey(parsed_pubkey);
1013 if (!pubkey.IsFullyValid()) {
1014 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Pubkey \"" + str + "\" is not a valid public key");
1015 }
1016 pubkey_map.emplace(pubkey.GetID(), pubkey);
1017 ordered_pubkeys.push_back(pubkey.GetID());
1018 }
1019 for (size_t i = 0; i < keys.size(); ++i) {
1020 const auto& str = keys[i].get_str();
1021 CKey key = DecodeSecret(str);
1022 if (!key.IsValid()) {
1023 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key encoding");
1024 }
1025 CPubKey pubkey = key.GetPubKey();
1026 CKeyID id = pubkey.GetID();
1027 if (pubkey_map.count(id)) {
1028 pubkey_map.erase(id);
1029 }
1030 privkey_map.emplace(id, key);
1031 }
1032
1033
1034 // Verify and process input data
1035 have_solving_data = import_data.redeemscript || import_data.witnessscript || pubkey_map.size() || privkey_map.size();
1036 if (have_solving_data) {
1037 // Match up data in import_data with the scriptPubKey in script.
1038 auto error = RecurseImportData(script, import_data, ScriptContext::TOP);
1039
1040 // Verify whether the watchonly option corresponds to the availability of private keys.
1041 bool spendable = std::all_of(import_data.used_keys.begin(), import_data.used_keys.end(), [&](const std::pair<CKeyID, bool>& used_key){ return privkey_map.count(used_key.first) > 0; });
1042 if (!watchOnly && !spendable) {
1043 warnings.push_back("Some private keys are missing, outputs will be considered watchonly. If this is intentional, specify the watchonly flag.");
1044 }
1045 if (watchOnly && spendable) {
1046 warnings.push_back("All private keys are provided, outputs will be considered spendable. If this is intentional, do not specify the watchonly flag.");
1047 }
1048
1049 // Check that all required keys for solvability are provided.
1050 if (error.empty()) {
1051 for (const auto& require_key : import_data.used_keys) {
1052 if (!require_key.second) continue; // Not a required key
1053 if (pubkey_map.count(require_key.first) == 0 && privkey_map.count(require_key.first) == 0) {
1054 error = "some required keys are missing";
1055 }
1056 }
1057 }
1058
1059 if (!error.empty()) {
1060 warnings.push_back("Importing as non-solvable: " + error + ". If this is intentional, don't provide any keys, pubkeys, witnessscript, or redeemscript.");
1061 import_data = ImportData();
1062 pubkey_map.clear();
1063 privkey_map.clear();
1064 have_solving_data = false;
1065 } else {
1066 // RecurseImportData() removes any relevant redeemscript/witnessscript from import_data, so we can use that to discover if a superfluous one was provided.
1067 if (import_data.redeemscript) warnings.push_back("Ignoring redeemscript as this is not a P2SH script.");
1068 if (import_data.witnessscript) warnings.push_back("Ignoring witnessscript as this is not a (P2SH-)P2WSH script.");
1069 for (auto it = privkey_map.begin(); it != privkey_map.end(); ) {
1070 auto oldit = it++;
1071 if (import_data.used_keys.count(oldit->first) == 0) {
1072 warnings.push_back("Ignoring irrelevant private key.");
1073 privkey_map.erase(oldit);
1074 }
1075 }
1076 for (auto it = pubkey_map.begin(); it != pubkey_map.end(); ) {
1077 auto oldit = it++;
1078 auto key_data_it = import_data.used_keys.find(oldit->first);
1079 if (key_data_it == import_data.used_keys.end() || !key_data_it->second) {
1080 warnings.push_back("Ignoring public key \"" + HexStr(oldit->first) + "\" as it doesn't appear inside P2PKH or P2WPKH.");
1081 pubkey_map.erase(oldit);
1082 }
1083 }
1084 }
1085 }
1086
1087 return warnings;
1088}
1089
1090static UniValue ProcessImportDescriptor(ImportData& import_data, std::map<CKeyID, CPubKey>& pubkey_map, std::map<CKeyID, CKey>& privkey_map, std::set<CScript>& script_pub_keys, bool& have_solving_data, const UniValue& data, std::vector<CKeyID>& ordered_pubkeys)
1091{
1092 UniValue warnings(UniValue::VARR);
1093
1094 const std::string& descriptor = data["desc"].get_str();
1096 std::string error;
1097 auto parsed_desc = Parse(descriptor, keys, error, /* require_checksum = */ true);
1098 if (!parsed_desc) {
1100 }
1101 if (parsed_desc->GetOutputType() == OutputType::BECH32M) {
1102 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Bech32m descriptors cannot be imported into legacy wallets");
1103 }
1104
1105 have_solving_data = parsed_desc->IsSolvable();
1106 const bool watch_only = data.exists("watchonly") ? data["watchonly"].get_bool() : false;
1107
1108 int64_t range_start = 0, range_end = 0;
1109 if (!parsed_desc->IsRange() && data.exists("range")) {
1110 throw JSONRPCError(RPC_INVALID_PARAMETER, "Range should not be specified for an un-ranged descriptor");
1111 } else if (parsed_desc->IsRange()) {
1112 if (!data.exists("range")) {
1113 throw JSONRPCError(RPC_INVALID_PARAMETER, "Descriptor is ranged, please specify the range");
1114 }
1115 std::tie(range_start, range_end) = ParseDescriptorRange(data["range"]);
1116 }
1117
1118 const UniValue& priv_keys = data.exists("keys") ? data["keys"].get_array() : UniValue();
1119
1120 // Expand all descriptors to get public keys and scripts, and private keys if available.
1121 for (int i = range_start; i <= range_end; ++i) {
1122 FlatSigningProvider out_keys;
1123 std::vector<CScript> scripts_temp;
1124 parsed_desc->Expand(i, keys, scripts_temp, out_keys);
1125 std::copy(scripts_temp.begin(), scripts_temp.end(), std::inserter(script_pub_keys, script_pub_keys.end()));
1126 for (const auto& key_pair : out_keys.pubkeys) {
1127 ordered_pubkeys.push_back(key_pair.first);
1128 }
1129
1130 for (const auto& x : out_keys.scripts) {
1131 import_data.import_scripts.emplace(x.second);
1132 }
1133
1134 parsed_desc->ExpandPrivate(i, keys, out_keys);
1135
1136 std::copy(out_keys.pubkeys.begin(), out_keys.pubkeys.end(), std::inserter(pubkey_map, pubkey_map.end()));
1137 std::copy(out_keys.keys.begin(), out_keys.keys.end(), std::inserter(privkey_map, privkey_map.end()));
1138 import_data.key_origins.insert(out_keys.origins.begin(), out_keys.origins.end());
1139 }
1140
1141 for (size_t i = 0; i < priv_keys.size(); ++i) {
1142 const auto& str = priv_keys[i].get_str();
1143 CKey key = DecodeSecret(str);
1144 if (!key.IsValid()) {
1145 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key encoding");
1146 }
1147 CPubKey pubkey = key.GetPubKey();
1148 CKeyID id = pubkey.GetID();
1149
1150 // Check if this private key corresponds to a public key from the descriptor
1151 if (!pubkey_map.count(id)) {
1152 warnings.push_back("Ignoring irrelevant private key.");
1153 } else {
1154 privkey_map.emplace(id, key);
1155 }
1156 }
1157
1158 // Check if all the public keys have corresponding private keys in the import for spendability.
1159 // This does not take into account threshold multisigs which could be spendable without all keys.
1160 // Thus, threshold multisigs without all keys will be considered not spendable here, even if they are,
1161 // perhaps triggering a false warning message. This is consistent with the current wallet IsMine check.
1162 bool spendable = std::all_of(pubkey_map.begin(), pubkey_map.end(),
1163 [&](const std::pair<CKeyID, CPubKey>& used_key) {
1164 return privkey_map.count(used_key.first) > 0;
1165 }) && std::all_of(import_data.key_origins.begin(), import_data.key_origins.end(),
1166 [&](const std::pair<CKeyID, std::pair<CPubKey, KeyOriginInfo>>& entry) {
1167 return privkey_map.count(entry.first) > 0;
1168 });
1169 if (!watch_only && !spendable) {
1170 warnings.push_back("Some private keys are missing, outputs will be considered watchonly. If this is intentional, specify the watchonly flag.");
1171 }
1172 if (watch_only && spendable) {
1173 warnings.push_back("All private keys are provided, outputs will be considered spendable. If this is intentional, do not specify the watchonly flag.");
1174 }
1175
1176 return warnings;
1177}
1178
1179static UniValue ProcessImport(CWallet& wallet, const UniValue& data, const int64_t timestamp) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet)
1180{
1181 UniValue warnings(UniValue::VARR);
1182 UniValue result(UniValue::VOBJ);
1183
1184 try {
1185 const bool internal = data.exists("internal") ? data["internal"].get_bool() : false;
1186 // Internal addresses should not have a label
1187 if (internal && data.exists("label")) {
1188 throw JSONRPCError(RPC_INVALID_PARAMETER, "Internal addresses should not have a label");
1189 }
1190 const std::string& label = data.exists("label") ? data["label"].get_str() : "";
1191 const bool add_keypool = data.exists("keypool") ? data["keypool"].get_bool() : false;
1192
1193 // Add to keypool only works with privkeys disabled
1194 if (add_keypool && !wallet.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
1195 throw JSONRPCError(RPC_INVALID_PARAMETER, "Keys can only be imported to the keypool when private keys are disabled");
1196 }
1197
1198 ImportData import_data;
1199 std::map<CKeyID, CPubKey> pubkey_map;
1200 std::map<CKeyID, CKey> privkey_map;
1201 std::set<CScript> script_pub_keys;
1202 std::vector<CKeyID> ordered_pubkeys;
1203 bool have_solving_data;
1204
1205 if (data.exists("scriptPubKey") && data.exists("desc")) {
1206 throw JSONRPCError(RPC_INVALID_PARAMETER, "Both a descriptor and a scriptPubKey should not be provided.");
1207 } else if (data.exists("scriptPubKey")) {
1208 warnings = ProcessImportLegacy(import_data, pubkey_map, privkey_map, script_pub_keys, have_solving_data, data, ordered_pubkeys);
1209 } else if (data.exists("desc")) {
1210 warnings = ProcessImportDescriptor(import_data, pubkey_map, privkey_map, script_pub_keys, have_solving_data, data, ordered_pubkeys);
1211 } else {
1212 throw JSONRPCError(RPC_INVALID_PARAMETER, "Either a descriptor or scriptPubKey must be provided.");
1213 }
1214
1215 // If private keys are disabled, abort if private keys are being imported
1216 if (wallet.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) && !privkey_map.empty()) {
1217 throw JSONRPCError(RPC_WALLET_ERROR, "Cannot import private keys to a wallet with private keys disabled");
1218 }
1219
1220 // Check whether we have any work to do
1221 for (const CScript& script : script_pub_keys) {
1222 if (wallet.IsMine(script) & ISMINE_SPENDABLE) {
1223 throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already contains the private key for this address or script (\"" + HexStr(script) + "\")");
1224 }
1225 }
1226
1227 // All good, time to import
1228 wallet.MarkDirty();
1229 if (!wallet.ImportScripts(import_data.import_scripts, timestamp)) {
1230 throw JSONRPCError(RPC_WALLET_ERROR, "Error adding script to wallet");
1231 }
1232 if (!wallet.ImportPrivKeys(privkey_map, timestamp)) {
1233 throw JSONRPCError(RPC_WALLET_ERROR, "Error adding key to wallet");
1234 }
1235 if (!wallet.ImportPubKeys(ordered_pubkeys, pubkey_map, import_data.key_origins, add_keypool, internal, timestamp)) {
1236 throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet");
1237 }
1238 if (!wallet.ImportScriptPubKeys(label, script_pub_keys, have_solving_data, !internal, timestamp)) {
1239 throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet");
1240 }
1241
1242 result.pushKV("success", UniValue(true));
1243 } catch (const UniValue& e) {
1244 result.pushKV("success", UniValue(false));
1245 result.pushKV("error", e);
1246 } catch (...) {
1247 result.pushKV("success", UniValue(false));
1248
1249 result.pushKV("error", JSONRPCError(RPC_MISC_ERROR, "Missing required fields"));
1250 }
1251 if (warnings.size()) result.pushKV("warnings", warnings);
1252 return result;
1253}
1254
1255static int64_t GetImportTimestamp(const UniValue& data, int64_t now)
1256{
1257 if (data.exists("timestamp")) {
1258 const UniValue& timestamp = data["timestamp"];
1259 if (timestamp.isNum()) {
1260 return timestamp.get_int64();
1261 } else if (timestamp.isStr() && timestamp.get_str() == "now") {
1262 return now;
1263 }
1264 throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Expected number or \"now\" timestamp value for key. got type %s", uvTypeName(timestamp.type())));
1265 }
1266 throw JSONRPCError(RPC_TYPE_ERROR, "Missing required timestamp field for key");
1267}
1268
1270{
1271 return RPCHelpMan{"importmulti",
1272 "\nImport addresses/scripts (with private or public keys, redeem script (P2SH)), optionally rescanning the blockchain from the earliest creation time of the imported scripts. Requires a new wallet backup.\n"
1273 "If an address/script is imported without all of the private keys required to spend from that address, it will be watchonly. The 'watchonly' option must be set to true in this case or a warning will be returned.\n"
1274 "Conversely, if all the private keys are provided and the address/script is spendable, the watchonly option must be set to false, or a warning will be returned.\n"
1275 "\nNote: This call can take over an hour to complete if rescan is true, during that time, other rpc calls\n"
1276 "may report that the imported keys, addresses or scripts exist but related transactions are still missing.\n"
1277 "Note: Use \"getwalletinfo\" to query the scanning progress.\n",
1278 {
1279 {"requests", RPCArg::Type::ARR, RPCArg::Optional::NO, "Data to be imported",
1280 {
1282 {
1283 {"desc", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "Descriptor to import. If using descriptor, do not also provide address/scriptPubKey, scripts, or pubkeys"},
1284 {"scriptPubKey", RPCArg::Type::STR, RPCArg::Optional::NO, "Type of scriptPubKey (string for script, json for address). Should not be provided if using a descriptor",
1285 /* oneline_description */ "", {"\"<script>\" | { \"address\":\"<address>\" }", "string / json"}
1286 },
1287 {"timestamp", RPCArg::Type::NUM, RPCArg::Optional::NO, "Creation time of the key expressed in " + UNIX_EPOCH_TIME + ",\n"
1288 " or the string \"now\" to substitute the current synced blockchain time. The timestamp of the oldest\n"
1289 " key will determine how far back blockchain rescans need to begin for missing wallet transactions.\n"
1290 " \"now\" can be specified to bypass scanning, for keys which are known to never have been used, and\n"
1291 " 0 can be specified to scan the entire blockchain. Blocks up to 2 hours before the earliest key\n"
1292 " creation time of all keys being imported by the importmulti call will be scanned.",
1293 /* oneline_description */ "", {"timestamp | \"now\"", "integer / string"}
1294 },
1295 {"redeemscript", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "Allowed only if the scriptPubKey is a P2SH or P2SH-P2WSH address/scriptPubKey"},
1296 {"witnessscript", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "Allowed only if the scriptPubKey is a P2SH-P2WSH or P2WSH address/scriptPubKey"},
1297 {"pubkeys", RPCArg::Type::ARR, RPCArg::Default{UniValue::VARR}, "Array of strings giving pubkeys to import. They must occur in P2PKH or P2WPKH scripts. They are not required when the private key is also provided (see the \"keys\" argument).",
1298 {
1300 }
1301 },
1302 {"keys", RPCArg::Type::ARR, RPCArg::Default{UniValue::VARR}, "Array of strings giving private keys to import. The corresponding public keys must occur in the output or redeemscript.",
1303 {
1305 }
1306 },
1307 {"range", RPCArg::Type::RANGE, RPCArg::Optional::OMITTED, "If a ranged descriptor is used, this specifies the end or the range (in the form [begin,end]) to import"},
1308 {"internal", RPCArg::Type::BOOL, RPCArg::Default{false}, "Stating whether matching outputs should be treated as not incoming payments (also known as change)"},
1309 {"watchonly", RPCArg::Type::BOOL, RPCArg::Default{false}, "Stating whether matching outputs should be considered watchonly."},
1310 {"label", RPCArg::Type::STR, RPCArg::Default{""}, "Label to assign to the address, only allowed with internal=false"},
1311 {"keypool", RPCArg::Type::BOOL, RPCArg::Default{false}, "Stating whether imported public keys should be added to the keypool for when users request new addresses. Only allowed when wallet private keys are disabled"},
1312 },
1313 },
1314 },
1315 "\"requests\""},
1317 {
1318 {"rescan", RPCArg::Type::BOOL, RPCArg::Default{true}, "Stating if should rescan the blockchain after all imports"},
1319 },
1320 "\"options\""},
1321 },
1322 RPCResult{
1323 RPCResult::Type::ARR, "", "Response is an array with the same size as the input that has the execution result",
1324 {
1325 {RPCResult::Type::OBJ, "", "",
1326 {
1327 {RPCResult::Type::BOOL, "success", ""},
1328 {RPCResult::Type::ARR, "warnings", /* optional */ true, "",
1329 {
1330 {RPCResult::Type::STR, "", ""},
1331 }},
1332 {RPCResult::Type::OBJ, "error", /* optional */ true, "",
1333 {
1334 {RPCResult::Type::ELISION, "", "JSONRPC error"},
1335 }},
1336 }},
1337 }
1338 },
1340 HelpExampleCli("importmulti", "'[{ \"scriptPubKey\": { \"address\": \"<my address>\" }, \"timestamp\":1455191478 }, "
1341 "{ \"scriptPubKey\": { \"address\": \"<my 2nd address>\" }, \"label\": \"example 2\", \"timestamp\": 1455191480 }]'") +
1342 HelpExampleCli("importmulti", "'[{ \"scriptPubKey\": { \"address\": \"<my address>\" }, \"timestamp\":1455191478 }]' '{ \"rescan\": false}'")
1343 },
1344 [&](const RPCHelpMan& self, const JSONRPCRequest& mainRequest) -> UniValue
1345{
1346 std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(mainRequest);
1347 if (!pwallet) return NullUniValue;
1348
1349 RPCTypeCheck(mainRequest.params, {UniValue::VARR, UniValue::VOBJ});
1350
1351 EnsureLegacyScriptPubKeyMan(*pwallet, true);
1352
1353 const UniValue& requests = mainRequest.params[0];
1354
1355 //Default options
1356 bool fRescan = true;
1357
1358 if (!mainRequest.params[1].isNull()) {
1359 const UniValue& options = mainRequest.params[1];
1360
1361 if (options.exists("rescan")) {
1362 fRescan = options["rescan"].get_bool();
1363 }
1364 }
1365
1366 WalletRescanReserver reserver(*pwallet);
1367 if (fRescan && !reserver.reserve()) {
1368 throw JSONRPCError(RPC_WALLET_ERROR, "Wallet is currently rescanning. Abort existing rescan or wait.");
1369 }
1370
1371 int64_t now = 0;
1372 bool fRunScan = false;
1373 int64_t nLowestTimestamp = 0;
1374 UniValue response(UniValue::VARR);
1375 {
1376 LOCK(pwallet->cs_wallet);
1377 EnsureWalletIsUnlocked(*pwallet);
1378
1379 // Verify all timestamps are present before importing any keys.
1380 CHECK_NONFATAL(pwallet->chain().findBlock(pwallet->GetLastBlockHash(), FoundBlock().time(nLowestTimestamp).mtpTime(now)));
1381 for (const UniValue& data : requests.getValues()) {
1382 GetImportTimestamp(data, now);
1383 }
1384
1385 const int64_t minimumTimestamp = 1;
1386
1387 for (const UniValue& data : requests.getValues()) {
1388 const int64_t timestamp = std::max(GetImportTimestamp(data, now), minimumTimestamp);
1389 const UniValue result = ProcessImport(*pwallet, data, timestamp);
1390 response.push_back(result);
1391
1392 if (!fRescan) {
1393 continue;
1394 }
1395
1396 // If at least one request was successful then allow rescan.
1397 if (result["success"].get_bool()) {
1398 fRunScan = true;
1399 }
1400
1401 // Get the lowest timestamp.
1402 if (timestamp < nLowestTimestamp) {
1403 nLowestTimestamp = timestamp;
1404 }
1405 }
1406 }
1407 if (fRescan && fRunScan && requests.size()) {
1408 int64_t scannedTime = pwallet->RescanFromTime(nLowestTimestamp, reserver, true /* update */);
1409 {
1410 LOCK(pwallet->cs_wallet);
1411 pwallet->ReacceptWalletTransactions();
1412 }
1413
1414 if (pwallet->IsAbortingRescan()) {
1415 throw JSONRPCError(RPC_MISC_ERROR, "Rescan aborted by user.");
1416 }
1417 if (scannedTime > nLowestTimestamp) {
1418 std::vector<UniValue> results = response.getValues();
1419 response.clear();
1420 response.setArray();
1421 size_t i = 0;
1422 for (const UniValue& request : requests.getValues()) {
1423 // If key creation date is within the successfully scanned
1424 // range, or if the import result already has an error set, let
1425 // the result stand unmodified. Otherwise replace the result
1426 // with an error message.
1427 if (scannedTime <= GetImportTimestamp(request, now) || results.at(i).exists("error")) {
1428 response.push_back(results.at(i));
1429 } else {
1431 result.pushKV("success", UniValue(false));
1432 result.pushKV(
1433 "error",
1436 strprintf("Rescan failed for key with creation timestamp %d. There was an error reading a "
1437 "block from time %d, which is after or within %d seconds of key creation, and "
1438 "could contain transactions pertaining to the key. As a result, transactions "
1439 "and coins using this key may not appear in the wallet. This error could be "
1440 "caused by pruning or data corruption (see bitcoind log for details) and could "
1441 "be dealt with by downloading and rescanning the relevant blocks (see -reindex "
1442 "option and rescanblockchain RPC).",
1443 GetImportTimestamp(request, now), scannedTime - TIMESTAMP_WINDOW - 1, TIMESTAMP_WINDOW)));
1444 response.push_back(std::move(result));
1445 }
1446 ++i;
1447 }
1448 }
1449 }
1450
1451 return response;
1452},
1453 };
1454}
1455
1456static UniValue ProcessDescriptorImport(CWallet& wallet, const UniValue& data, const int64_t timestamp) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet)
1457{
1458 UniValue warnings(UniValue::VARR);
1459 UniValue result(UniValue::VOBJ);
1460
1461 try {
1462 if (!data.exists("desc")) {
1463 throw JSONRPCError(RPC_INVALID_PARAMETER, "Descriptor not found.");
1464 }
1465
1466 const std::string& descriptor = data["desc"].get_str();
1467 const bool active = data.exists("active") ? data["active"].get_bool() : false;
1468 const bool internal = data.exists("internal") ? data["internal"].get_bool() : false;
1469 const std::string& label = data.exists("label") ? data["label"].get_str() : "";
1470
1471 // Parse descriptor string
1473 std::string error;
1474 auto parsed_desc = Parse(descriptor, keys, error, /* require_checksum = */ true);
1475 if (!parsed_desc) {
1477 }
1478
1479 // Range check
1480 int64_t range_start = 0, range_end = 1, next_index = 0;
1481 if (!parsed_desc->IsRange() && data.exists("range")) {
1482 throw JSONRPCError(RPC_INVALID_PARAMETER, "Range should not be specified for an un-ranged descriptor");
1483 } else if (parsed_desc->IsRange()) {
1484 if (data.exists("range")) {
1485 auto range = ParseDescriptorRange(data["range"]);
1486 range_start = range.first;
1487 range_end = range.second + 1; // Specified range end is inclusive, but we need range end as exclusive
1488 } else {
1489 warnings.push_back("Range not given, using default keypool range");
1490 range_start = 0;
1491 range_end = gArgs.GetIntArg("-keypool", DEFAULT_KEYPOOL_SIZE);
1492 }
1493 next_index = range_start;
1494
1495 if (data.exists("next_index")) {
1496 next_index = data["next_index"].get_int64();
1497 // bound checks
1498 if (next_index < range_start || next_index >= range_end) {
1499 throw JSONRPCError(RPC_INVALID_PARAMETER, "next_index is out of range");
1500 }
1501 }
1502 }
1503
1504 // Active descriptors must be ranged
1505 if (active && !parsed_desc->IsRange()) {
1506 throw JSONRPCError(RPC_INVALID_PARAMETER, "Active descriptors must be ranged");
1507 }
1508
1509 // Ranged descriptors should not have a label
1510 if (data.exists("range") && data.exists("label")) {
1511 throw JSONRPCError(RPC_INVALID_PARAMETER, "Ranged descriptors should not have a label");
1512 }
1513
1514 // Internal addresses should not have a label either
1515 if (internal && data.exists("label")) {
1516 throw JSONRPCError(RPC_INVALID_PARAMETER, "Internal addresses should not have a label");
1517 }
1518
1519 // Combo descriptor check
1520 if (active && !parsed_desc->IsSingleType()) {
1521 throw JSONRPCError(RPC_WALLET_ERROR, "Combo descriptors cannot be set to active");
1522 }
1523
1524 // If the wallet disabled private keys, abort if private keys exist
1525 if (wallet.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) && !keys.keys.empty()) {
1526 throw JSONRPCError(RPC_WALLET_ERROR, "Cannot import private keys to a wallet with private keys disabled");
1527 }
1528
1529 // Need to ExpandPrivate to check if private keys are available for all pubkeys
1530 FlatSigningProvider expand_keys;
1531 std::vector<CScript> scripts;
1532 if (!parsed_desc->Expand(0, keys, scripts, expand_keys)) {
1533 throw JSONRPCError(RPC_WALLET_ERROR, "Cannot expand descriptor. Probably because of hardened derivations without private keys provided");
1534 }
1535 parsed_desc->ExpandPrivate(0, keys, expand_keys);
1536
1537 // Check if all private keys are provided
1538 bool have_all_privkeys = !expand_keys.keys.empty();
1539 for (const auto& entry : expand_keys.origins) {
1540 const CKeyID& key_id = entry.first;
1541 CKey key;
1542 if (!expand_keys.GetKey(key_id, key)) {
1543 have_all_privkeys = false;
1544 break;
1545 }
1546 }
1547
1548 // Taproot descriptors cannot be imported if Taproot is not yet active.
1549 // Check if this is a Taproot descriptor
1550 CTxDestination dest;
1551 ExtractDestination(scripts[0], dest);
1552 if (std::holds_alternative<WitnessV1Taproot>(dest)) {
1553 // Check if Taproot is active
1554 if (!wallet.chain().isTaprootActive()) {
1555 // Taproot is not active, raise an error
1556 throw JSONRPCError(RPC_WALLET_ERROR, "Cannot import tr() descriptor when Taproot is not active");
1557 }
1558 }
1559
1560 // If private keys are enabled, check some things.
1561 if (!wallet.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
1562 if (keys.keys.empty()) {
1563 throw JSONRPCError(RPC_WALLET_ERROR, "Cannot import descriptor without private keys to a wallet with private keys enabled");
1564 }
1565 if (!have_all_privkeys) {
1566 warnings.push_back("Not all private keys provided. Some wallet functionality may return unexpected errors");
1567 }
1568 }
1569
1570 WalletDescriptor w_desc(std::move(parsed_desc), timestamp, range_start, range_end, next_index);
1571
1572 // Check if the wallet already contains the descriptor
1573 auto existing_spk_manager = wallet.GetDescriptorScriptPubKeyMan(w_desc);
1574 if (existing_spk_manager) {
1575 if (!existing_spk_manager->CanUpdateToWalletDescriptor(w_desc, error)) {
1577 }
1578 }
1579
1580 // Add descriptor to the wallet
1581 auto spk_manager = wallet.AddWalletDescriptor(w_desc, keys, label, internal);
1582 if (spk_manager == nullptr) {
1583 throw JSONRPCError(RPC_WALLET_ERROR, strprintf("Could not add descriptor '%s'", descriptor));
1584 }
1585
1586 // Set descriptor as active if necessary
1587 if (active) {
1588 if (!w_desc.descriptor->GetOutputType()) {
1589 warnings.push_back("Unknown output type, cannot set descriptor to active.");
1590 } else {
1591 wallet.AddActiveScriptPubKeyMan(spk_manager->GetID(), *w_desc.descriptor->GetOutputType(), internal);
1592 }
1593 } else {
1594 if (w_desc.descriptor->GetOutputType()) {
1595 wallet.DeactivateScriptPubKeyMan(spk_manager->GetID(), *w_desc.descriptor->GetOutputType(), internal);
1596 }
1597 }
1598
1599 result.pushKV("success", UniValue(true));
1600 } catch (const UniValue& e) {
1601 result.pushKV("success", UniValue(false));
1602 result.pushKV("error", e);
1603 }
1604 if (warnings.size()) result.pushKV("warnings", warnings);
1605 return result;
1606}
1607
1609{
1610 return RPCHelpMan{"importdescriptors",
1611 "\nImport descriptors. This will trigger a rescan of the blockchain based on the earliest timestamp of all descriptors being imported. Requires a new wallet backup.\n"
1612 "\nNote: This call can take over an hour to complete if using an early timestamp; during that time, other rpc calls\n"
1613 "may report that the imported keys, addresses or scripts exist but related transactions are still missing.\n",
1614 {
1615 {"requests", RPCArg::Type::ARR, RPCArg::Optional::NO, "Data to be imported",
1616 {
1618 {
1619 {"desc", RPCArg::Type::STR, RPCArg::Optional::NO, "Descriptor to import."},
1620 {"active", RPCArg::Type::BOOL, RPCArg::Default{false}, "Set this descriptor to be the active descriptor for the corresponding output type/externality"},
1621 {"range", RPCArg::Type::RANGE, RPCArg::Optional::OMITTED, "If a ranged descriptor is used, this specifies the end or the range (in the form [begin,end]) to import"},
1622 {"next_index", RPCArg::Type::NUM, RPCArg::Optional::OMITTED, "If a ranged descriptor is set to active, this specifies the next index to generate addresses from"},
1623 {"timestamp", RPCArg::Type::NUM, RPCArg::Optional::NO, "Time from which to start rescanning the blockchain for this descriptor, in " + UNIX_EPOCH_TIME + "\n"
1624 " Use the string \"now\" to substitute the current synced blockchain time.\n"
1625 " \"now\" can be specified to bypass scanning, for outputs which are known to never have been used, and\n"
1626 " 0 can be specified to scan the entire blockchain. Blocks up to 2 hours before the earliest timestamp\n"
1627 " of all descriptors being imported will be scanned.",
1628 /* oneline_description */ "", {"timestamp | \"now\"", "integer / string"}
1629 },
1630 {"internal", RPCArg::Type::BOOL, RPCArg::Default{false}, "Whether matching outputs should be treated as not incoming payments (e.g. change)"},
1631 {"label", RPCArg::Type::STR, RPCArg::Default{""}, "Label to assign to the address, only allowed with internal=false"},
1632 },
1633 },
1634 },
1635 "\"requests\""},
1636 },
1637 RPCResult{
1638 RPCResult::Type::ARR, "", "Response is an array with the same size as the input that has the execution result",
1639 {
1640 {RPCResult::Type::OBJ, "", "",
1641 {
1642 {RPCResult::Type::BOOL, "success", ""},
1643 {RPCResult::Type::ARR, "warnings", /* optional */ true, "",
1644 {
1645 {RPCResult::Type::STR, "", ""},
1646 }},
1647 {RPCResult::Type::OBJ, "error", /* optional */ true, "",
1648 {
1649 {RPCResult::Type::ELISION, "", "JSONRPC error"},
1650 }},
1651 }},
1652 }
1653 },
1655 HelpExampleCli("importdescriptors", "'[{ \"desc\": \"<my descriptor>\", \"timestamp\":1455191478, \"internal\": true }, "
1656 "{ \"desc\": \"<my desccriptor 2>\", \"label\": \"example 2\", \"timestamp\": 1455191480 }]'") +
1657 HelpExampleCli("importdescriptors", "'[{ \"desc\": \"<my descriptor>\", \"timestamp\":1455191478, \"active\": true, \"range\": [0,100], \"label\": \"<my bech32 wallet>\" }]'")
1658 },
1659 [&](const RPCHelpMan& self, const JSONRPCRequest& main_request) -> UniValue
1660{
1661 std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(main_request);
1662 if (!pwallet) return NullUniValue;
1663
1664 // Make sure wallet is a descriptor wallet
1665 if (!pwallet->IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS)) {
1666 throw JSONRPCError(RPC_WALLET_ERROR, "importdescriptors is not available for non-descriptor wallets");
1667 }
1668
1669 RPCTypeCheck(main_request.params, {UniValue::VARR, UniValue::VOBJ});
1670
1671 WalletRescanReserver reserver(*pwallet);
1672 if (!reserver.reserve()) {
1673 throw JSONRPCError(RPC_WALLET_ERROR, "Wallet is currently rescanning. Abort existing rescan or wait.");
1674 }
1675
1676 const UniValue& requests = main_request.params[0];
1677 const int64_t minimum_timestamp = 1;
1678 int64_t now = 0;
1679 int64_t lowest_timestamp = 0;
1680 bool rescan = false;
1681 UniValue response(UniValue::VARR);
1682 {
1683 LOCK(pwallet->cs_wallet);
1684 EnsureWalletIsUnlocked(*pwallet);
1685
1686 CHECK_NONFATAL(pwallet->chain().findBlock(pwallet->GetLastBlockHash(), FoundBlock().time(lowest_timestamp).mtpTime(now)));
1687
1688 // Get all timestamps and extract the lowest timestamp
1689 for (const UniValue& request : requests.getValues()) {
1690 // This throws an error if "timestamp" doesn't exist
1691 const int64_t timestamp = std::max(GetImportTimestamp(request, now), minimum_timestamp);
1692 const UniValue result = ProcessDescriptorImport(*pwallet, request, timestamp);
1693 response.push_back(result);
1694
1695 if (lowest_timestamp > timestamp ) {
1696 lowest_timestamp = timestamp;
1697 }
1698
1699 // If we know the chain tip, and at least one request was successful then allow rescan
1700 if (!rescan && result["success"].get_bool()) {
1701 rescan = true;
1702 }
1703 }
1704 pwallet->ConnectScriptPubKeyManNotifiers();
1705 }
1706
1707 // Rescan the blockchain using the lowest timestamp
1708 if (rescan) {
1709 int64_t scanned_time = pwallet->RescanFromTime(lowest_timestamp, reserver, true /* update */);
1710 {
1711 LOCK(pwallet->cs_wallet);
1712 pwallet->ReacceptWalletTransactions();
1713 }
1714
1715 if (pwallet->IsAbortingRescan()) {
1716 throw JSONRPCError(RPC_MISC_ERROR, "Rescan aborted by user.");
1717 }
1718
1719 if (scanned_time > lowest_timestamp) {
1720 std::vector<UniValue> results = response.getValues();
1721 response.clear();
1722 response.setArray();
1723
1724 // Compose the response
1725 for (unsigned int i = 0; i < requests.size(); ++i) {
1726 const UniValue& request = requests.getValues().at(i);
1727
1728 // If the descriptor timestamp is within the successfully scanned
1729 // range, or if the import result already has an error set, let
1730 // the result stand unmodified. Otherwise replace the result
1731 // with an error message.
1732 if (scanned_time <= GetImportTimestamp(request, now) || results.at(i).exists("error")) {
1733 response.push_back(results.at(i));
1734 } else {
1736 result.pushKV("success", UniValue(false));
1737 result.pushKV(
1738 "error",
1741 strprintf("Rescan failed for descriptor with timestamp %d. There was an error reading a "
1742 "block from time %d, which is after or within %d seconds of key creation, and "
1743 "could contain transactions pertaining to the desc. As a result, transactions "
1744 "and coins using this desc may not appear in the wallet. This error could be "
1745 "caused by pruning or data corruption (see bitcoind log for details) and could "
1746 "be dealt with by downloading and rescanning the relevant blocks (see -reindex "
1747 "option and rescanblockchain RPC).",
1748 GetImportTimestamp(request, now), scanned_time - TIMESTAMP_WINDOW - 1, TIMESTAMP_WINDOW)));
1749 response.push_back(std::move(result));
1750 }
1751 }
1752 }
1753 }
1754
1755 return response;
1756},
1757 };
1758}
1759
1761{
1762 return RPCHelpMan{
1763 "listdescriptors",
1764 "\nList descriptors imported into a descriptor-enabled wallet.\n",
1765 {
1766 {"private", RPCArg::Type::BOOL, RPCArg::Default{false}, "Show private descriptors."}
1767 },
1769 {RPCResult::Type::STR, "wallet_name", "Name of wallet this operation was performed on"},
1770 {RPCResult::Type::ARR, "descriptors", "Array of descriptor objects",
1771 {
1772 {RPCResult::Type::OBJ, "", "", {
1773 {RPCResult::Type::STR, "desc", "Descriptor string representation"},
1774 {RPCResult::Type::NUM, "timestamp", "The creation time of the descriptor"},
1775 {RPCResult::Type::BOOL, "active", "Activeness flag"},
1776 {RPCResult::Type::BOOL, "internal", true, "Whether this is an internal or external descriptor; defined only for active descriptors"},
1777 {RPCResult::Type::ARR_FIXED, "range", true, "Defined only for ranged descriptors", {
1778 {RPCResult::Type::NUM, "", "Range start inclusive"},
1779 {RPCResult::Type::NUM, "", "Range end inclusive"},
1780 }},
1781 {RPCResult::Type::NUM, "next", true, "The next index to generate addresses from; defined only for ranged descriptors"},
1782 }},
1783 }}
1784 }},
1786 HelpExampleCli("listdescriptors", "") + HelpExampleRpc("listdescriptors", "")
1787 + HelpExampleCli("listdescriptors", "true") + HelpExampleRpc("listdescriptors", "true")
1788 },
1789 [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
1790{
1791 const std::shared_ptr<const CWallet> wallet = GetWalletForJSONRPCRequest(request);
1792 if (!wallet) return NullUniValue;
1793
1794 if (!wallet->IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS)) {
1795 throw JSONRPCError(RPC_WALLET_ERROR, "listdescriptors is not available for non-descriptor wallets");
1796 }
1797
1798 const bool priv = !request.params[0].isNull() && request.params[0].get_bool();
1799 if (priv) {
1801 }
1802
1803 LOCK(wallet->cs_wallet);
1804
1805 UniValue descriptors(UniValue::VARR);
1806 const auto active_spk_mans = wallet->GetActiveScriptPubKeyMans();
1807 for (const auto& spk_man : wallet->GetAllScriptPubKeyMans()) {
1808 const auto desc_spk_man = dynamic_cast<DescriptorScriptPubKeyMan*>(spk_man);
1809 if (!desc_spk_man) {
1810 throw JSONRPCError(RPC_WALLET_ERROR, "Unexpected ScriptPubKey manager type.");
1811 }
1813 LOCK(desc_spk_man->cs_desc_man);
1814 const auto& wallet_descriptor = desc_spk_man->GetWalletDescriptor();
1815 std::string descriptor;
1816
1817 if (!desc_spk_man->GetDescriptorString(descriptor, priv)) {
1818 throw JSONRPCError(RPC_WALLET_ERROR, "Can't get descriptor string.");
1819 }
1820 spk.pushKV("desc", descriptor);
1821 spk.pushKV("timestamp", wallet_descriptor.creation_time);
1822 const bool active = active_spk_mans.count(desc_spk_man) != 0;
1823 spk.pushKV("active", active);
1824 const auto& type = wallet_descriptor.descriptor->GetOutputType();
1825 if (active && type) {
1826 spk.pushKV("internal", wallet->GetScriptPubKeyMan(*type, true) == desc_spk_man);
1827 }
1828 if (wallet_descriptor.descriptor->IsRange()) {
1829 UniValue range(UniValue::VARR);
1830 range.push_back(wallet_descriptor.range_start);
1831 range.push_back(wallet_descriptor.range_end - 1);
1832 spk.pushKV("range", range);
1833 spk.pushKV("next", wallet_descriptor.next_index);
1834 }
1835 descriptors.push_back(spk);
1836 }
1837
1838 UniValue response(UniValue::VOBJ);
1839 response.pushKV("wallet_name", wallet->GetName());
1840 response.pushKV("descriptors", descriptors);
1841
1842 return response;
1843},
1844 };
1845}
std::string WriteHDKeypath(const std::vector< uint32_t > &keypath)
Write HD keypaths as strings.
Definition: bip32.cpp:63
#define PACKAGE_NAME
static constexpr int64_t TIMESTAMP_WINDOW
Timestamp window used as a grace period by code that compares external timestamps (such as timestamps...
Definition: chain.h:30
#define CHECK_NONFATAL(condition)
Throw a NonFatalCheckError when the condition evaluates to false.
Definition: check.h:32
int64_t GetIntArg(const std::string &strArg, int64_t nDefault) const
Return integer argument or default value.
Definition: system.cpp:596
uint256 hashMerkleRoot
Definition: block.h:26
uint256 GetHash() const
Definition: block.cpp:11
Double ended buffer combining vector and stream-like interfaces.
Definition: streams.h:205
CKeyID seed_id
seed hash160
Definition: walletdb.h:92
An encapsulated private key.
Definition: key.h:27
bool IsValid() const
Check whether this private key is valid.
Definition: key.h:93
CPubKey GetPubKey() const
Compute the public key from a private key.
Definition: key.cpp:187
bool VerifyPubKey(const CPubKey &vchPubKey) const
Verify thoroughly whether a private key and a public key match.
Definition: key.cpp:235
A reference to a CKey: the Hash160 of its serialized public key.
Definition: pubkey.h:23
Used to relay blocks as header + vector<merkle branch> to filtered nodes.
Definition: merkleblock.h:125
CBlockHeader header
Public only for unit testing.
Definition: merkleblock.h:128
CPartialMerkleTree txn
Definition: merkleblock.h:129
uint256 ExtractMatches(std::vector< uint256 > &vMatch, std::vector< unsigned int > &vnIndex)
extract the matching txid's represented by this partial merkle tree and their respective indices with...
An encapsulated public key.
Definition: pubkey.h:33
bool IsCompressed() const
Check whether this is a compressed public key.
Definition: pubkey.h:194
CKeyID GetID() const
Get the KeyID of this public key (hash of its serialization)
Definition: pubkey.h:160
bool IsFullyValid() const
fully validate whether this is a valid public key (more expensive than IsValid())
Definition: pubkey.cpp:292
A hasher class for RIPEMD-160.
Definition: ripemd160.h:13
CRIPEMD160 & Write(const unsigned char *data, size_t len)
Definition: ripemd160.cpp:247
void Finalize(unsigned char hash[OUTPUT_SIZE])
Definition: ripemd160.cpp:273
Serialized script, used inside transaction inputs and outputs.
Definition: script.h:406
A reference to a CScript: the Hash160 of its serialization (see script.h)
Definition: standard.h:26
A CWallet maintains a set of transactions and balances, and provides the ability to create new transa...
Definition: wallet.h:229
virtual bool GetCScript(const CScriptID &hash, CScript &redeemScriptOut) const override
virtual std::set< CScriptID > GetCScripts() const
RecursiveMutex cs_KeyStore
const CHDChain & GetHDChain() const
bool GetKey(const CKeyID &address, CKey &keyOut) const override
const std::map< CKeyID, int64_t > & GetAllReserveKeys() const
bool setArray()
Definition: univalue.cpp:94
const std::string & get_str() const
enum VType getType() const
Definition: univalue.h:62
@ VOBJ
Definition: univalue.h:19
@ VSTR
Definition: univalue.h:19
@ VARR
Definition: univalue.h:19
int64_t get_int64() const
void clear()
Definition: univalue.cpp:15
size_t size() const
Definition: univalue.h:66
enum VType type() const
Definition: univalue.h:179
const std::vector< UniValue > & getValues() const
bool isStr() const
Definition: univalue.h:79
bool push_back(const UniValue &val)
Definition: univalue.cpp:108
const UniValue & get_array() const
bool exists(const std::string &key) const
Definition: univalue.h:73
bool pushKV(const std::string &key, const UniValue &val)
Definition: univalue.cpp:133
bool isNum() const
Definition: univalue.h:80
bool get_bool() const
Descriptor with some wallet metadata.
Definition: walletutil.h:76
std::shared_ptr< Descriptor > descriptor
Definition: walletutil.h:78
RAII object to check and reserve a wallet rescan.
Definition: wallet.h:890
static constexpr unsigned int size()
Definition: uint256.h:78
unsigned char * begin()
Definition: uint256.h:58
bool IsNull() const
Definition: uint256.h:31
Path class wrapper to prepare application code for transition from boost::filesystem library to std::...
Definition: fs.h:34
std::string u8string() const
Definition: fs.h:59
Helper for findBlock to selectively return pieces of block data.
Definition: chain.h:42
iterator insert(iterator pos, const T &value)
Definition: prevector.h:347
160-bit opaque blob.
Definition: uint256.h:113
256-bit opaque blob.
Definition: uint256.h:124
std::string FormatFullVersion()
bool DecodeHexTx(CMutableTransaction &tx, const std::string &hex_tx, bool try_no_witness=false, bool try_witness=true)
Definition: core_read.cpp:189
static NodeId id
std::unique_ptr< Descriptor > Parse(const std::string &descriptor, FlatSigningProvider &out, std::string &error, bool require_checksum)
Parse a descriptor string.
@ WITNESS_V0
Witness v0 (P2WPKH and P2WSH); see BIP 141.
@ ISMINE_SPENDABLE
Definition: ismine.h:42
std::string EncodeExtKey(const CExtKey &key)
Definition: key_io.cpp:245
std::string EncodeSecret(const CKey &key)
Definition: key_io.cpp:196
std::string EncodeDestination(const CTxDestination &dest)
Definition: key_io.cpp:256
CKey DecodeSecret(const std::string &str)
Definition: key_io.cpp:178
CTxDestination DecodeDestination(const std::string &str, std::string &error_msg)
Definition: key_io.cpp:261
static path u8path(const std::string &string)
Definition: fs.h:63
static bool exists(const path &p)
Definition: fs.h:77
fs::ofstream ofstream
Definition: fs.h:225
fs::ifstream ifstream
Definition: fs.h:224
CTxDestination GetDestinationForKey(const CPubKey &key, OutputType type)
Get a destination of the requested type (if possible) to the specified key.
Definition: outputtype.cpp:49
std::optional< OutputType > OutputTypeFromDestination(const CTxDestination &dest)
Get the OutputType for a CTxDestination.
Definition: outputtype.cpp:109
std::vector< CTxDestination > GetAllDestinationsForKey(const CPubKey &key)
Get all destinations (potentially) supported by the wallet for the given key.
Definition: outputtype.cpp:69
static CTransactionRef MakeTransactionRef(Tx &&txIn)
Definition: transaction.h:387
std::shared_ptr< const CTransaction > CTransactionRef
Definition: transaction.h:386
UniValue JSONRPCError(int code, const std::string &message)
Definition: request.cpp:51
@ RPC_MISC_ERROR
General application defined errors.
Definition: protocol.h:39
@ RPC_TYPE_ERROR
Unexpected type was passed as parameter.
Definition: protocol.h:40
@ RPC_INVALID_PARAMETER
Invalid, missing or duplicate parameter.
Definition: protocol.h:43
@ RPC_WALLET_ERROR
Wallet errors.
Definition: protocol.h:71
@ RPC_DESERIALIZATION_ERROR
Error parsing or validating structure in raw format.
Definition: protocol.h:45
@ RPC_INVALID_ADDRESS_OR_KEY
Invalid address or key.
Definition: protocol.h:41
std::vector< unsigned char > ParseHexV(const UniValue &v, std::string strName)
Definition: util.cpp:103
void RPCTypeCheck(const UniValue &params, const std::list< UniValueType > &typesExpected, bool fAllowNull)
Type-check arguments; throws JSONRPCError if wrong type given.
Definition: util.cpp:24
std::pair< int64_t, int64_t > ParseDescriptorRange(const UniValue &value)
Parse a JSON range specified as int64, or [int64, int64].
Definition: util.cpp:976
std::string HelpExampleCli(const std::string &methodname, const std::string &args)
Definition: util.cpp:156
std::string HelpExampleRpc(const std::string &methodname, const std::string &args)
Definition: util.cpp:174
const std::string UNIX_EPOCH_TIME
String used to describe UNIX epoch time in documentation, factored out to a constant for consistency.
Definition: util.cpp:21
uint256 ParseHashV(const UniValue &v, std::string strName)
Utilities: convert hex-encoded Values (throws error if not hex).
Definition: util.cpp:90
RPCHelpMan importprivkey()
Definition: rpcdump.cpp:94
static const int64_t TIMESTAMP_MIN
Definition: rpcdump.cpp:82
RPCHelpMan importdescriptors()
Definition: rpcdump.cpp:1608
RPCHelpMan listdescriptors()
Definition: rpcdump.cpp:1760
static void RescanWallet(CWallet &wallet, const WalletRescanReserver &reserver, int64_t time_begin=TIMESTAMP_MIN, bool update=true)
Definition: rpcdump.cpp:84
RPCHelpMan importmulti()
Definition: rpcdump.cpp:1269
static std::string RecurseImportData(const CScript &script, ImportData &import_data, const ScriptContext script_ctx)
Definition: rpcdump.cpp:875
RPCHelpMan importaddress()
Definition: rpcdump.cpp:223
static UniValue ProcessImport(CWallet &wallet, const UniValue &data, const int64_t timestamp) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet)
Definition: rpcdump.cpp:1179
RPCHelpMan importwallet()
Definition: rpcdump.cpp:509
static bool GetWalletAddressesForKey(LegacyScriptPubKeyMan *spk_man, const CWallet &wallet, const CKeyID &keyid, std::string &strAddr, std::string &strLabel) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet)
Definition: rpcdump.cpp:60
RPCHelpMan dumpwallet()
Definition: rpcdump.cpp:712
RPCHelpMan importpubkey()
Definition: rpcdump.cpp:423
static UniValue ProcessImportLegacy(ImportData &import_data, std::map< CKeyID, CPubKey > &pubkey_map, std::map< CKeyID, CKey > &privkey_map, std::set< CScript > &script_pub_keys, bool &have_solving_data, const UniValue &data, std::vector< CKeyID > &ordered_pubkeys)
Definition: rpcdump.cpp:943
static std::string EncodeDumpString(const std::string &str)
Definition: rpcdump.cpp:34
static std::string DecodeDumpString(const std::string &str)
Definition: rpcdump.cpp:46
RPCHelpMan importprunedfunds()
Definition: rpcdump.cpp:327
static UniValue ProcessImportDescriptor(ImportData &import_data, std::map< CKeyID, CPubKey > &pubkey_map, std::map< CKeyID, CKey > &privkey_map, std::set< CScript > &script_pub_keys, bool &have_solving_data, const UniValue &data, std::vector< CKeyID > &ordered_pubkeys)
Definition: rpcdump.cpp:1090
RPCHelpMan abortrescan()
Definition: rpcdump.cpp:196
ScriptContext
Definition: rpcdump.cpp:867
@ P2SH
P2SH redeemScript.
@ TOP
Top-level scriptPubKey.
@ WITNESS_V0
P2WSH witnessScript.
RPCHelpMan dumpprivkey()
Definition: rpcdump.cpp:666
static UniValue ProcessDescriptorImport(CWallet &wallet, const UniValue &data, const int64_t timestamp) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet)
Definition: rpcdump.cpp:1456
RPCHelpMan removeprunedfunds()
Definition: rpcdump.cpp:385
static int64_t GetImportTimestamp(const UniValue &data, int64_t now)
Definition: rpcdump.cpp:1255
std::shared_ptr< CWallet > GetWalletForJSONRPCRequest(const JSONRPCRequest &request)
Figures out what wallet, if any, to use for a JSONRPCRequest.
Definition: rpcwallet.cpp:99
void EnsureWalletIsUnlocked(const CWallet &wallet)
Definition: rpcwallet.cpp:124
LegacyScriptPubKeyMan & EnsureLegacyScriptPubKeyMan(CWallet &wallet, bool also_create)
Definition: rpcwallet.cpp:141
static const unsigned int DEFAULT_KEYPOOL_SIZE
Default for -keypool.
@ SER_NETWORK
Definition: serialize.h:138
CKeyID GetKeyForDestination(const SigningProvider &store, const CTxDestination &dest)
Return the CKeyID of the key involved in a script (if there is a unique one).
TxoutType Solver(const CScript &scriptPubKey, std::vector< std::vector< unsigned char > > &vSolutionsRet)
Parse a scriptPubKey and identify script type for standard scripts.
Definition: standard.cpp:144
bool ExtractDestination(const CScript &scriptPubKey, CTxDestination &addressRet)
Parse a standard scriptPubKey for the destination address.
Definition: standard.cpp:213
bool IsValidDestination(const CTxDestination &dest)
Check whether a CTxDestination is a CNoDestination.
Definition: standard.cpp:332
CScript GetScriptForDestination(const CTxDestination &dest)
Generate a Bitcoin scriptPubKey for the given CTxDestination.
Definition: standard.cpp:310
TxoutType
Definition: standard.h:59
@ WITNESS_V1_TAPROOT
@ WITNESS_UNKNOWN
Only for Witness versions not already defined above.
@ WITNESS_V0_SCRIPTHASH
@ NULL_DATA
unspendable OP_RETURN script that carries data
@ WITNESS_V0_KEYHASH
std::variant< CNoDestination, PKHash, ScriptHash, WitnessV0ScriptHash, WitnessV0KeyHash, WitnessV1Taproot, WitnessUnknown > CTxDestination
A txout script template with a specific destination.
Definition: standard.h:157
std::string HexStr(const Span< const uint8_t > s)
Convert a span of bytes to a lower-case hexadecimal string.
std::vector< unsigned char > ParseHex(const char *psz)
bool IsHex(const std::string &str)
Definition: key.h:161
void SetSeed(Span< const uint8_t > seed)
Definition: key.cpp:322
A mutable version of CTransaction.
Definition: transaction.h:345
uint256 GetHash() const
Compute the hash of this CMutableTransaction.
Definition: transaction.cpp:63
Confirmation includes tx status and a triplet of {block height/block hash/tx index in block} at which...
Definition: transaction.h:160
std::map< CKeyID, std::pair< CPubKey, KeyOriginInfo > > origins
bool GetKey(const CKeyID &keyid, CKey &key) const override
std::map< CKeyID, CPubKey > pubkeys
std::map< CKeyID, CKey > keys
std::map< CScriptID, CScript > scripts
std::unique_ptr< CScript > redeemscript
Provided redeemScript; will be moved to import_scripts if relevant.
Definition: rpcdump.cpp:857
std::map< CKeyID, bool > used_keys
Import these private keys if available (the value indicates whether if the key is required for solvab...
Definition: rpcdump.cpp:862
std::set< CScript > import_scripts
Definition: rpcdump.cpp:861
std::unique_ptr< CScript > witnessscript
Provided witnessScript; will be moved to import_scripts if relevant.
Definition: rpcdump.cpp:858
std::map< CKeyID, std::pair< CPubKey, KeyOriginInfo > > key_origins
Definition: rpcdump.cpp:863
@ RANGE
Special type that is a NUM or [NUM,NUM].
@ STR_HEX
Special type that is a STR with only hex chars.
std::string DefaultHint
Definition: util.h:155
@ OMITTED_NAMED_ARG
Optional arg that is a named argument and has a default value of null.
@ OMITTED
Optional argument with default value omitted because they are implicitly clear.
@ NO
Required arg.
@ ELISION
Special type to denote elision (...)
@ ARR_FIXED
Special array that has a fixed number of entries.
#define LOCK2(cs1, cs2)
Definition: sync.h:227
#define LOCK(cs)
Definition: sync.h:226
bool error(const char *fmt, const Args &... args)
Definition: system.h:49
#define EXCLUSIVE_LOCKS_REQUIRED(...)
Definition: threadsafety.h:49
int64_t ParseISO8601DateTime(const std::string &str)
Definition: time.cpp:158
int64_t GetTime()
DEPRECATED Use either GetTimeSeconds (not mockable) or GetTime<T> (mockable)
Definition: time.cpp:26
std::string FormatISO8601DateTime(int64_t nTime)
ISO 8601 formatting is preferred.
Definition: time.cpp:132
#define strprintf
Format arguments and return the string or write to given std::ostream (see tinyformat::format doc for...
Definition: tinyformat.h:1164
bilingual_str _(const char *psz)
Translation function.
Definition: translation.h:63
const UniValue NullUniValue
Definition: univalue.cpp:13
const char * uvTypeName(UniValue::VType t)
Definition: univalue.cpp:221
ArgsManager gArgs
Definition: system.cpp:85
static const int PROTOCOL_VERSION
network protocol versioning
Definition: version.h:12
@ WALLET_FLAG_DISABLE_PRIVATE_KEYS
Definition: walletutil.h:50
@ WALLET_FLAG_DESCRIPTORS
Indicate that this wallet supports DescriptorScriptPubKeyMan.
Definition: walletutil.h:65