backup.cpp raw

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