Bitcoin ABC 0.32.12
P2P Digital Currency
backup.cpp
Go to the documentation of this file.
1// Copyright (c) 2009-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 <wallet/rpc/backup.h>
6
7#include <chain.h>
8#include <common/args.h>
9#include <config.h>
10#include <core_io.h>
11#include <interfaces/chain.h>
12#include <key_io.h>
13#include <merkleblock.h>
14#include <rpc/server.h>
15#include <rpc/util.h>
16#include <script/descriptor.h>
17#include <script/script.h>
18#include <script/standard.h>
19#include <sync.h>
20#include <util/bip32.h>
21#include <util/fs.h>
22#include <util/fs_helpers.h>
23#include <util/time.h>
24#include <util/translation.h>
25#include <wallet/rpc/util.h>
26#include <wallet/rpcwallet.h>
27#include <wallet/spend.h>
28#include <wallet/wallet.h>
29
30#include <algorithm>
31#include <cstdint>
32#include <fstream>
33#include <string>
34#include <tuple>
35#include <utility>
36#include <vector>
37
39using util::Join;
41
42static std::string EncodeDumpString(const std::string &str) {
43 std::stringstream ret;
44 for (const uint8_t c : str) {
45 if (c <= 32 || c >= 128 || c == '%') {
46 ret << '%' << HexStr({&c, 1});
47 } else {
48 ret << c;
49 }
50 }
51 return ret.str();
52}
53
54static std::string DecodeDumpString(const std::string &str) {
55 std::stringstream ret;
56 for (unsigned int pos = 0; pos < str.length(); pos++) {
57 uint8_t c = str[pos];
58 if (c == '%' && pos + 2 < str.length()) {
59 c = (((str[pos + 1] >> 6) * 9 + ((str[pos + 1] - '0') & 15)) << 4) |
60 ((str[pos + 2] >> 6) * 9 + ((str[pos + 2] - '0') & 15));
61 pos += 2;
62 }
63 ret << c;
64 }
65 return ret.str();
66}
67
68static bool
70 const CWallet *const pwallet, const CKeyID &keyid,
71 std::string &strAddr, std::string &strLabel)
72 EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet) {
73 bool fLabelFound = false;
74 CKey key;
75 spk_man->GetKey(keyid, key);
76 for (const auto &dest : GetAllDestinationsForKey(key.GetPubKey())) {
77 const auto *address_book_entry = pwallet->FindAddressBookEntry(dest);
78 if (address_book_entry) {
79 if (!strAddr.empty()) {
80 strAddr += ",";
81 }
82 strAddr += EncodeDestination(dest, config);
83 strLabel = EncodeDumpString(address_book_entry->GetLabel());
84 fLabelFound = true;
85 }
86 }
87 if (!fLabelFound) {
88 strAddr = EncodeDestination(
90 pwallet->m_default_address_type),
91 config);
92 }
93 return fLabelFound;
94}
95
96static const int64_t TIMESTAMP_MIN = 0;
97
98static void RescanWallet(CWallet &wallet, const WalletRescanReserver &reserver,
99 int64_t time_begin = TIMESTAMP_MIN,
100 bool update = true) {
101 int64_t scanned_time = wallet.RescanFromTime(time_begin, reserver, update);
102 if (wallet.IsAbortingRescan()) {
103 throw JSONRPCError(RPC_MISC_ERROR, "Rescan aborted by user.");
104 } else if (scanned_time > time_begin) {
106 "Rescan was unable to fully rescan the blockchain. "
107 "Some transactions may be missing.");
108 }
109}
110
111static void EnsureBlockDataFromTime(const CWallet &wallet, int64_t timestamp) {
112 auto &chain{wallet.chain()};
113 if (!chain.havePruned()) {
114 return;
115 }
116
117 int height{0};
118 const bool found{chain.findFirstBlockWithTimeAndHeight(
119 timestamp - TIMESTAMP_WINDOW, 0, FoundBlock().height(height))};
120
121 BlockHash tip_hash{
122 WITH_LOCK(wallet.cs_wallet, return wallet.GetLastBlockHash())};
123 if (found && !chain.hasBlocks(tip_hash, height)) {
124 throw JSONRPCError(
126 strprintf(
127 "Pruned blocks from height %d required to import keys. Use RPC "
128 "call getblockchaininfo to determine your pruned height.",
129 height));
130 }
131}
132
134 return RPCHelpMan{
135 "importprivkey",
136 "Adds a private key (as returned by dumpprivkey) to your wallet. "
137 "Requires a new wallet backup.\n"
138 "Hint: use importmulti to import more than one private key.\n"
139 "\nNote: This call can take minutes to complete if rescan is true, "
140 "during that time, other rpc calls\n"
141 "may report that the imported key exists but related transactions are "
142 "still missing, leading to temporarily incorrect/bogus balances and "
143 "unspent outputs until rescan completes.\n"
144 "Note: Use \"getwalletinfo\" to query the scanning progress.\n"
145 "Note: This command is only compatible with legacy wallets. Use "
146 "\"importdescriptors\" with \"combo(X)\" for descriptor wallets.\n",
147 {
149 "The private key (see dumpprivkey)"},
150 {"label", RPCArg::Type::STR,
152 "current label if address exists, otherwise \"\""},
153 "An optional label"},
154 {"rescan", RPCArg::Type::BOOL, RPCArg::Default{true},
155 "Rescan the wallet for transactions"},
156 },
159 "\nDump a private key\n" +
160 HelpExampleCli("dumpprivkey", "\"myaddress\"") +
161 "\nImport the private key with rescan\n" +
162 HelpExampleCli("importprivkey", "\"mykey\"") +
163 "\nImport using a label and without rescan\n" +
164 HelpExampleCli("importprivkey", "\"mykey\" \"testing\" false") +
165 "\nImport using default blank label and without rescan\n" +
166 HelpExampleCli("importprivkey", "\"mykey\" \"\" false") +
167 "\nAs a JSON-RPC call\n" +
168 HelpExampleRpc("importprivkey", "\"mykey\", \"testing\", false")},
169 [&](const RPCHelpMan &self, const Config &config,
170 const JSONRPCRequest &request) -> UniValue {
171 std::shared_ptr<CWallet> const wallet =
173 if (!wallet) {
174 return NullUniValue;
175 }
176 CWallet *const pwallet = wallet.get();
177
179 throw JSONRPCError(
181 "Cannot import private keys to a wallet with "
182 "private keys disabled");
183 }
184
186
187 WalletRescanReserver reserver(*pwallet);
188 bool fRescan = true;
189 {
190 LOCK(pwallet->cs_wallet);
191
192 EnsureWalletIsUnlocked(pwallet);
193
194 std::string strSecret = request.params[0].get_str();
195 std::string strLabel = "";
196 if (!request.params[1].isNull()) {
197 strLabel = request.params[1].get_str();
198 }
199
200 // Whether to perform rescan after import
201 if (!request.params[2].isNull()) {
202 fRescan = request.params[2].get_bool();
203 }
204
205 if (fRescan && pwallet->chain().havePruned()) {
206 // Exit early and print an error.
207 // If a block is pruned after this check, we will import the
208 // key(s), but fail the rescan with a generic error.
209 throw JSONRPCError(
211 "Rescan is disabled when blocks are pruned");
212 }
213
214 if (fRescan && !reserver.reserve()) {
215 throw JSONRPCError(
217 "Wallet is currently rescanning. Abort existing "
218 "rescan or wait.");
219 }
220
221 CKey key = DecodeSecret(strSecret);
222 if (!key.IsValid()) {
224 "Invalid private key encoding");
225 }
226
227 CPubKey pubkey = key.GetPubKey();
228 CHECK_NONFATAL(key.VerifyPubKey(pubkey));
229 CKeyID vchAddress = pubkey.GetID();
230 {
231 pwallet->MarkDirty();
232
233 // We don't know which corresponding address will be used;
234 // label all new addresses, and label existing addresses if
235 // a label was passed.
236 for (const auto &dest : GetAllDestinationsForKey(pubkey)) {
237 if (!request.params[1].isNull() ||
238 !pwallet->FindAddressBookEntry(dest)) {
239 pwallet->SetAddressBook(dest, strLabel, "receive");
240 }
241 }
242
243 // Use timestamp of 1 to scan the whole chain
244 if (!pwallet->ImportPrivKeys({{vchAddress, key}}, 1)) {
246 "Error adding key to wallet");
247 }
248 }
249 }
250 if (fRescan) {
251 RescanWallet(*pwallet, reserver);
252 }
253
254 return NullUniValue;
255 },
256 };
257}
258
260 return RPCHelpMan{
261 "abortrescan",
262 "Stops current wallet rescan triggered by an RPC call, e.g. by an "
263 "importprivkey call.\n"
264 "Note: Use \"getwalletinfo\" to query the scanning progress.\n",
265 {},
267 "Whether the abort was successful"},
268 RPCExamples{"\nImport a private key\n" +
269 HelpExampleCli("importprivkey", "\"mykey\"") +
270 "\nAbort the running wallet rescan\n" +
271 HelpExampleCli("abortrescan", "") +
272 "\nAs a JSON-RPC call\n" +
273 HelpExampleRpc("abortrescan", "")},
274 [&](const RPCHelpMan &self, const Config &config,
275 const JSONRPCRequest &request) -> UniValue {
276 std::shared_ptr<CWallet> const wallet =
278 if (!wallet) {
279 return NullUniValue;
280 }
281 CWallet *const pwallet = wallet.get();
282
283 if (!pwallet->IsScanning() || pwallet->IsAbortingRescan()) {
284 return false;
285 }
286 pwallet->AbortRescan();
287 return true;
288 },
289 };
290}
291
293 return RPCHelpMan{
294 "importaddress",
295 "Adds an address or script (in hex) that can be watched as if it "
296 "were in your wallet but cannot be used to spend. Requires a new "
297 "wallet backup.\n"
298 "\nNote: This call can take minutes to complete if rescan is true, "
299 "during that time, other rpc calls\n"
300 "may report that the imported address exists but related transactions "
301 "are still missing, leading to temporarily incorrect/bogus balances "
302 "and unspent outputs until rescan completes.\n"
303 "If you have the full public key, you should call importpubkey instead "
304 "of this.\n"
305 "Hint: use importmulti to import more than one address.\n"
306 "\nNote: If you import a non-standard raw script in hex form, outputs "
307 "sending to it will be treated\n"
308 "as change, and not show up in many RPCs.\n"
309 "Note: Use \"getwalletinfo\" to query the scanning progress.\n"
310 "Note: This command is only compatible with legacy wallets. Use "
311 "\"importdescriptors\" for descriptor wallets.\n",
312 {
314 "The Bitcoin address (or hex-encoded script)"},
315 {"label", RPCArg::Type::STR, RPCArg::Default{""},
316 "An optional label"},
317 {"rescan", RPCArg::Type::BOOL, RPCArg::Default{true},
318 "Rescan the wallet for transactions"},
319 {"p2sh", RPCArg::Type::BOOL, RPCArg::Default{false},
320 "Add the P2SH version of the script as well"},
321 },
324 "\nImport an address with rescan\n" +
325 HelpExampleCli("importaddress", "\"myaddress\"") +
326 "\nImport using a label without rescan\n" +
327 HelpExampleCli("importaddress", "\"myaddress\" \"testing\" false") +
328 "\nAs a JSON-RPC call\n" +
329 HelpExampleRpc("importaddress",
330 "\"myaddress\", \"testing\", false")},
331 [&](const RPCHelpMan &self, const Config &config,
332 const JSONRPCRequest &request) -> UniValue {
333 std::shared_ptr<CWallet> const wallet =
335 if (!wallet) {
336 return NullUniValue;
337 }
338 CWallet *const pwallet = wallet.get();
339
340 EnsureLegacyScriptPubKeyMan(*pwallet, true);
341
342 std::string strLabel;
343 if (!request.params[1].isNull()) {
344 strLabel = request.params[1].get_str();
345 }
346
347 // Whether to perform rescan after import
348 bool fRescan = true;
349 if (!request.params[2].isNull()) {
350 fRescan = request.params[2].get_bool();
351 }
352
353 if (fRescan && pwallet->chain().havePruned()) {
354 // Exit early and print an error.
355 // If a block is pruned after this check, we will import the
356 // key(s), but fail the rescan with a generic error.
358 "Rescan is disabled when blocks are pruned");
359 }
360
361 WalletRescanReserver reserver(*pwallet);
362 if (fRescan && !reserver.reserve()) {
364 "Wallet is currently rescanning. Abort "
365 "existing rescan or wait.");
366 }
367
368 // Whether to import a p2sh version, too
369 bool fP2SH = false;
370 if (!request.params[3].isNull()) {
371 fP2SH = request.params[3].get_bool();
372 }
373
374 {
375 LOCK(pwallet->cs_wallet);
376
378 request.params[0].get_str(), wallet->GetChainParams());
379 if (IsValidDestination(dest)) {
380 if (fP2SH) {
381 throw JSONRPCError(
383 "Cannot use the p2sh flag with an address - "
384 "use a script instead");
385 }
386
387 pwallet->MarkDirty();
388
389 pwallet->ImportScriptPubKeys(
390 strLabel, {GetScriptForDestination(dest)},
391 false /* have_solving_data */, true /* apply_label */,
392 1 /* timestamp */);
393 } else if (IsHex(request.params[0].get_str())) {
394 std::vector<uint8_t> data(
395 ParseHex(request.params[0].get_str()));
396 CScript redeem_script(data.begin(), data.end());
397
398 std::set<CScript> scripts = {redeem_script};
399 pwallet->ImportScripts(scripts, 0 /* timestamp */);
400
401 if (fP2SH) {
402 scripts.insert(
403 GetScriptForDestination(ScriptHash(redeem_script)));
404 }
405
406 pwallet->ImportScriptPubKeys(
407 strLabel, scripts, false /* have_solving_data */,
408 true /* apply_label */, 1 /* timestamp */);
409 } else {
411 "Invalid Bitcoin address or script");
412 }
413 }
414 if (fRescan) {
415 RescanWallet(*pwallet, reserver);
416 {
417 LOCK(pwallet->cs_wallet);
419 }
420 }
421
422 return NullUniValue;
423 },
424 };
425}
426
428 return RPCHelpMan{
429 "importprunedfunds",
430 "Imports funds without rescan. Corresponding address or script must "
431 "previously be included in wallet. Aimed towards pruned wallets. The "
432 "end-user is responsible to import additional transactions that "
433 "subsequently spend the imported outputs or rescan after the point in "
434 "the blockchain the transaction is included.\n",
435 {
437 "A raw transaction in hex funding an already-existing address in "
438 "wallet"},
440 "The hex output from gettxoutproof that contains the transaction"},
441 },
443 RPCExamples{""},
444 [&](const RPCHelpMan &self, const Config &config,
445 const JSONRPCRequest &request) -> UniValue {
446 std::shared_ptr<CWallet> const wallet =
448 if (!wallet) {
449 return NullUniValue;
450 }
451 CWallet *const pwallet = wallet.get();
452
454 if (!DecodeHexTx(tx, request.params[0].get_str())) {
456 "TX decode failed");
457 }
458 uint256 txid = tx.GetId();
459
460 DataStream ssMB{ParseHexV(request.params[1], "proof")};
461 CMerkleBlock merkleBlock;
462 ssMB >> merkleBlock;
463
464 // Search partial merkle tree in proof for our transaction and index
465 // in valid block
466 std::vector<uint256> vMatch;
467 std::vector<size_t> vIndex;
468 if (merkleBlock.txn.ExtractMatches(vMatch, vIndex) !=
469 merkleBlock.header.hashMerkleRoot) {
471 "Something wrong with merkleblock");
472 }
473
474 LOCK(pwallet->cs_wallet);
475 int height;
476 if (!pwallet->chain().findAncestorByHash(
477 pwallet->GetLastBlockHash(), merkleBlock.header.GetHash(),
478 FoundBlock().height(height))) {
480 "Block not found in chain");
481 }
482
483 std::vector<uint256>::const_iterator it;
484 if ((it = std::find(vMatch.begin(), vMatch.end(), txid)) ==
485 vMatch.end()) {
487 "Transaction given doesn't exist in proof");
488 }
489
490 size_t txnIndex = vIndex[it - vMatch.begin()];
491
493 CWalletTx::Status::CONFIRMED, height,
494 merkleBlock.header.GetHash(), txnIndex);
495
497 if (pwallet->IsMine(*tx_ref)) {
498 pwallet->AddToWallet(std::move(tx_ref), confirm);
499 return NullUniValue;
500 }
501
502 throw JSONRPCError(
504 "No addresses in wallet correspond to included transaction");
505 },
506 };
507}
508
510 return RPCHelpMan{
511 "removeprunedfunds",
512 "Deletes the specified transaction from the wallet. Meant for use "
513 "with pruned wallets and as a companion to importprunedfunds. This "
514 "will affect wallet balances.\n",
515 {
517 "The hex-encoded id of the transaction you are deleting"},
518 },
520 RPCExamples{HelpExampleCli("removeprunedfunds",
521 "\"a8d0c0184dde994a09ec054286f1ce581bebf4644"
522 "6a512166eae7628734ea0a5\"") +
523 "\nAs a JSON-RPC call\n" +
524 HelpExampleRpc("removeprunedfunds",
525 "\"a8d0c0184dde994a09ec054286f1ce581bebf4644"
526 "6a512166eae7628734ea0a5\"")},
527 [&](const RPCHelpMan &self, const Config &config,
528 const JSONRPCRequest &request) -> UniValue {
529 std::shared_ptr<CWallet> const wallet =
531 if (!wallet) {
532 return NullUniValue;
533 }
534 CWallet *const pwallet = wallet.get();
535
536 LOCK(pwallet->cs_wallet);
537
538 TxId txid(ParseHashV(request.params[0], "txid"));
539 std::vector<TxId> txIds;
540 txIds.push_back(txid);
541 std::vector<TxId> txIdsOut;
542
543 if (pwallet->ZapSelectTx(txIds, txIdsOut) != DBErrors::LOAD_OK) {
544 throw JSONRPCError(
546 "Could not properly delete the transaction.");
547 }
548
549 if (txIdsOut.empty()) {
551 "Transaction does not exist in wallet.");
552 }
553
554 return NullUniValue;
555 },
556 };
557}
558
560 return RPCHelpMan{
561 "importpubkey",
562 "Adds a public key (in hex) that can be watched as if it were in "
563 "your wallet but cannot be used to spend. Requires a new wallet "
564 "backup.\n"
565 "Hint: use importmulti to import more than one public key.\n"
566 "\nNote: This call can take minutes to complete if rescan is true, "
567 "during that time, other rpc calls\n"
568 "may report that the imported pubkey exists but related transactions "
569 "are still missing, leading to temporarily incorrect/bogus balances "
570 "and unspent outputs until rescan completes.\n"
571 "Note: Use \"getwalletinfo\" to query the scanning progress.\n"
572 "Note: This command is only compatible with legacy wallets. Use "
573 "\"importdescriptors\" with \"combo(X)\" for descriptor wallets.\n",
574 {
576 "The hex-encoded public key"},
577 {"label", RPCArg::Type::STR, RPCArg::Default{""},
578 "An optional label"},
579 {"rescan", RPCArg::Type::BOOL, RPCArg::Default{true},
580 "Rescan the wallet for transactions"},
581 },
584 "\nImport a public key with rescan\n" +
585 HelpExampleCli("importpubkey", "\"mypubkey\"") +
586 "\nImport using a label without rescan\n" +
587 HelpExampleCli("importpubkey", "\"mypubkey\" \"testing\" false") +
588 "\nAs a JSON-RPC call\n" +
589 HelpExampleRpc("importpubkey", "\"mypubkey\", \"testing\", false")},
590 [&](const RPCHelpMan &self, const Config &config,
591 const JSONRPCRequest &request) -> UniValue {
592 std::shared_ptr<CWallet> const wallet =
594 if (!wallet) {
595 return NullUniValue;
596 }
597 CWallet *const pwallet = wallet.get();
598
600
601 std::string strLabel;
602 if (!request.params[1].isNull()) {
603 strLabel = request.params[1].get_str();
604 }
605
606 // Whether to perform rescan after import
607 bool fRescan = true;
608 if (!request.params[2].isNull()) {
609 fRescan = request.params[2].get_bool();
610 }
611
612 if (fRescan && pwallet->chain().havePruned()) {
613 // Exit early and print an error.
614 // If a block is pruned after this check, we will import the
615 // key(s), but fail the rescan with a generic error.
617 "Rescan is disabled when blocks are pruned");
618 }
619
620 WalletRescanReserver reserver(*pwallet);
621 if (fRescan && !reserver.reserve()) {
623 "Wallet is currently rescanning. Abort "
624 "existing rescan or wait.");
625 }
626
627 if (!IsHex(request.params[0].get_str())) {
629 "Pubkey must be a hex string");
630 }
631 std::vector<uint8_t> data(ParseHex(request.params[0].get_str()));
632 CPubKey pubKey(data);
633 if (!pubKey.IsFullyValid()) {
635 "Pubkey is not a valid public key");
636 }
637
638 {
639 LOCK(pwallet->cs_wallet);
640
641 std::set<CScript> script_pub_keys;
642 for (const auto &dest : GetAllDestinationsForKey(pubKey)) {
643 script_pub_keys.insert(GetScriptForDestination(dest));
644 }
645
646 pwallet->MarkDirty();
647
648 pwallet->ImportScriptPubKeys(
649 strLabel, script_pub_keys, true /* have_solving_data */,
650 true /* apply_label */, 1 /* timestamp */);
651
652 pwallet->ImportPubKeys(
653 {pubKey.GetID()}, {{pubKey.GetID(), pubKey}},
654 {} /* key_origins */, false /* add_keypool */,
655 false /* internal */, 1 /* timestamp */);
656 }
657 if (fRescan) {
658 RescanWallet(*pwallet, reserver);
659 {
660 LOCK(pwallet->cs_wallet);
662 }
663 }
664
665 return NullUniValue;
666 },
667 };
668}
669
671 return RPCHelpMan{
672 "importwallet",
673 "Imports keys from a wallet dump file (see dumpwallet). Requires a "
674 "new wallet backup to include imported keys.\n"
675 "Note: Use \"getwalletinfo\" to query the scanning progress.\n"
676 "Note: This command is only compatible with legacy wallets.\n",
677 {
679 "The wallet file"},
680 },
682 RPCExamples{"\nDump the wallet\n" +
683 HelpExampleCli("dumpwallet", "\"test\"") +
684 "\nImport the wallet\n" +
685 HelpExampleCli("importwallet", "\"test\"") +
686 "\nImport using the json rpc call\n" +
687 HelpExampleRpc("importwallet", "\"test\"")},
688 [&](const RPCHelpMan &self, const Config &config,
689 const JSONRPCRequest &request) -> UniValue {
690 std::shared_ptr<CWallet> const wallet =
692 if (!wallet) {
693 return NullUniValue;
694 }
695 CWallet *const pwallet = wallet.get();
696
698
699 WalletRescanReserver reserver(*pwallet);
700 if (!reserver.reserve()) {
702 "Wallet is currently rescanning. Abort "
703 "existing rescan or wait.");
704 }
705
706 int64_t nTimeBegin = 0;
707 bool fGood = true;
708 {
709 LOCK(pwallet->cs_wallet);
710
711 EnsureWalletIsUnlocked(pwallet);
712
713 std::ifstream file;
714 file.open(fs::u8path(request.params[0].get_str()),
715 std::ios::in | std::ios::ate);
716 if (!file.is_open()) {
718 "Cannot open wallet dump file");
719 }
721 pwallet->chain().findBlock(pwallet->GetLastBlockHash(),
722 FoundBlock().time(nTimeBegin)));
723
724 int64_t nFilesize = std::max<int64_t>(1, file.tellg());
725 file.seekg(0, file.beg);
726
727 // Use uiInterface.ShowProgress instead of pwallet.ShowProgress
728 // because pwallet.ShowProgress has a cancel button tied to
729 // AbortRescan which we don't want for this progress bar showing
730 // the import progress. uiInterface.ShowProgress does not have a
731 // cancel button.
732
733 // show progress dialog in GUI
734 pwallet->chain().showProgress(
735 strprintf("%s " + _("Importing...").translated,
736 pwallet->GetDisplayName()),
737 0, false);
738 std::vector<std::tuple<CKey, int64_t, bool, std::string>> keys;
739 std::vector<std::pair<CScript, int64_t>> scripts;
740 while (file.good()) {
741 pwallet->chain().showProgress(
742 "",
743 std::max(1,
744 std::min<int>(50, 100 * double(file.tellg()) /
745 double(nFilesize))),
746 false);
747 std::string line;
748 std::getline(file, line);
749 if (line.empty() || line[0] == '#') {
750 continue;
751 }
752
753 std::vector<std::string> vstr = SplitString(line, ' ');
754 if (vstr.size() < 2) {
755 continue;
756 }
757 CKey key = DecodeSecret(vstr[0]);
758 if (key.IsValid()) {
759 int64_t nTime{
760 ParseISO8601DateTime(vstr[1]).value_or(0)};
761 std::string strLabel;
762 bool fLabel = true;
763 for (size_t nStr = 2; nStr < vstr.size(); nStr++) {
764 if (vstr[nStr].front() == '#') {
765 break;
766 }
767 if (vstr[nStr] == "change=1") {
768 fLabel = false;
769 }
770 if (vstr[nStr] == "reserve=1") {
771 fLabel = false;
772 }
773 if (vstr[nStr].substr(0, 6) == "label=") {
774 strLabel =
775 DecodeDumpString(vstr[nStr].substr(6));
776 fLabel = true;
777 }
778 }
779 nTimeBegin = std::min(nTimeBegin, nTime);
780 keys.push_back(
781 std::make_tuple(key, nTime, fLabel, strLabel));
782 } else if (IsHex(vstr[0])) {
783 std::vector<uint8_t> vData(ParseHex(vstr[0]));
784 CScript script = CScript(vData.begin(), vData.end());
785 int64_t birth_time{
786 ParseISO8601DateTime(vstr[1]).value_or(0)};
787 if (birth_time > 0) {
788 nTimeBegin = std::min(nTimeBegin, birth_time);
789 }
790 scripts.push_back(
791 std::pair<CScript, int64_t>(script, birth_time));
792 }
793 }
794 file.close();
795 EnsureBlockDataFromTime(*pwallet, nTimeBegin);
796 // We now know whether we are importing private keys, so we can
797 // error if private keys are disabled
798 if (keys.size() > 0 && pwallet->IsWalletFlagSet(
800 // hide progress dialog in GUI
801 pwallet->chain().showProgress("", 100, false);
803 "Importing wallets is disabled when "
804 "private keys are disabled");
805 }
806 double total = double(keys.size() + scripts.size());
807 double progress = 0;
808 for (const auto &key_tuple : keys) {
809 pwallet->chain().showProgress(
810 "",
811 std::max(50, std::min<int>(75, 100 * progress / total) +
812 50),
813 false);
814 const CKey &key = std::get<0>(key_tuple);
815 int64_t time = std::get<1>(key_tuple);
816 bool has_label = std::get<2>(key_tuple);
817 std::string label = std::get<3>(key_tuple);
818
819 CPubKey pubkey = key.GetPubKey();
820 CHECK_NONFATAL(key.VerifyPubKey(pubkey));
821 CKeyID keyid = pubkey.GetID();
822
823 pwallet->WalletLogPrintf(
824 "Importing %s...\n",
825 EncodeDestination(PKHash(keyid), config));
826
827 if (!pwallet->ImportPrivKeys({{keyid, key}}, time)) {
828 pwallet->WalletLogPrintf(
829 "Error importing key for %s\n",
830 EncodeDestination(PKHash(keyid), config));
831 fGood = false;
832 continue;
833 }
834
835 if (has_label) {
836 pwallet->SetAddressBook(PKHash(keyid), label,
837 "receive");
838 }
839 progress++;
840 }
841 for (const auto &script_pair : scripts) {
842 pwallet->chain().showProgress(
843 "",
844 std::max(50, std::min<int>(75, 100 * progress / total) +
845 50),
846 false);
847 const CScript &script = script_pair.first;
848 int64_t time = script_pair.second;
849
850 if (!pwallet->ImportScripts({script}, time)) {
851 pwallet->WalletLogPrintf("Error importing script %s\n",
852 HexStr(script));
853 fGood = false;
854 continue;
855 }
856
857 progress++;
858 }
859
860 // hide progress dialog in GUI
861 pwallet->chain().showProgress("", 100, false);
862 }
863 // hide progress dialog in GUI
864 pwallet->chain().showProgress("", 100, false);
865 RescanWallet(*pwallet, reserver, nTimeBegin, false /* update */);
866 pwallet->MarkDirty();
867
868 if (!fGood) {
870 "Error adding some keys/scripts to wallet");
871 }
872
873 return NullUniValue;
874 },
875 };
876}
877
879 return RPCHelpMan{
880 "dumpprivkey",
881 "Reveals the private key corresponding to 'address'.\n"
882 "Then the importprivkey can be used with this output\n"
883 "Note: This command is only compatible with legacy wallets.\n",
884 {
886 "The bitcoin address for the private key"},
887 },
888 RPCResult{RPCResult::Type::STR, "key", "The private key"},
889 RPCExamples{HelpExampleCli("dumpprivkey", "\"myaddress\"") +
890 HelpExampleCli("importprivkey", "\"mykey\"") +
891 HelpExampleRpc("dumpprivkey", "\"myaddress\"")},
892 [&](const RPCHelpMan &self, const Config &config,
893 const JSONRPCRequest &request) -> UniValue {
894 std::shared_ptr<CWallet> const wallet =
896 if (!wallet) {
897 return NullUniValue;
898 }
899 const CWallet *const pwallet = wallet.get();
900
901 LegacyScriptPubKeyMan &spk_man =
903
904 LOCK2(pwallet->cs_wallet, spk_man.cs_KeyStore);
905
906 EnsureWalletIsUnlocked(pwallet);
907
908 std::string strAddress = request.params[0].get_str();
909 CTxDestination dest =
910 DecodeDestination(strAddress, wallet->GetChainParams());
911 if (!IsValidDestination(dest)) {
913 "Invalid Bitcoin address");
914 }
915 auto keyid = GetKeyForDestination(spk_man, dest);
916 if (keyid.IsNull()) {
918 "Address does not refer to a key");
919 }
920 CKey vchSecret;
921 if (!spk_man.GetKey(keyid, vchSecret)) {
923 "Private key for address " + strAddress +
924 " is not known");
925 }
926 return EncodeSecret(vchSecret);
927 },
928 };
929}
930
932 return RPCHelpMan{
933 "dumpwallet",
934 "Dumps all wallet keys in a human-readable format to a server-side "
935 "file. This does not allow overwriting existing files.\n"
936 "Imported scripts are included in the dumpsfile, but corresponding "
937 "addresses may not be added automatically by importwallet.\n"
938 "Note that if your wallet contains keys which are not derived from "
939 "your HD seed (e.g. imported keys), these are not covered by\n"
940 "only backing up the seed itself, and must be backed up too (e.g. "
941 "ensure you back up the whole dumpfile).\n"
942 "Note: This command is only compatible with legacy wallets.\n",
943 {
945 "The filename with path (absolute path recommended)"},
946 },
948 "",
949 "",
950 {
951 {RPCResult::Type::STR, "filename",
952 "The filename with full absolute path"},
953 }},
954 RPCExamples{HelpExampleCli("dumpwallet", "\"test\"") +
955 HelpExampleRpc("dumpwallet", "\"test\"")},
956 [&](const RPCHelpMan &self, const Config &config,
957 const JSONRPCRequest &request) -> UniValue {
958 std::shared_ptr<CWallet> const pwallet =
960 if (!pwallet) {
961 return NullUniValue;
962 }
963
964 CWallet &wallet = *pwallet;
965 LegacyScriptPubKeyMan &spk_man =
967
968 // Make sure the results are valid at least up to the most recent
969 // block the user could have gotten from another RPC command prior
970 // to now
971 wallet.BlockUntilSyncedToCurrentChain();
972
973 LOCK(wallet.cs_wallet);
974
976
977 fs::path filepath = fs::u8path(request.params[0].get_str());
978 filepath = fs::absolute(filepath);
979
986 if (fs::exists(filepath)) {
988 filepath.u8string() +
989 " already exists. If you are "
990 "sure this is what you want, "
991 "move it out of the way first");
992 }
993
994 std::ofstream file;
995 file.open(filepath);
996 if (!file.is_open()) {
998 "Cannot open wallet dump file");
999 }
1000
1001 std::map<CKeyID, int64_t> mapKeyBirth;
1002 wallet.GetKeyBirthTimes(mapKeyBirth);
1003
1004 int64_t block_time = 0;
1005 CHECK_NONFATAL(wallet.chain().findBlock(
1006 wallet.GetLastBlockHash(), FoundBlock().time(block_time)));
1007
1008 // Note: To avoid a lock order issue, access to cs_main must be
1009 // locked before cs_KeyStore. So we do the two things in this
1010 // function that lock cs_main first: GetKeyBirthTimes, and
1011 // findBlock.
1012 LOCK(spk_man.cs_KeyStore);
1013
1014 const std::map<CKeyID, int64_t> &mapKeyPool =
1015 spk_man.GetAllReserveKeys();
1016 std::set<CScriptID> scripts = spk_man.GetCScripts();
1017
1018 // sort time/key pairs
1019 std::vector<std::pair<int64_t, CKeyID>> vKeyBirth;
1020 vKeyBirth.reserve(mapKeyBirth.size());
1021 for (const auto &entry : mapKeyBirth) {
1022 vKeyBirth.push_back(std::make_pair(entry.second, entry.first));
1023 }
1024 mapKeyBirth.clear();
1025 std::sort(vKeyBirth.begin(), vKeyBirth.end());
1026
1027 // produce output
1028 file << strprintf("# Wallet dump created by %s %s\n", CLIENT_NAME,
1029 CLIENT_BUILD);
1030 file << strprintf("# * Created on %s\n",
1032 file << strprintf("# * Best block at time of backup was %i (%s),\n",
1033 wallet.GetLastBlockHeight(),
1034 wallet.GetLastBlockHash().ToString());
1035 file << strprintf("# mined on %s\n",
1036 FormatISO8601DateTime(block_time));
1037 file << "\n";
1038
1039 // add the base58check encoded extended master if the wallet uses HD
1040 CKeyID seed_id = spk_man.GetHDChain().seed_id;
1041 if (!seed_id.IsNull()) {
1042 CKey seed;
1043 if (spk_man.GetKey(seed_id, seed)) {
1044 CExtKey masterKey;
1045 masterKey.SetSeed(seed);
1046
1047 file << "# extended private masterkey: "
1048 << EncodeExtKey(masterKey) << "\n\n";
1049 }
1050 }
1051 for (std::vector<std::pair<int64_t, CKeyID>>::const_iterator it =
1052 vKeyBirth.begin();
1053 it != vKeyBirth.end(); it++) {
1054 const CKeyID &keyid = it->second;
1055 std::string strTime = FormatISO8601DateTime(it->first);
1056 std::string strAddr;
1057 std::string strLabel;
1058 CKey key;
1059 if (spk_man.GetKey(keyid, key)) {
1060 file << strprintf("%s %s ", EncodeSecret(key), strTime);
1061 if (GetWalletAddressesForKey(config, &spk_man, &wallet,
1062 keyid, strAddr, strLabel)) {
1063 file << strprintf("label=%s", strLabel);
1064 } else if (keyid == seed_id) {
1065 file << "hdseed=1";
1066 } else if (mapKeyPool.count(keyid)) {
1067 file << "reserve=1";
1068 } else if (spk_man.mapKeyMetadata[keyid].hdKeypath == "s") {
1069 file << "inactivehdseed=1";
1070 } else {
1071 file << "change=1";
1072 }
1073 file << strprintf(
1074 " # addr=%s%s\n", strAddr,
1075 (spk_man.mapKeyMetadata[keyid].has_key_origin
1076 ? " hdkeypath=" +
1077 WriteHDKeypath(spk_man.mapKeyMetadata[keyid]
1078 .key_origin.path)
1079 : ""));
1080 }
1081 }
1082 file << "\n";
1083 for (const CScriptID &scriptid : scripts) {
1084 CScript script;
1085 std::string create_time = "0";
1086 std::string address =
1087 EncodeDestination(ScriptHash(scriptid), config);
1088 // get birth times for scripts with metadata
1089 auto it = spk_man.m_script_metadata.find(scriptid);
1090 if (it != spk_man.m_script_metadata.end()) {
1091 create_time = FormatISO8601DateTime(it->second.nCreateTime);
1092 }
1093 if (spk_man.GetCScript(scriptid, script)) {
1094 file << strprintf("%s %s script=1", HexStr(script),
1095 create_time);
1096 file << strprintf(" # addr=%s\n", address);
1097 }
1098 }
1099 file << "\n";
1100 file << "# End of dump\n";
1101 file.close();
1102
1103 UniValue reply(UniValue::VOBJ);
1104 reply.pushKV("filename", filepath.u8string());
1105
1106 return reply;
1107 },
1108 };
1109}
1110
1112 return RPCHelpMan{
1113 "dumpcoins",
1114 "dump all the UTXO tracked by the wallet.\n",
1115 {},
1116 RPCResult{
1118 "",
1119 "",
1120 {{
1122 "address",
1123 "The list of UTXO corresponding to this address.",
1124 {{
1126 "",
1127 "",
1128 {
1129 {RPCResult::Type::STR_HEX, "txid",
1130 "The transaction id"},
1131 {RPCResult::Type::NUM, "vout", "The output number"},
1132 {RPCResult::Type::NUM, "depth", "The output's depth"},
1134 "The output's amount"},
1135 },
1136 }},
1137 }},
1138 },
1139 RPCExamples{HelpExampleCli("dumpcoins", "") +
1140 HelpExampleRpc("dumpcoins", "")},
1141 [&](const RPCHelpMan &self, const Config &config,
1142 const JSONRPCRequest &request) -> UniValue {
1143 std::shared_ptr<CWallet> const pwallet =
1145 if (!pwallet) {
1146 return NullUniValue;
1147 }
1148
1149 CWallet &wallet = *pwallet;
1150
1151 // Make sure the results are valid at least up to the most recent
1152 // block the user could have gotten from another RPC command prior
1153 // to now
1154 wallet.BlockUntilSyncedToCurrentChain();
1155
1156 LOCK(wallet.cs_wallet);
1157
1159
1160 UniValue result(UniValue::VOBJ);
1161 for (const auto &p : ListCoins(wallet)) {
1162 UniValue coins(UniValue::VARR);
1163 for (const auto &o : p.second) {
1165 utxo.pushKV("txid", o.tx->GetId().ToString());
1166 utxo.pushKV("vout", o.i);
1167 utxo.pushKV("depth", o.nDepth);
1168 utxo.pushKV("value", o.tx->tx->vout[o.i].nValue);
1169
1170 coins.push_back(std::move(utxo));
1171 }
1172
1173 result.pushKV(EncodeDestination(p.first, config), coins);
1174 }
1175
1176 return result;
1177 },
1178 };
1179}
1180
1182 // Input data
1184 std::unique_ptr<CScript> redeemscript;
1185
1186 // Output data
1187 std::set<CScript> import_scripts;
1190 std::map<CKeyID, bool> used_keys;
1191 std::map<CKeyID, std::pair<CPubKey, KeyOriginInfo>> key_origins;
1192};
1193
1194enum class ScriptContext {
1196 TOP,
1198 P2SH,
1199};
1200
1201// Analyse the provided scriptPubKey, determining which keys and which redeem
1202// scripts from the ImportData struct are needed to spend it, and mark them as
1203// used. Returns an error string, or the empty string for success.
1204static std::string RecurseImportData(const CScript &script,
1205 ImportData &import_data,
1206 const ScriptContext script_ctx) {
1207 // Use Solver to obtain script type and parsed pubkeys or hashes:
1208 std::vector<std::vector<uint8_t>> solverdata;
1209 TxoutType script_type = Solver(script, solverdata);
1210
1211 switch (script_type) {
1212 case TxoutType::PUBKEY: {
1213 CPubKey pubkey(solverdata[0]);
1214 import_data.used_keys.emplace(pubkey.GetID(), false);
1215 return "";
1216 }
1217 case TxoutType::PUBKEYHASH: {
1218 CKeyID id = CKeyID(uint160(solverdata[0]));
1219 import_data.used_keys[id] = true;
1220 return "";
1221 }
1222 case TxoutType::SCRIPTHASH: {
1223 if (script_ctx == ScriptContext::P2SH) {
1225 "Trying to nest P2SH inside another P2SH");
1226 }
1227 CHECK_NONFATAL(script_ctx == ScriptContext::TOP);
1228 CScriptID id = CScriptID(uint160(solverdata[0]));
1229 // Remove redeemscript from import_data to check for superfluous
1230 // script later.
1231 auto subscript = std::move(import_data.redeemscript);
1232 if (!subscript) {
1233 return "missing redeemscript";
1234 }
1235 if (CScriptID(*subscript) != id) {
1236 return "redeemScript does not match the scriptPubKey";
1237 }
1238 import_data.import_scripts.emplace(*subscript);
1239 return RecurseImportData(*subscript, import_data,
1241 }
1242 case TxoutType::MULTISIG: {
1243 for (size_t i = 1; i + 1 < solverdata.size(); ++i) {
1244 CPubKey pubkey(solverdata[i]);
1245 import_data.used_keys.emplace(pubkey.GetID(), false);
1246 }
1247 return "";
1248 }
1250 return "unspendable script";
1252 default:
1253 return "unrecognized script";
1254 }
1255}
1256
1258 CWallet *const pwallet, ImportData &import_data,
1259 std::map<CKeyID, CPubKey> &pubkey_map, std::map<CKeyID, CKey> &privkey_map,
1260 std::set<CScript> &script_pub_keys, bool &have_solving_data,
1261 const UniValue &data, std::vector<CKeyID> &ordered_pubkeys) {
1262 UniValue warnings(UniValue::VARR);
1263
1264 // First ensure scriptPubKey has either a script or JSON with "address"
1265 // string
1266 const UniValue &scriptPubKey = data["scriptPubKey"];
1267 bool isScript = scriptPubKey.getType() == UniValue::VSTR;
1268 if (!isScript && !(scriptPubKey.getType() == UniValue::VOBJ &&
1269 scriptPubKey.exists("address"))) {
1271 "scriptPubKey must be string with script or JSON "
1272 "with address string");
1273 }
1274 const std::string &output =
1275 isScript ? scriptPubKey.get_str() : scriptPubKey["address"].get_str();
1276
1277 // Optional fields.
1278 const std::string &strRedeemScript =
1279 data.exists("redeemscript") ? data["redeemscript"].get_str() : "";
1280 const UniValue &pubKeys =
1281 data.exists("pubkeys") ? data["pubkeys"].get_array() : UniValue();
1282 const UniValue &keys =
1283 data.exists("keys") ? data["keys"].get_array() : UniValue();
1284 const bool internal =
1285 data.exists("internal") ? data["internal"].get_bool() : false;
1286 const bool watchOnly =
1287 data.exists("watchonly") ? data["watchonly"].get_bool() : false;
1288
1289 if (data.exists("range")) {
1290 throw JSONRPCError(
1292 "Range should not be specified for a non-descriptor import");
1293 }
1294
1295 // Generate the script and destination for the scriptPubKey provided
1296 CScript script;
1297 if (!isScript) {
1298 CTxDestination dest =
1299 DecodeDestination(output, pwallet->GetChainParams());
1300 if (!IsValidDestination(dest)) {
1302 "Invalid address \"" + output + "\"");
1303 }
1305 } else {
1306 if (!IsHex(output)) {
1308 "Invalid scriptPubKey \"" + output + "\"");
1309 }
1310 std::vector<uint8_t> vData(ParseHex(output));
1311 script = CScript(vData.begin(), vData.end());
1312 CTxDestination dest;
1313 if (!ExtractDestination(script, dest) && !internal) {
1315 "Internal must be set to true for "
1316 "nonstandard scriptPubKey imports.");
1317 }
1318 }
1319 script_pub_keys.emplace(script);
1320
1321 // Parse all arguments
1322 if (strRedeemScript.size()) {
1323 if (!IsHex(strRedeemScript)) {
1325 "Invalid redeem script \"" + strRedeemScript +
1326 "\": must be hex string");
1327 }
1328 auto parsed_redeemscript = ParseHex(strRedeemScript);
1329 import_data.redeemscript = std::make_unique<CScript>(
1330 parsed_redeemscript.begin(), parsed_redeemscript.end());
1331 }
1332 for (size_t i = 0; i < pubKeys.size(); ++i) {
1333 const auto &str = pubKeys[i].get_str();
1334 if (!IsHex(str)) {
1336 "Pubkey \"" + str + "\" must be a hex string");
1337 }
1338 auto parsed_pubkey = ParseHex(str);
1339 CPubKey pubkey(parsed_pubkey);
1340 if (!pubkey.IsFullyValid()) {
1342 "Pubkey \"" + str +
1343 "\" is not a valid public key");
1344 }
1345 pubkey_map.emplace(pubkey.GetID(), pubkey);
1346 ordered_pubkeys.push_back(pubkey.GetID());
1347 }
1348 for (size_t i = 0; i < keys.size(); ++i) {
1349 const auto &str = keys[i].get_str();
1350 CKey key = DecodeSecret(str);
1351 if (!key.IsValid()) {
1353 "Invalid private key encoding");
1354 }
1355 CPubKey pubkey = key.GetPubKey();
1356 CKeyID id = pubkey.GetID();
1357 if (pubkey_map.count(id)) {
1358 pubkey_map.erase(id);
1359 }
1360 privkey_map.emplace(id, key);
1361 }
1362
1363 // Verify and process input data
1364 have_solving_data =
1365 import_data.redeemscript || pubkey_map.size() || privkey_map.size();
1366 if (have_solving_data) {
1367 // Match up data in import_data with the scriptPubKey in script.
1368 auto error = RecurseImportData(script, import_data, ScriptContext::TOP);
1369
1370 // Verify whether the watchonly option corresponds to the
1371 // availability of private keys.
1372 bool spendable = std::all_of(
1373 import_data.used_keys.begin(), import_data.used_keys.end(),
1374 [&](const std::pair<CKeyID, bool> &used_key) {
1375 return privkey_map.count(used_key.first) > 0;
1376 });
1377 if (!watchOnly && !spendable) {
1378 warnings.push_back("Some private keys are missing, outputs "
1379 "will be considered watchonly. If this is "
1380 "intentional, specify the watchonly flag.");
1381 }
1382 if (watchOnly && spendable) {
1383 warnings.push_back(
1384 "All private keys are provided, outputs will be considered "
1385 "spendable. If this is intentional, do not specify the "
1386 "watchonly flag.");
1387 }
1388
1389 // Check that all required keys for solvability are provided.
1390 if (error.empty()) {
1391 for (const auto &require_key : import_data.used_keys) {
1392 if (!require_key.second) {
1393 // Not a required key
1394 continue;
1395 }
1396
1397 if (pubkey_map.count(require_key.first) == 0 &&
1398 privkey_map.count(require_key.first) == 0) {
1399 error = "some required keys are missing";
1400 }
1401 }
1402 }
1403
1404 if (!error.empty()) {
1405 warnings.push_back("Importing as non-solvable: " + error +
1406 ". If this is intentional, don't provide "
1407 "any keys, pubkeys or redeemscript.");
1408 import_data = ImportData();
1409 pubkey_map.clear();
1410 privkey_map.clear();
1411 have_solving_data = false;
1412 } else {
1413 // RecurseImportData() removes any relevant redeemscript from
1414 // import_data, so we can use that to discover if a superfluous
1415 // one was provided.
1416 if (import_data.redeemscript) {
1417 warnings.push_back(
1418 "Ignoring redeemscript as this is not a P2SH script.");
1419 }
1420 for (auto it = privkey_map.begin(); it != privkey_map.end();) {
1421 auto oldit = it++;
1422 if (import_data.used_keys.count(oldit->first) == 0) {
1423 warnings.push_back("Ignoring irrelevant private key.");
1424 privkey_map.erase(oldit);
1425 }
1426 }
1427 for (auto it = pubkey_map.begin(); it != pubkey_map.end();) {
1428 auto oldit = it++;
1429 auto key_data_it = import_data.used_keys.find(oldit->first);
1430 if (key_data_it == import_data.used_keys.end() ||
1431 !key_data_it->second) {
1432 warnings.push_back("Ignoring public key \"" +
1433 HexStr(oldit->first) +
1434 "\" as it doesn't appear inside P2PKH.");
1435 pubkey_map.erase(oldit);
1436 }
1437 }
1438 }
1439 }
1440
1441 return warnings;
1442}
1443
1445 std::map<CKeyID, CPubKey> &pubkey_map,
1446 std::map<CKeyID, CKey> &privkey_map,
1447 std::set<CScript> &script_pub_keys,
1448 bool &have_solving_data,
1449 const UniValue &data,
1450 std::vector<CKeyID> &ordered_pubkeys) {
1451 UniValue warnings(UniValue::VARR);
1452
1453 const std::string &descriptor = data["desc"].get_str();
1455 std::string error;
1456 auto parsed_desc =
1457 Parse(descriptor, keys, error, /* require_checksum = */ true);
1458 if (!parsed_desc) {
1460 }
1461
1462 have_solving_data = parsed_desc->IsSolvable();
1463 const bool watch_only =
1464 data.exists("watchonly") ? data["watchonly"].get_bool() : false;
1465
1466 int64_t range_start = 0, range_end = 0;
1467 if (!parsed_desc->IsRange() && data.exists("range")) {
1468 throw JSONRPCError(
1470 "Range should not be specified for an un-ranged descriptor");
1471 } else if (parsed_desc->IsRange()) {
1472 if (!data.exists("range")) {
1473 throw JSONRPCError(
1475 "Descriptor is ranged, please specify the range");
1476 }
1477 std::tie(range_start, range_end) = ParseDescriptorRange(data["range"]);
1478 }
1479
1480 const UniValue &priv_keys =
1481 data.exists("keys") ? data["keys"].get_array() : UniValue();
1482
1483 // Expand all descriptors to get public keys and scripts, and private keys
1484 // if available.
1485 for (int i = range_start; i <= range_end; ++i) {
1486 FlatSigningProvider out_keys;
1487 std::vector<CScript> scripts_temp;
1488 parsed_desc->Expand(i, keys, scripts_temp, out_keys);
1489 std::copy(scripts_temp.begin(), scripts_temp.end(),
1490 std::inserter(script_pub_keys, script_pub_keys.end()));
1491 for (const auto &key_pair : out_keys.pubkeys) {
1492 ordered_pubkeys.push_back(key_pair.first);
1493 }
1494
1495 for (const auto &x : out_keys.scripts) {
1496 import_data.import_scripts.emplace(x.second);
1497 }
1498
1499 parsed_desc->ExpandPrivate(i, keys, out_keys);
1500
1501 std::copy(out_keys.pubkeys.begin(), out_keys.pubkeys.end(),
1502 std::inserter(pubkey_map, pubkey_map.end()));
1503 std::copy(out_keys.keys.begin(), out_keys.keys.end(),
1504 std::inserter(privkey_map, privkey_map.end()));
1505 import_data.key_origins.insert(out_keys.origins.begin(),
1506 out_keys.origins.end());
1507 }
1508
1509 for (size_t i = 0; i < priv_keys.size(); ++i) {
1510 const auto &str = priv_keys[i].get_str();
1511 CKey key = DecodeSecret(str);
1512 if (!key.IsValid()) {
1514 "Invalid private key encoding");
1515 }
1516 CPubKey pubkey = key.GetPubKey();
1517 CKeyID id = pubkey.GetID();
1518
1519 // Check if this private key corresponds to a public key from the
1520 // descriptor
1521 if (!pubkey_map.count(id)) {
1522 warnings.push_back("Ignoring irrelevant private key.");
1523 } else {
1524 privkey_map.emplace(id, key);
1525 }
1526 }
1527
1528 // Check if all the public keys have corresponding private keys in the
1529 // import for spendability. This does not take into account threshold
1530 // multisigs which could be spendable without all keys. Thus, threshold
1531 // multisigs without all keys will be considered not spendable here, even if
1532 // they are, perhaps triggering a false warning message. This is consistent
1533 // with the current wallet IsMine check.
1534 bool spendable =
1535 std::all_of(pubkey_map.begin(), pubkey_map.end(),
1536 [&](const std::pair<CKeyID, CPubKey> &used_key) {
1537 return privkey_map.count(used_key.first) > 0;
1538 }) &&
1539 std::all_of(
1540 import_data.key_origins.begin(), import_data.key_origins.end(),
1541 [&](const std::pair<CKeyID, std::pair<CPubKey, KeyOriginInfo>>
1542 &entry) { return privkey_map.count(entry.first) > 0; });
1543 if (!watch_only && !spendable) {
1544 warnings.push_back(
1545 "Some private keys are missing, outputs will be considered "
1546 "watchonly. If this is intentional, specify the watchonly flag.");
1547 }
1548 if (watch_only && spendable) {
1549 warnings.push_back("All private keys are provided, outputs will be "
1550 "considered spendable. If this is intentional, do "
1551 "not specify the watchonly flag.");
1552 }
1553
1554 return warnings;
1555}
1556
1557static UniValue ProcessImport(CWallet *const pwallet, const UniValue &data,
1558 const int64_t timestamp)
1559 EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet) {
1560 UniValue warnings(UniValue::VARR);
1561 UniValue result(UniValue::VOBJ);
1562
1563 try {
1564 const bool internal =
1565 data.exists("internal") ? data["internal"].get_bool() : false;
1566 // Internal addresses should not have a label
1567 if (internal && data.exists("label")) {
1569 "Internal addresses should not have a label");
1570 }
1571 const std::string &label =
1572 data.exists("label") ? data["label"].get_str() : "";
1573 const bool add_keypool =
1574 data.exists("keypool") ? data["keypool"].get_bool() : false;
1575
1576 // Add to keypool only works with privkeys disabled
1577 if (add_keypool &&
1578 !pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
1580 "Keys can only be imported to the keypool when "
1581 "private keys are disabled");
1582 }
1583
1584 ImportData import_data;
1585 std::map<CKeyID, CPubKey> pubkey_map;
1586 std::map<CKeyID, CKey> privkey_map;
1587 std::set<CScript> script_pub_keys;
1588 std::vector<CKeyID> ordered_pubkeys;
1589 bool have_solving_data;
1590
1591 if (data.exists("scriptPubKey") && data.exists("desc")) {
1592 throw JSONRPCError(
1594 "Both a descriptor and a scriptPubKey should not be provided.");
1595 } else if (data.exists("scriptPubKey")) {
1596 warnings = ProcessImportLegacy(
1597 pwallet, import_data, pubkey_map, privkey_map, script_pub_keys,
1598 have_solving_data, data, ordered_pubkeys);
1599 } else if (data.exists("desc")) {
1600 warnings = ProcessImportDescriptor(
1601 import_data, pubkey_map, privkey_map, script_pub_keys,
1602 have_solving_data, data, ordered_pubkeys);
1603 } else {
1604 throw JSONRPCError(
1606 "Either a descriptor or scriptPubKey must be provided.");
1607 }
1608
1609 // If private keys are disabled, abort if private keys are being
1610 // imported
1611 if (pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) &&
1612 !privkey_map.empty()) {
1614 "Cannot import private keys to a wallet with "
1615 "private keys disabled");
1616 }
1617
1618 // Check whether we have any work to do
1619 for (const CScript &script : script_pub_keys) {
1620 if (pwallet->IsMine(script) & ISMINE_SPENDABLE) {
1622 "The wallet already contains the private "
1623 "key for this address or script (\"" +
1624 HexStr(script) + "\")");
1625 }
1626 }
1627
1628 // All good, time to import
1629 pwallet->MarkDirty();
1630 if (!pwallet->ImportScripts(import_data.import_scripts, timestamp)) {
1632 "Error adding script to wallet");
1633 }
1634 if (!pwallet->ImportPrivKeys(privkey_map, timestamp)) {
1635 throw JSONRPCError(RPC_WALLET_ERROR, "Error adding key to wallet");
1636 }
1637 if (!pwallet->ImportPubKeys(ordered_pubkeys, pubkey_map,
1638 import_data.key_origins, add_keypool,
1639 internal, timestamp)) {
1641 "Error adding address to wallet");
1642 }
1643 if (!pwallet->ImportScriptPubKeys(label, script_pub_keys,
1644 have_solving_data, !internal,
1645 timestamp)) {
1647 "Error adding address to wallet");
1648 }
1649
1650 result.pushKV("success", UniValue(true));
1651 } catch (const UniValue &e) {
1652 result.pushKV("success", UniValue(false));
1653 result.pushKV("error", e);
1654 } catch (...) {
1655 result.pushKV("success", UniValue(false));
1656 result.pushKV("error",
1657 JSONRPCError(RPC_MISC_ERROR, "Missing required fields"));
1658 }
1659
1660 if (warnings.size()) {
1661 result.pushKV("warnings", warnings);
1662 }
1663 return result;
1664}
1665
1666static int64_t GetImportTimestamp(const UniValue &data, int64_t now) {
1667 if (data.exists("timestamp")) {
1668 const UniValue &timestamp = data["timestamp"];
1669 if (timestamp.isNum()) {
1670 return timestamp.getInt<int64_t>();
1671 } else if (timestamp.isStr() && timestamp.get_str() == "now") {
1672 return now;
1673 }
1675 strprintf("Expected number or \"now\" timestamp "
1676 "value for key. got type %s",
1677 uvTypeName(timestamp.type())));
1678 }
1680 "Missing required timestamp field for key");
1681}
1682
1683static std::string GetRescanErrorMessage(const std::string &object,
1684 const int64_t objectTimestamp,
1685 const int64_t blockTimestamp,
1686 const bool have_pruned,
1687 const bool has_assumed_valid_chain) {
1688 std::string error_msg{
1689 strprintf("Rescan failed for %s with creation timestamp %d. There "
1690 "was an error reading a block from time %d, which is after "
1691 "or within %d seconds of key creation, and could contain "
1692 "transactions pertaining to the %s. As a result, "
1693 "transactions and coins using this %s may not appear "
1694 "in the wallet. ",
1695 object, objectTimestamp, blockTimestamp, TIMESTAMP_WINDOW,
1696 object, object)};
1697 if (have_pruned) {
1698 error_msg += strprintf(
1699 "This error could be caused by pruning or data corruption "
1700 "(see bitcoind log for details) and could be dealt with by "
1701 "downloading and rescanning the relevant blocks (see -reindex "
1702 "option and rescanblockchain RPC).");
1703 } else if (has_assumed_valid_chain) {
1704 error_msg += strprintf(
1705 "This error is likely caused by an in-progress assumeutxo "
1706 "background sync. Check logs or getchainstates RPC for assumeutxo "
1707 "background sync progress and try again later.");
1708 } else {
1709 error_msg += strprintf(
1710 "This error could potentially be caused by data corruption. If "
1711 "the issue persists you may want to reindex (see -reindex "
1712 "option).");
1713 }
1714 return error_msg;
1715}
1716
1718 return RPCHelpMan{
1719 "importmulti",
1720 "Import addresses/scripts (with private or public keys, redeem "
1721 "script (P2SH)), optionally rescanning the blockchain from the "
1722 "earliest creation time of the imported scripts. Requires a new wallet "
1723 "backup.\n"
1724 "If an address/script is imported without all of the private keys "
1725 "required to spend from that address, it will be watchonly. The "
1726 "'watchonly' option must be set to true in this case or a warning will "
1727 "be returned.\n"
1728 "Conversely, if all the private keys are provided and the "
1729 "address/script is spendable, the watchonly option must be set to "
1730 "false, or a warning will be returned.\n"
1731 "Note: Use \"getwalletinfo\" to query the scanning progress.\n"
1732 "Note: This command is only compatible with legacy wallets. Use "
1733 "\"importdescriptors\" for descriptor wallets.\n",
1734 {
1735 {"requests",
1738 "Data to be imported",
1739 {
1740 {
1741 "",
1744 "",
1745 {
1747 "Descriptor to import. If using descriptor, do not "
1748 "also provide address/scriptPubKey, scripts, or "
1749 "pubkeys"},
1750 {"scriptPubKey", RPCArg::Type::STR,
1752 "Type of scriptPubKey (string for script, json for "
1753 "address). Should not be provided if using a "
1754 "descriptor",
1756 .type_str = {"\"<script>\" | { "
1757 "\"address\":\"<address>\" }",
1758 "string / json"}}},
1760 "Creation time of the key expressed in " +
1762 ",\n"
1763 "or the string \"now\" to substitute the current "
1764 "synced blockchain time. The timestamp of the "
1765 "oldest\n"
1766 "key will determine how far back blockchain "
1767 "rescans need to begin for missing wallet "
1768 "transactions.\n"
1769 "\"now\" can be specified to bypass scanning, "
1770 "for keys which are known to never have been "
1771 "used, and\n"
1772 "0 can be specified to scan the entire "
1773 "blockchain. Blocks up to 2 hours before the "
1774 "earliest key\n"
1775 "creation time of all keys being imported by the "
1776 "importmulti call will be scanned.",
1777 RPCArgOptions{.type_str = {"timestamp | \"now\"",
1778 "integer / string"}}},
1779 {"redeemscript", RPCArg::Type::STR,
1781 "Allowed only if the scriptPubKey is a P2SH "
1782 "address/scriptPubKey"},
1783 {"pubkeys",
1786 "Array of strings giving pubkeys to import. They "
1787 "must occur in P2PKH scripts. They are not required "
1788 "when the private key is also provided (see the "
1789 "\"keys\" argument).",
1790 {
1791 {"pubKey", RPCArg::Type::STR,
1793 }},
1794 {"keys",
1797 "Array of strings giving private keys to import. The "
1798 "corresponding public keys must occur in the output "
1799 "or redeemscript.",
1800 {
1801 {"key", RPCArg::Type::STR,
1803 }},
1804 {"range", RPCArg::Type::RANGE,
1806 "If a ranged descriptor is used, this specifies the "
1807 "end or the range (in the form [begin,end]) to "
1808 "import"},
1809 {"internal", RPCArg::Type::BOOL,
1810 RPCArg::Default{false},
1811 "Stating whether matching outputs should be treated "
1812 "as not incoming payments (also known as change)"},
1813 {"watchonly", RPCArg::Type::BOOL,
1814 RPCArg::Default{false},
1815 "Stating whether matching outputs should be "
1816 "considered watchonly."},
1817 {"label", RPCArg::Type::STR, RPCArg::Default{""},
1818 "Label to assign to the address, only allowed with "
1819 "internal=false"},
1820 {"keypool", RPCArg::Type::BOOL, RPCArg::Default{false},
1821 "Stating whether imported public keys should be "
1822 "added to the keypool for when users request new "
1823 "addresses. Only allowed when wallet private keys "
1824 "are disabled"},
1825 },
1826 },
1827 },
1828 RPCArgOptions{.oneline_description = "\"requests\""}},
1829 {"options",
1832 "",
1833 {
1834 {"rescan", RPCArg::Type::BOOL, RPCArg::Default{true},
1835 "Stating if should rescan the blockchain after all imports"},
1836 },
1837 RPCArgOptions{.oneline_description = "\"options\""}},
1838 },
1840 "",
1841 "Response is an array with the same size as the input that "
1842 "has the execution result",
1843 {
1845 "",
1846 "",
1847 {
1848 {RPCResult::Type::BOOL, "success", ""},
1850 "warnings",
1851 /* optional */ true,
1852 "",
1853 {
1854 {RPCResult::Type::STR, "", ""},
1855 }},
1857 "error",
1858 /* optional */ true,
1859 "",
1860 {
1861 {RPCResult::Type::ELISION, "", "JSONRPC error"},
1862 }},
1863 }},
1864 }},
1867 "importmulti",
1868 "'[{ \"scriptPubKey\": { \"address\": \"<my address>\" }, "
1869 "\"timestamp\":1455191478 }, "
1870 "{ \"scriptPubKey\": { \"address\": \"<my 2nd address>\" "
1871 "}, "
1872 "\"label\": \"example 2\", \"timestamp\": 1455191480 }]'") +
1874 "importmulti",
1875 "'[{ \"scriptPubKey\": { \"address\": \"<my address>\" }, "
1876 "\"timestamp\":1455191478 }]' '{ \"rescan\": false}'")
1877
1878 },
1879 [&](const RPCHelpMan &self, const Config &config,
1880 const JSONRPCRequest &mainRequest) -> UniValue {
1881 std::shared_ptr<CWallet> const wallet =
1882 GetWalletForJSONRPCRequest(mainRequest);
1883 if (!wallet) {
1884 return NullUniValue;
1885 }
1886 CWallet *const pwallet = wallet.get();
1887
1889
1890 const UniValue &requests = mainRequest.params[0];
1891
1892 // Default options
1893 bool fRescan = true;
1894
1895 if (!mainRequest.params[1].isNull()) {
1896 const UniValue &options = mainRequest.params[1];
1897
1898 if (options.exists("rescan")) {
1899 fRescan = options["rescan"].get_bool();
1900 }
1901 }
1902
1903 WalletRescanReserver reserver(*pwallet);
1904 if (fRescan && !reserver.reserve()) {
1906 "Wallet is currently rescanning. Abort "
1907 "existing rescan or wait.");
1908 }
1909
1910 int64_t now = 0;
1911 bool fRunScan = false;
1912 int64_t nLowestTimestamp = 0;
1914 {
1915 LOCK(pwallet->cs_wallet);
1916 EnsureWalletIsUnlocked(pwallet);
1917
1918 // Verify all timestamps are present before importing any keys.
1919 CHECK_NONFATAL(pwallet->chain().findBlock(
1920 pwallet->GetLastBlockHash(),
1921 FoundBlock().time(nLowestTimestamp).mtpTime(now)));
1922 for (const UniValue &data : requests.getValues()) {
1923 GetImportTimestamp(data, now);
1924 }
1925
1926 const int64_t minimumTimestamp = 1;
1927
1928 for (const UniValue &data : requests.getValues()) {
1929 const int64_t timestamp = std::max(
1930 GetImportTimestamp(data, now), minimumTimestamp);
1931 const UniValue result =
1932 ProcessImport(pwallet, data, timestamp);
1933 response.push_back(result);
1934
1935 if (!fRescan) {
1936 continue;
1937 }
1938
1939 // If at least one request was successful then allow rescan.
1940 if (result["success"].get_bool()) {
1941 fRunScan = true;
1942 }
1943
1944 // Get the lowest timestamp.
1945 if (timestamp < nLowestTimestamp) {
1946 nLowestTimestamp = timestamp;
1947 }
1948 }
1949 }
1950 if (fRescan && fRunScan && requests.size()) {
1951 int64_t scannedTime = pwallet->RescanFromTime(
1952 nLowestTimestamp, reserver, true /* update */);
1953 {
1954 LOCK(pwallet->cs_wallet);
1955 pwallet->ReacceptWalletTransactions();
1956 }
1957
1958 if (pwallet->IsAbortingRescan()) {
1960 "Rescan aborted by user.");
1961 }
1962 if (scannedTime > nLowestTimestamp) {
1963 std::vector<UniValue> results = response.getValues();
1964 response.clear();
1965 response.setArray();
1966 size_t i = 0;
1967 for (const UniValue &request : requests.getValues()) {
1968 // If key creation date is within the successfully
1969 // scanned range, or if the import result already has an
1970 // error set, let the result stand unmodified. Otherwise
1971 // replace the result with an error message.
1972 if (scannedTime <= GetImportTimestamp(request, now) ||
1973 results.at(i).exists("error")) {
1974 response.push_back(results.at(i));
1975 } else {
1977 result.pushKV("success", UniValue(false));
1978 result.pushKV(
1979 "error",
1983 "key", GetImportTimestamp(request, now),
1984 scannedTime - TIMESTAMP_WINDOW - 1,
1985 pwallet->chain().havePruned(),
1986 pwallet->chain()
1988 response.push_back(std::move(result));
1989 }
1990 ++i;
1991 }
1992 }
1993 }
1994
1995 return response;
1996 },
1997 };
1998}
1999
2001 const UniValue &data,
2002 const int64_t timestamp)
2003 EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet) {
2004 UniValue warnings(UniValue::VARR);
2005 UniValue result(UniValue::VOBJ);
2006
2007 try {
2008 if (!data.exists("desc")) {
2009 throw JSONRPCError(RPC_INVALID_PARAMETER, "Descriptor not found.");
2010 }
2011
2012 const std::string &descriptor = data["desc"].get_str();
2013 const bool active =
2014 data.exists("active") ? data["active"].get_bool() : false;
2015 const bool internal =
2016 data.exists("internal") ? data["internal"].get_bool() : false;
2017 const std::string &label =
2018 data.exists("label") ? data["label"].get_str() : "";
2019
2020 // Parse descriptor string
2022 std::string error;
2023 auto parsed_desc =
2024 Parse(descriptor, keys, error, /* require_checksum = */ true);
2025 if (!parsed_desc) {
2027 }
2028
2029 // Range check
2030 int64_t range_start = 0, range_end = 1, next_index = 0;
2031 if (!parsed_desc->IsRange() && data.exists("range")) {
2032 throw JSONRPCError(
2034 "Range should not be specified for an un-ranged descriptor");
2035 } else if (parsed_desc->IsRange()) {
2036 if (data.exists("range")) {
2037 auto range = ParseDescriptorRange(data["range"]);
2038 range_start = range.first;
2039 // Specified range end is inclusive, but we need range end as
2040 // exclusive
2041 range_end = range.second + 1;
2042 } else {
2043 warnings.push_back(
2044 "Range not given, using default keypool range");
2045 range_start = 0;
2046 range_end = gArgs.GetIntArg("-keypool", DEFAULT_KEYPOOL_SIZE);
2047 }
2048 next_index = range_start;
2049
2050 if (data.exists("next_index")) {
2051 next_index = data["next_index"].getInt<int64_t>();
2052 // bound checks
2053 if (next_index < range_start || next_index >= range_end) {
2055 "next_index is out of range");
2056 }
2057 }
2058 }
2059
2060 // Active descriptors must be ranged
2061 if (active && !parsed_desc->IsRange()) {
2063 "Active descriptors must be ranged");
2064 }
2065
2066 // Ranged descriptors should not have a label
2067 if (data.exists("range") && data.exists("label")) {
2069 "Ranged descriptors should not have a label");
2070 }
2071
2072 // Internal addresses should not have a label either
2073 if (internal && data.exists("label")) {
2075 "Internal addresses should not have a label");
2076 }
2077
2078 // Combo descriptor check
2079 if (active && !parsed_desc->IsSingleType()) {
2081 "Combo descriptors cannot be set to active");
2082 }
2083
2084 // If the wallet disabled private keys, abort if private keys exist
2085 if (pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) &&
2086 !keys.keys.empty()) {
2088 "Cannot import private keys to a wallet with "
2089 "private keys disabled");
2090 }
2091
2092 // Need to ExpandPrivate to check if private keys are available for all
2093 // pubkeys
2094 FlatSigningProvider expand_keys;
2095 std::vector<CScript> scripts;
2096 if (!parsed_desc->Expand(0, keys, scripts, expand_keys)) {
2097 throw JSONRPCError(
2099 "Cannot expand descriptor. Probably because of hardened "
2100 "derivations without private keys provided");
2101 }
2102 parsed_desc->ExpandPrivate(0, keys, expand_keys);
2103
2104 // Check if all private keys are provided
2105 bool have_all_privkeys = !expand_keys.keys.empty();
2106 for (const auto &entry : expand_keys.origins) {
2107 const CKeyID &key_id = entry.first;
2108 CKey key;
2109 if (!expand_keys.GetKey(key_id, key)) {
2110 have_all_privkeys = false;
2111 break;
2112 }
2113 }
2114
2115 // If private keys are enabled, check some things.
2116 if (!pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
2117 if (keys.keys.empty()) {
2118 throw JSONRPCError(
2120 "Cannot import descriptor without private keys to a wallet "
2121 "with private keys enabled");
2122 }
2123 if (!have_all_privkeys) {
2124 warnings.push_back(
2125 "Not all private keys provided. Some wallet functionality "
2126 "may return unexpected errors");
2127 }
2128 }
2129
2130 WalletDescriptor w_desc(std::move(parsed_desc), timestamp, range_start,
2131 range_end, next_index);
2132
2133 // Check if the wallet already contains the descriptor
2134 auto existing_spk_manager =
2135 pwallet->GetDescriptorScriptPubKeyMan(w_desc);
2136 if (existing_spk_manager &&
2137 !existing_spk_manager->CanUpdateToWalletDescriptor(w_desc, error)) {
2138 throw JSONRPCError(RPC_INVALID_PARAMETER, error);
2139 }
2140
2141 // Add descriptor to the wallet
2142 auto spk_manager =
2143 pwallet->AddWalletDescriptor(w_desc, keys, label, internal);
2144 if (spk_manager == nullptr) {
2145 throw JSONRPCError(
2147 strprintf("Could not add descriptor '%s'", descriptor));
2148 }
2149
2150 // Set descriptor as active if necessary
2151 if (active) {
2152 if (!w_desc.descriptor->GetOutputType()) {
2153 warnings.push_back(
2154 "Unknown output type, cannot set descriptor to active.");
2155 } else {
2156 pwallet->AddActiveScriptPubKeyMan(
2157 spk_manager->GetID(), *w_desc.descriptor->GetOutputType(),
2158 internal);
2159 }
2160 } else {
2161 if (w_desc.descriptor->GetOutputType()) {
2162 pwallet->DeactivateScriptPubKeyMan(
2163 spk_manager->GetID(), *w_desc.descriptor->GetOutputType(),
2164 internal);
2165 }
2166 }
2167
2168 result.pushKV("success", UniValue(true));
2169 } catch (const UniValue &e) {
2170 result.pushKV("success", UniValue(false));
2171 result.pushKV("error", e);
2172 }
2173 if (warnings.size()) {
2174 result.pushKV("warnings", warnings);
2175 }
2176 return result;
2177}
2178
2180 return RPCHelpMan{
2181 "importdescriptors",
2182 "Import descriptors. This will trigger a rescan of the blockchain "
2183 "based on the earliest timestamp of all descriptors being imported. "
2184 "Requires a new wallet backup.\n"
2185 "\nNote: This call can take over an hour to complete if using an early "
2186 "timestamp; during that time, other rpc calls\n"
2187 "may report that the imported keys, addresses or scripts exist but "
2188 "related transactions are still missing.\n",
2189 {
2190 {"requests",
2193 "Data to be imported",
2194 {
2195 {
2196 "",
2199 "",
2200 {
2202 "Descriptor to import."},
2203 {"active", RPCArg::Type::BOOL, RPCArg::Default{false},
2204 "Set this descriptor to be the active descriptor for "
2205 "the corresponding output type/externality"},
2206 {"range", RPCArg::Type::RANGE,
2208 "If a ranged descriptor is used, this specifies the "
2209 "end or the range (in the form [begin,end]) to "
2210 "import"},
2211 {"next_index", RPCArg::Type::NUM,
2213 "If a ranged descriptor is set to active, this "
2214 "specifies the next index to generate addresses "
2215 "from"},
2217 "Time from which to start rescanning the blockchain "
2218 "for this descriptor, in " +
2220 "\n"
2221 "Use the string \"now\" to substitute the "
2222 "current synced blockchain time.\n"
2223 "\"now\" can be specified to bypass scanning, "
2224 "for outputs which are known to never have been "
2225 "used, and\n"
2226 "0 can be specified to scan the entire "
2227 "blockchain. Blocks up to 2 hours before the "
2228 "earliest timestamp\n"
2229 "of all descriptors being imported will be "
2230 "scanned.",
2231 RPCArgOptions{.type_str = {"timestamp | \"now\"",
2232 "integer / string"}}},
2233 {"internal", RPCArg::Type::BOOL,
2234 RPCArg::Default{false},
2235 "Whether matching outputs should be treated as not "
2236 "incoming payments (e.g. change)"},
2237 {"label", RPCArg::Type::STR, RPCArg::Default{""},
2238 "Label to assign to the address, only allowed with "
2239 "internal=false"},
2240 },
2241 },
2242 },
2243 RPCArgOptions{.oneline_description = "\"requests\""}},
2244 },
2246 "",
2247 "Response is an array with the same size as the input that "
2248 "has the execution result",
2249 {
2251 "",
2252 "",
2253 {
2254 {RPCResult::Type::BOOL, "success", ""},
2256 "warnings",
2257 /* optional */ true,
2258 "",
2259 {
2260 {RPCResult::Type::STR, "", ""},
2261 }},
2263 "error",
2264 /* optional */ true,
2265 "",
2266 {
2267 {RPCResult::Type::ELISION, "", "JSONRPC error"},
2268 }},
2269 }},
2270 }},
2272 HelpExampleCli("importdescriptors",
2273 "'[{ \"desc\": \"<my descriptor>\", "
2274 "\"timestamp\":1455191478, \"internal\": true }, "
2275 "{ \"desc\": \"<my desccriptor 2>\", \"label\": "
2276 "\"example 2\", \"timestamp\": 1455191480 }]'") +
2278 "importdescriptors",
2279 "'[{ \"desc\": \"<my descriptor>\", \"timestamp\":1455191478, "
2280 "\"active\": true, \"range\": [0,100], \"label\": \"<my "
2281 "cashaddr wallet>\" }]'")},
2282 [&](const RPCHelpMan &self, const Config &config,
2283 const JSONRPCRequest &main_request) -> UniValue {
2284 std::shared_ptr<CWallet> const wallet =
2285 GetWalletForJSONRPCRequest(main_request);
2286 if (!wallet) {
2287 return NullUniValue;
2288 }
2289 CWallet *const pwallet = wallet.get();
2290
2291 // Make sure wallet is a descriptor wallet
2294 "importdescriptors is not available for "
2295 "non-descriptor wallets");
2296 }
2297
2298 WalletRescanReserver reserver(*pwallet);
2299 if (!reserver.reserve()) {
2301 "Wallet is currently rescanning. Abort "
2302 "existing rescan or wait.");
2303 }
2304
2305 const UniValue &requests = main_request.params[0];
2306 const int64_t minimum_timestamp = 1;
2307 int64_t now = 0;
2308 int64_t lowest_timestamp = 0;
2309 bool rescan = false;
2311 {
2312 LOCK(pwallet->cs_wallet);
2313 EnsureWalletIsUnlocked(pwallet);
2314
2315 CHECK_NONFATAL(pwallet->chain().findBlock(
2316 pwallet->GetLastBlockHash(),
2317 FoundBlock().time(lowest_timestamp).mtpTime(now)));
2318
2319 // Get all timestamps and extract the lowest timestamp
2320 for (const UniValue &request : requests.getValues()) {
2321 // This throws an error if "timestamp" doesn't exist
2322 const int64_t timestamp = std::max(
2323 GetImportTimestamp(request, now), minimum_timestamp);
2324 const UniValue result =
2325 ProcessDescriptorImport(pwallet, request, timestamp);
2326 response.push_back(result);
2327
2328 if (lowest_timestamp > timestamp) {
2329 lowest_timestamp = timestamp;
2330 }
2331
2332 // If we know the chain tip, and at least one request was
2333 // successful then allow rescan
2334 if (!rescan && result["success"].get_bool()) {
2335 rescan = true;
2336 }
2337 }
2339 }
2340
2341 // Rescan the blockchain using the lowest timestamp
2342 if (rescan) {
2343 int64_t scanned_time = pwallet->RescanFromTime(
2344 lowest_timestamp, reserver, true /* update */);
2345 {
2346 LOCK(pwallet->cs_wallet);
2347 pwallet->ReacceptWalletTransactions();
2348 }
2349
2350 if (pwallet->IsAbortingRescan()) {
2352 "Rescan aborted by user.");
2353 }
2354
2355 if (scanned_time > lowest_timestamp) {
2356 std::vector<UniValue> results = response.getValues();
2357 response.clear();
2358 response.setArray();
2359
2360 // Compose the response
2361 for (unsigned int i = 0; i < requests.size(); ++i) {
2362 const UniValue &request = requests.getValues().at(i);
2363
2364 // If the descriptor timestamp is within the
2365 // successfully scanned range, or if the import result
2366 // already has an error set, let the result stand
2367 // unmodified. Otherwise replace the result with an
2368 // error message.
2369 if (scanned_time <= GetImportTimestamp(request, now) ||
2370 results.at(i).exists("error")) {
2371 response.push_back(results.at(i));
2372 } else {
2374 result.pushKV("success", UniValue(false));
2375 result.pushKV(
2376 "error",
2380 "descriptor",
2381 GetImportTimestamp(request, now),
2382 scanned_time - TIMESTAMP_WINDOW - 1,
2383 pwallet->chain().havePruned(),
2384 pwallet->chain()
2386 response.push_back(std::move(result));
2387 }
2388 }
2389 }
2390 }
2391
2392 return response;
2393 },
2394 };
2395}
2396
2398 return RPCHelpMan{
2399 "backupwallet",
2400 "Safely copies current wallet file to destination, which can be a "
2401 "directory or a path with filename.\n",
2402 {
2403 {"destination", RPCArg::Type::STR, RPCArg::Optional::NO,
2404 "The destination directory or file"},
2405 },
2407 RPCExamples{HelpExampleCli("backupwallet", "\"backup.dat\"") +
2408 HelpExampleRpc("backupwallet", "\"backup.dat\"")},
2409 [&](const RPCHelpMan &self, const Config &config,
2410 const JSONRPCRequest &request) -> UniValue {
2411 std::shared_ptr<CWallet> const wallet =
2413 if (!wallet) {
2414 return NullUniValue;
2415 }
2416 const CWallet *const pwallet = wallet.get();
2417
2418 // Make sure the results are valid at least up to the most recent
2419 // block the user could have gotten from another RPC command prior
2420 // to now
2421 pwallet->BlockUntilSyncedToCurrentChain();
2422
2423 LOCK(pwallet->cs_wallet);
2424
2425 std::string strDest = request.params[0].get_str();
2426 if (!pwallet->BackupWallet(strDest)) {
2428 "Error: Wallet backup failed!");
2429 }
2430
2431 return NullUniValue;
2432 },
2433 };
2434}
2435
2437 return RPCHelpMan{
2438 "restorewallet",
2439 "\nRestore and loads a wallet from backup.\n",
2440 {
2441 {"wallet_name", RPCArg::Type::STR, RPCArg::Optional::NO,
2442 "The name that will be applied to the restored wallet"},
2443 {"backup_file", RPCArg::Type::STR, RPCArg::Optional::NO,
2444 "The backup file that will be used to restore the wallet."},
2445 {"load_on_startup", RPCArg::Type::BOOL, RPCArg::Optional::OMITTED,
2446 "Save wallet name to persistent settings and load on startup. "
2447 "True to add wallet to startup list, false to remove, null to "
2448 "leave unchanged."},
2449 },
2451 "",
2452 "",
2453 {
2454 {RPCResult::Type::STR, "name",
2455 "The wallet name if restored successfully."},
2456 {RPCResult::Type::STR, "warning",
2457 "Warning message if wallet was not loaded cleanly."},
2458 }},
2460 "restorewallet",
2461 "\"testwallet\" \"home\\backups\\backup-file.bak\"") +
2463 "restorewallet",
2464 "\"testwallet\" \"home\\backups\\backup-file.bak\"") +
2466 "restorewallet",
2467 {{"wallet_name", "testwallet"},
2468 {"backup_file", "home\\backups\\backup-file.bak\""},
2469 {"load_on_startup", true}}) +
2471 "restorewallet",
2472 {{"wallet_name", "testwallet"},
2473 {"backup_file", "home\\backups\\backup-file.bak\""},
2474 {"load_on_startup", true}})},
2475 [&](const RPCHelpMan &self, const Config &config,
2476 const JSONRPCRequest &request) -> UniValue {
2477 WalletContext &context = EnsureWalletContext(request.context);
2478
2479 fs::path backup_file =
2480 fs::PathFromString(request.params[1].get_str());
2481
2482 std::string wallet_name = request.params[0].get_str();
2483
2484 std::optional<bool> load_on_start =
2485 request.params[2].isNull()
2486 ? std::nullopt
2487 : std::optional<bool>(request.params[2].get_bool());
2488
2489 DatabaseStatus status;
2490 bilingual_str error;
2491 std::vector<bilingual_str> warnings;
2492
2493 const std::shared_ptr<CWallet> wallet =
2494 RestoreWallet(context, backup_file, wallet_name, load_on_start,
2495 status, error, warnings);
2496
2497 HandleWalletError(wallet, status, error);
2498
2500 obj.pushKV("name", wallet->GetName());
2501 obj.pushKV("warning", Join(warnings, Untranslated("\n")).original);
2502
2503 return obj;
2504 },
2505 };
2506}
2507
2509 // clang-format off
2510 static const CRPCCommand commands[] = {
2511 // category actor (function)
2512 // ------------------ ----------------------
2513 { "wallet", abortrescan, },
2514 { "wallet", backupwallet, },
2515 { "wallet", dumpprivkey, },
2516 { "wallet", dumpwallet, },
2517 { "wallet", dumpcoins, },
2518 { "wallet", importdescriptors, },
2519 { "wallet", importmulti, },
2520 { "wallet", importprivkey, },
2521 { "wallet", importwallet, },
2522 { "wallet", importaddress, },
2523 { "wallet", importprunedfunds, },
2524 { "wallet", importpubkey, },
2525 { "wallet", removeprunedfunds, },
2526 { "wallet", restorewallet, },
2527 };
2528 // clang-format on
2529
2530 return commands;
2531}
ArgsManager gArgs
Definition: args.cpp:39
static RPCHelpMan dumpcoins()
Definition: backup.cpp:1111
RPCHelpMan importprivkey()
Definition: backup.cpp:133
static const int64_t TIMESTAMP_MIN
Definition: backup.cpp:96
RPCHelpMan importdescriptors()
Definition: backup.cpp:2179
static void RescanWallet(CWallet &wallet, const WalletRescanReserver &reserver, int64_t time_begin=TIMESTAMP_MIN, bool update=true)
Definition: backup.cpp:98
RPCHelpMan importmulti()
Definition: backup.cpp:1717
static std::string RecurseImportData(const CScript &script, ImportData &import_data, const ScriptContext script_ctx)
Definition: backup.cpp:1204
RPCHelpMan importaddress()
Definition: backup.cpp:292
RPCHelpMan importwallet()
Definition: backup.cpp:670
Span< const CRPCCommand > GetWalletDumpRPCCommands()
Definition: backup.cpp:2508
static std::string GetRescanErrorMessage(const std::string &object, const int64_t objectTimestamp, const int64_t blockTimestamp, const bool have_pruned, const bool has_assumed_valid_chain)
Definition: backup.cpp:1683
RPCHelpMan dumpwallet()
Definition: backup.cpp:931
static UniValue ProcessImportLegacy(CWallet *const pwallet, 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: backup.cpp:1257
RPCHelpMan importpubkey()
Definition: backup.cpp:559
static std::string EncodeDumpString(const std::string &str)
Definition: backup.cpp:42
static bool GetWalletAddressesForKey(const Config &config, LegacyScriptPubKeyMan *spk_man, const CWallet *const pwallet, const CKeyID &keyid, std::string &strAddr, std::string &strLabel) EXCLUSIVE_LOCKS_REQUIRED(pwallet -> cs_wallet)
Definition: backup.cpp:69
static std::string DecodeDumpString(const std::string &str)
Definition: backup.cpp:54
RPCHelpMan importprunedfunds()
Definition: backup.cpp:427
RPCHelpMan restorewallet()
Definition: backup.cpp:2436
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: backup.cpp:1444
RPCHelpMan abortrescan()
Definition: backup.cpp:259
ScriptContext
Definition: backup.cpp:1194
@ P2SH
P2SH redeemScript.
@ TOP
Top-level scriptPubKey.
static UniValue ProcessImport(CWallet *const pwallet, const UniValue &data, const int64_t timestamp) EXCLUSIVE_LOCKS_REQUIRED(pwallet -> cs_wallet)
Definition: backup.cpp:1557
static void EnsureBlockDataFromTime(const CWallet &wallet, int64_t timestamp)
Definition: backup.cpp:111
RPCHelpMan backupwallet()
Definition: backup.cpp:2397
RPCHelpMan dumpprivkey()
Definition: backup.cpp:878
static UniValue ProcessDescriptorImport(CWallet *const pwallet, const UniValue &data, const int64_t timestamp) EXCLUSIVE_LOCKS_REQUIRED(pwallet -> cs_wallet)
Definition: backup.cpp:2000
RPCHelpMan removeprunedfunds()
Definition: backup.cpp:509
static int64_t GetImportTimestamp(const UniValue &data, int64_t now)
Definition: backup.cpp:1666
std::string WriteHDKeypath(const std::vector< uint32_t > &keypath)
Write HD keypaths as strings.
Definition: bip32.cpp:66
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:36
#define CHECK_NONFATAL(condition)
Identity function.
Definition: check.h:53
int64_t GetIntArg(const std::string &strArg, int64_t nDefault) const
Return integer argument or default value.
Definition: args.cpp:494
BlockHash GetHash() const
Definition: block.cpp:11
uint256 hashMerkleRoot
Definition: block.h:28
CKeyID seed_id
seed hash160
Definition: walletdb.h:93
An encapsulated secp256k1 private key.
Definition: key.h:28
bool IsValid() const
Check whether this private key is valid.
Definition: key.h:97
CPubKey GetPubKey() const
Compute the public key from a private key.
Definition: key.cpp:209
bool VerifyPubKey(const CPubKey &vchPubKey) const
Verify thoroughly whether a private key and a public key match.
Definition: key.cpp:301
A reference to a CKey: the Hash160 of its serialized public key.
Definition: pubkey.h:22
Used to create a Merkle proof (usually from a subset of transactions), which consists of a block head...
Definition: merkleblock.h:147
CBlockHeader header
Public only for unit testing.
Definition: merkleblock.h:150
CPartialMerkleTree txn
Definition: merkleblock.h:151
A mutable version of CTransaction.
Definition: transaction.h:274
TxId GetId() const
Compute the id and hash of this CMutableTransaction.
Definition: transaction.cpp:52
uint256 ExtractMatches(std::vector< uint256 > &vMatch, std::vector< size_t > &vnIndex)
Extract the matching txid's represented by this partial merkle tree and their respective indices with...
An encapsulated public key.
Definition: pubkey.h:31
CKeyID GetID() const
Get the KeyID of this public key (hash of its serialization)
Definition: pubkey.h:137
bool IsFullyValid() const
fully validate whether this is a valid public key (more expensive than IsValid())
Definition: pubkey.cpp:261
A reference to a CScript: the Hash160 of its serialization (see script.h)
Definition: standard.h:24
A CWallet maintains a set of transactions and balances, and provides the ability to create new transa...
Definition: wallet.h:272
BlockHash GetLastBlockHash() const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet)
Definition: wallet.h:1059
void ConnectScriptPubKeyManNotifiers()
Connect the signals from ScriptPubKeyMans to the signals in CWallet.
Definition: wallet.cpp:3437
RecursiveMutex cs_wallet
Definition: wallet.h:418
void WalletLogPrintf(std::string fmt, Params... parameters) const
Prepends the wallet name in logging output to ease debugging in multi-wallet use cases.
Definition: wallet.h:997
bool IsAbortingRescan() const
Definition: wallet.h:544
interfaces::Chain & chain() const
Interface for accessing chain state.
Definition: wallet.h:477
bool BackupWallet(const std::string &strDest) const
Definition: wallet.cpp:3210
bool IsScanning() const
Definition: wallet.h:545
std::string GetDisplayName() const override
Returns a bracketed wallet name for displaying in logs, will return [default wallet] if the wallet ha...
Definition: wallet.h:986
void AbortRescan()
Definition: wallet.h:543
const CAddressBookData * FindAddressBookEntry(const CTxDestination &, bool allow_change=false) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet)
Definition: wallet.cpp:3144
Definition: config.h:19
Double ended buffer combining vector and stream-like interfaces.
Definition: streams.h:118
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
A Span is an object that can refer to a contiguous sequence of objects.
Definition: span.h:94
void push_back(UniValue val)
Definition: univalue.cpp:96
const std::string & get_str() const
enum VType getType() const
Definition: univalue.h:88
@ VOBJ
Definition: univalue.h:31
@ VSTR
Definition: univalue.h:33
@ VARR
Definition: univalue.h:32
size_t size() const
Definition: univalue.h:92
enum VType type() const
Definition: univalue.h:147
const std::vector< UniValue > & getValues() const
bool isStr() const
Definition: univalue.h:108
Int getInt() const
Definition: univalue.h:157
const UniValue & get_array() const
bool exists(const std::string &key) const
Definition: univalue.h:99
bool isNum() const
Definition: univalue.h:109
void pushKV(std::string key, UniValue val)
Definition: univalue.cpp:115
bool get_bool() const
Descriptor with some wallet metadata.
Definition: walletutil.h:80
std::shared_ptr< Descriptor > descriptor
Definition: walletutil.h:82
RAII object to check and reserve a wallet rescan.
Definition: wallet.h:1132
bool IsNull() const
Definition: uint256.h:32
Path class wrapper to block calls to the fs::path(std::string) implicit constructor and the fs::path:...
Definition: fs.h:30
std::string u8string() const
Definition: fs.h:72
virtual bool findAncestorByHash(const BlockHash &block_hash, const BlockHash &ancestor_hash, const FoundBlock &ancestor_out={})=0
Return whether block descends from a specified ancestor, and optionally return ancestor information.
virtual bool findBlock(const BlockHash &hash, const FoundBlock &block={})=0
Return whether node has the block and optionally return block metadata or contents.
virtual void showProgress(const std::string &title, int progress, bool resume_possible)=0
Send progress indicator.
virtual bool havePruned()=0
Check if any block has been pruned.
virtual bool hasAssumedValidChain()=0
Return true if an assumed-valid chain is in use.
Helper for findBlock to selectively return pieces of block data.
Definition: chain.h:55
160-bit opaque blob.
Definition: uint256.h:117
256-bit opaque blob.
Definition: uint256.h:129
static UniValue Parse(std::string_view raw)
Parse string to UniValue or throw runtime_error if string contains invalid JSON.
Definition: client.cpp:229
const std::string CLIENT_BUILD
const std::string CLIENT_NAME
bool DecodeHexTx(CMutableTransaction &tx, const std::string &strHexTx)
Definition: core_read.cpp:196
bool SetAddressBook(const CTxDestination &address, const std::string &strName, const std::string &purpose)
Definition: wallet.cpp:2333
DBErrors ZapSelectTx(std::vector< TxId > &txIdsIn, std::vector< TxId > &txIdsOut) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet)
Definition: wallet.cpp:2274
bool ImportPubKeys(const std::vector< CKeyID > &ordered_pubkeys, const std::map< CKeyID, CPubKey > &pubkey_map, const std::map< CKeyID, std::pair< CPubKey, KeyOriginInfo > > &key_origins, const bool add_keypool, const bool internal, const int64_t timestamp) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet)
Definition: wallet.cpp:1684
void MarkDirty()
Definition: wallet.cpp:967
bool ImportScripts(const std::set< CScript > scripts, int64_t timestamp) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet)
Definition: wallet.cpp:1664
CWalletTx * AddToWallet(CTransactionRef tx, const CWalletTx::Confirmation &confirm, const UpdateWalletTxFn &update_wtx=nullptr, bool fFlushOnClose=true)
Definition: wallet.cpp:1025
bool ImportPrivKeys(const std::map< CKeyID, CKey > &privkey_map, const int64_t timestamp) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet)
Definition: wallet.cpp:1674
isminetype IsMine(const CTxDestination &dest) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet)
Definition: wallet.cpp:1501
bool ImportScriptPubKeys(const std::string &label, const std::set< CScript > &script_pub_keys, const bool have_solving_data, const bool apply_label, const int64_t timestamp) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet)
Definition: wallet.cpp:1698
const CChainParams & GetChainParams() const override
Definition: wallet.cpp:452
void ReacceptWalletTransactions() EXCLUSIVE_LOCKS_REQUIRED(cs_wallet)
Definition: wallet.cpp:1925
bool IsWalletFlagSet(uint64_t flag) const override
Check if a certain wallet flag is set.
Definition: wallet.cpp:1594
int64_t RescanFromTime(int64_t startTime, const WalletRescanReserver &reserver, bool update)
Scan active chain for relevant transactions after importing keys.
Definition: wallet.cpp:1733
std::string HexStr(const Span< const uint8_t > s)
Convert a span of bytes to a lower-case hexadecimal string.
Definition: hex_base.cpp:30
@ ISMINE_SPENDABLE
Definition: ismine.h:21
std::string EncodeDestination(const CTxDestination &dest, const Config &config)
Definition: key_io.cpp:167
std::string EncodeExtKey(const CExtKey &key)
Definition: key_io.cpp:156
std::string EncodeSecret(const CKey &key)
Definition: key_io.cpp:102
CTxDestination DecodeDestination(const std::string &addr, const CChainParams &params)
Definition: key_io.cpp:174
CKey DecodeSecret(const std::string &str)
Definition: key_io.cpp:77
static path absolute(const path &p)
Definition: fs.h:101
static path u8path(const std::string &utf8_str)
Definition: fs.h:90
static bool exists(const path &p)
Definition: fs.h:107
static path PathFromString(const std::string &string)
Convert byte string to path object.
Definition: fs.h:170
std::vector< std::string > SplitString(std::string_view str, char sep)
Definition: string.h:59
auto Join(const std::vector< T > &list, const BaseType &separator, UnaryOp unary_op) -> decltype(unary_op(list.at(0)))
Join a list of items.
Definition: string.h:105
CTxDestination GetDestinationForKey(const CPubKey &key, OutputType type)
Get a destination of the requested type (if possible) to the specified key.
Definition: outputtype.cpp:35
std::vector< CTxDestination > GetAllDestinationsForKey(const CPubKey &key)
Get all destinations (potentially) supported by the wallet for the given key.
Definition: outputtype.cpp:43
static CTransactionRef MakeTransactionRef()
Definition: transaction.h:316
std::shared_ptr< const CTransaction > CTransactionRef
Definition: transaction.h:315
Response response
Definition: processor.cpp:522
UniValue JSONRPCError(int code, const std::string &message)
Definition: request.cpp:58
@ RPC_MISC_ERROR
General application defined errors std::exception thrown in command handling.
Definition: protocol.h:38
@ RPC_TYPE_ERROR
Unexpected type was passed as parameter.
Definition: protocol.h:40
@ RPC_INVALID_PARAMETER
Invalid, missing or duplicate parameter.
Definition: protocol.h:46
@ RPC_WALLET_ERROR
Wallet errors Unspecified problem with wallet (key not found etc.)
Definition: protocol.h:90
@ RPC_DESERIALIZATION_ERROR
Error parsing or validating structure in raw format.
Definition: protocol.h:50
@ RPC_INVALID_ADDRESS_OR_KEY
Invalid address or key.
Definition: protocol.h:42
std::pair< int64_t, int64_t > ParseDescriptorRange(const UniValue &value)
Parse a JSON range specified as int64, or [int64, int64].
Definition: util.cpp:1365
std::string HelpExampleCli(const std::string &methodname, const std::string &args)
Definition: util.cpp:163
std::string HelpExampleRpcNamed(const std::string &methodname, const RPCArgList &args)
Definition: util.cpp:189
std::vector< uint8_t > ParseHexV(const UniValue &v, std::string strName)
Definition: util.cpp:107
std::string HelpExampleRpc(const std::string &methodname, const std::string &args)
Definition: util.cpp:180
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:35
uint256 ParseHashV(const UniValue &v, std::string strName)
Utilities: convert hex-encoded values (throws error if not hex).
Definition: util.cpp:86
std::string HelpExampleCliNamed(const std::string &methodname, const RPCArgList &args)
Definition: util.cpp:168
static const unsigned int DEFAULT_KEYPOOL_SIZE
Default for -keypool.
CKeyID GetKeyForDestination(const SigningProvider &store, const CTxDestination &dest)
Return the CKeyID of the key involved in a script (if there is a unique one).
std::map< CTxDestination, std::vector< COutput > > ListCoins(const CWallet &wallet)
Return list of available coins and locked coins grouped by non-change output address.
Definition: spend.cpp:255
bool ExtractDestination(const CScript &scriptPubKey, CTxDestination &addressRet)
Parse a standard scriptPubKey for the destination address.
Definition: standard.cpp:158
TxoutType Solver(const CScript &scriptPubKey, std::vector< std::vector< uint8_t > > &vSolutionsRet)
Parse a scriptPubKey and identify script type for standard scripts.
Definition: standard.cpp:108
bool IsValidDestination(const CTxDestination &dest)
Check whether a CTxDestination is a CNoDestination.
Definition: standard.cpp:260
CScript GetScriptForDestination(const CTxDestination &dest)
Generate a Bitcoin scriptPubKey for the given CTxDestination.
Definition: standard.cpp:240
TxoutType
Definition: standard.h:38
std::variant< CNoDestination, PKHash, ScriptHash > CTxDestination
A txout script template with a specific destination.
Definition: standard.h:85
A BlockHash is a unqiue identifier for a block.
Definition: blockhash.h:13
Definition: key.h:167
void SetSeed(Span< const std::byte > seed)
Definition: key.cpp:381
Confirmation includes tx status and a triplet of {block height/block hash/tx index in block} at which...
Definition: transaction.h:181
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: backup.cpp:1184
std::map< CKeyID, bool > used_keys
Import these private keys if available (the value indicates whether if the key is required for solvab...
Definition: backup.cpp:1190
std::set< CScript > import_scripts
Definition: backup.cpp:1187
std::map< CKeyID, std::pair< CPubKey, KeyOriginInfo > > key_origins
Definition: backup.cpp:1191
@ RANGE
Special type that is a NUM or [NUM,NUM].
@ STR_HEX
Special type that is a STR with only hex chars.
@ OBJ_NAMED_PARAMS
Special type that behaves almost exactly like OBJ, defining an options object with a list of pre-defi...
std::string DefaultHint
Hint for default value.
Definition: util.h:212
@ OMITTED
Optional argument for which the default value is omitted from help text for one of two reasons:
@ NO
Required arg.
std::vector< std::string > type_str
Should be empty unless it is supposed to override the auto-generated type strings.
Definition: util.h:155
std::string oneline_description
Should be empty unless it is supposed to override the auto-generated summary line.
Definition: util.h:149
@ ELISION
Special type to denote elision (...)
@ OBJ_DYN
Special dictionary with keys that are not literals.
@ STR_HEX
Special string with only hex chars.
@ STR_AMOUNT
Special string to represent a floating point amount.
A TxId is the identifier of a transaction.
Definition: txid.h:14
WalletContext struct containing references to state shared between CWallet instances,...
Definition: context.h:35
Bilingual messages:
Definition: translation.h:17
#define LOCK2(cs1, cs2)
Definition: sync.h:309
#define LOCK(cs)
Definition: sync.h:306
#define WITH_LOCK(cs, code)
Run code while locking a mutex.
Definition: sync.h:357
#define EXCLUSIVE_LOCKS_REQUIRED(...)
Definition: threadsafety.h:56
int64_t GetTime()
DEPRECATED Use either ClockType::now() or Now<TimePointType>() if a cast is needed.
Definition: time.cpp:62
std::optional< int64_t > ParseISO8601DateTime(std::string_view str)
Definition: time.cpp:103
std::string FormatISO8601DateTime(int64_t nTime)
ISO 8601 formatting is preferred.
Definition: time.cpp:78
#define strprintf
Format arguments and return the string or write to given std::ostream (see tinyformat::format doc for...
Definition: tinyformat.h:1202
bilingual_str _(const char *psz)
Translation function.
Definition: translation.h:68
bilingual_str Untranslated(std::string original)
Mark a bilingual_str as untranslated.
Definition: translation.h:36
const UniValue NullUniValue
Definition: univalue.cpp:16
const char * uvTypeName(UniValue::VType t)
Definition: univalue.cpp:209
template std::vector< std::byte > ParseHex(std::string_view)
bool IsHex(std::string_view str)
Returns true if each character in str is a hex character, and has an even number of hex digits.
DatabaseStatus
Definition: db.h:227
void EnsureWalletIsUnlocked(const CWallet *pwallet)
Definition: util.cpp:96
std::shared_ptr< CWallet > GetWalletForJSONRPCRequest(const JSONRPCRequest &request)
Figures out what wallet, if any, to use for a JSONRPCRequest.
Definition: util.cpp:63
LegacyScriptPubKeyMan & EnsureLegacyScriptPubKeyMan(CWallet &wallet, bool also_create)
Definition: util.cpp:114
void HandleWalletError(const std::shared_ptr< CWallet > wallet, DatabaseStatus &status, bilingual_str &error)
Definition: util.cpp:135
WalletContext & EnsureWalletContext(const std::any &context)
Definition: util.cpp:104
std::shared_ptr< CWallet > RestoreWallet(WalletContext &context, const fs::path &backup_file, const std::string &wallet_name, std::optional< bool > load_on_start, DatabaseStatus &status, bilingual_str &error, std::vector< bilingual_str > &warnings)
Definition: wallet.cpp:402
@ WALLET_FLAG_DISABLE_PRIVATE_KEYS
Definition: walletutil.h:55
@ WALLET_FLAG_DESCRIPTORS
Indicate that this wallet supports DescriptorScriptPubKeyMan.
Definition: walletutil.h:70