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