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