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