/ src / wallet / rpc / wallet.cpp
wallet.cpp
  1  // Copyright (c) 2010 Satoshi Nakamoto
  2  // Copyright (c) 2009-present The Bitcoin Core developers
  3  // Distributed under the MIT software license, see the accompanying
  4  // file COPYING or http://www.opensource.org/licenses/mit-license.php.
  5  
  6  #include <bitcoin-build-config.h> // IWYU pragma: keep
  7  
  8  #include <core_io.h>
  9  #include <key_io.h>
 10  #include <rpc/server.h>
 11  #include <rpc/util.h>
 12  #include <univalue.h>
 13  #include <util/translation.h>
 14  #include <wallet/context.h>
 15  #include <wallet/receive.h>
 16  #include <wallet/rpc/util.h>
 17  #include <wallet/rpc/wallet.h>
 18  #include <wallet/wallet.h>
 19  #include <wallet/walletutil.h>
 20  
 21  #include <optional>
 22  #include <string_view>
 23  
 24  
 25  namespace wallet {
 26  
 27  static const std::map<uint64_t, std::string> WALLET_FLAG_CAVEATS{
 28      {WALLET_FLAG_AVOID_REUSE,
 29       "You need to rescan the blockchain in order to correctly mark used "
 30       "destinations in the past. Until this is done, some destinations may "
 31       "be considered unused, even if the opposite is the case."},
 32  };
 33  
 34  static RPCHelpMan getwalletinfo()
 35  {
 36      return RPCHelpMan{"getwalletinfo",
 37                  "Returns an object containing various wallet state info.\n",
 38                  {},
 39                  RPCResult{
 40                      RPCResult::Type::OBJ, "", "",
 41                      {
 42                          {
 43                          {RPCResult::Type::STR, "walletname", "the wallet name"},
 44                          {RPCResult::Type::NUM, "walletversion", "(DEPRECATED) only related to unsupported legacy wallet, returns the latest version 169900 for backwards compatibility"},
 45                          {RPCResult::Type::STR, "format", "the database format (only sqlite)"},
 46                          {RPCResult::Type::NUM, "txcount", "the total number of transactions in the wallet"},
 47                          {RPCResult::Type::NUM, "keypoolsize", "how many new keys are pre-generated (only counts external keys)"},
 48                          {RPCResult::Type::NUM, "keypoolsize_hd_internal", /*optional=*/true, "how many new keys are pre-generated for internal use (used for change outputs, only appears if the wallet is using this feature, otherwise external keys are used)"},
 49                          {RPCResult::Type::NUM_TIME, "unlocked_until", /*optional=*/true, "the " + UNIX_EPOCH_TIME + " until which the wallet is unlocked for transfers, or 0 if the wallet is locked (only present for passphrase-encrypted wallets)"},
 50                          {RPCResult::Type::BOOL, "private_keys_enabled", "false if privatekeys are disabled for this wallet (enforced watch-only wallet)"},
 51                          {RPCResult::Type::BOOL, "avoid_reuse", "whether this wallet tracks clean/dirty coins in terms of reuse"},
 52                          {RPCResult::Type::OBJ, "scanning", "current scanning details, or false if no scan is in progress",
 53                          {
 54                              {RPCResult::Type::NUM, "duration", "elapsed seconds since scan start"},
 55                              {RPCResult::Type::NUM, "progress", "scanning progress percentage [0.0, 1.0]"},
 56                          }, /*skip_type_check=*/true},
 57                          {RPCResult::Type::BOOL, "descriptors", "whether this wallet uses descriptors for output script management"},
 58                          {RPCResult::Type::BOOL, "external_signer", "whether this wallet is configured to use an external signer such as a hardware wallet"},
 59                          {RPCResult::Type::BOOL, "blank", "Whether this wallet intentionally does not contain any keys, scripts, or descriptors"},
 60                          {RPCResult::Type::NUM_TIME, "birthtime", /*optional=*/true, "The start time for blocks scanning. It could be modified by (re)importing any descriptor with an earlier timestamp."},
 61                          {RPCResult::Type::ARR, "flags", "The flags currently set on the wallet",
 62                          {
 63                              {RPCResult::Type::STR, "flag", "The name of the flag"},
 64                          }},
 65                          RESULT_LAST_PROCESSED_BLOCK,
 66                      }},
 67                  },
 68                  RPCExamples{
 69                      HelpExampleCli("getwalletinfo", "")
 70              + HelpExampleRpc("getwalletinfo", "")
 71                  },
 72          [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
 73  {
 74      const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
 75      if (!pwallet) return UniValue::VNULL;
 76  
 77      // Make sure the results are valid at least up to the most recent block
 78      // the user could have gotten from another RPC command prior to now
 79      pwallet->BlockUntilSyncedToCurrentChain();
 80  
 81      LOCK(pwallet->cs_wallet);
 82  
 83      UniValue obj(UniValue::VOBJ);
 84  
 85      const int latest_legacy_wallet_minversion{169900};
 86  
 87      size_t kpExternalSize = pwallet->KeypoolCountExternalKeys();
 88      obj.pushKV("walletname", pwallet->GetName());
 89      obj.pushKV("walletversion", latest_legacy_wallet_minversion);
 90      obj.pushKV("format", pwallet->GetDatabase().Format());
 91      obj.pushKV("txcount", pwallet->mapWallet.size());
 92      obj.pushKV("keypoolsize", kpExternalSize);
 93      obj.pushKV("keypoolsize_hd_internal", pwallet->GetKeyPoolSize() - kpExternalSize);
 94  
 95      if (pwallet->HasEncryptionKeys()) {
 96          obj.pushKV("unlocked_until", pwallet->nRelockTime);
 97      }
 98      obj.pushKV("private_keys_enabled", !pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS));
 99      obj.pushKV("avoid_reuse", pwallet->IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE));
100      if (pwallet->IsScanning()) {
101          UniValue scanning(UniValue::VOBJ);
102          scanning.pushKV("duration", Ticks<std::chrono::seconds>(pwallet->ScanningDuration()));
103          scanning.pushKV("progress", pwallet->ScanningProgress());
104          obj.pushKV("scanning", std::move(scanning));
105      } else {
106          obj.pushKV("scanning", false);
107      }
108      obj.pushKV("descriptors", pwallet->IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS));
109      obj.pushKV("external_signer", pwallet->IsWalletFlagSet(WALLET_FLAG_EXTERNAL_SIGNER));
110      obj.pushKV("blank", pwallet->IsWalletFlagSet(WALLET_FLAG_BLANK_WALLET));
111      if (int64_t birthtime = pwallet->GetBirthTime(); birthtime != UNKNOWN_TIME) {
112          obj.pushKV("birthtime", birthtime);
113      }
114  
115      // Push known flags
116      UniValue flags(UniValue::VARR);
117      uint64_t wallet_flags = pwallet->GetWalletFlags();
118      for (uint64_t i = 0; i < 64; ++i) {
119          uint64_t flag = uint64_t{1} << i;
120          if (flag & wallet_flags) {
121              if (flag & KNOWN_WALLET_FLAGS) {
122                  flags.push_back(WALLET_FLAG_TO_STRING.at(WalletFlags{flag}));
123              } else {
124                  flags.push_back(strprintf("unknown_flag_%u", i));
125              }
126          }
127      }
128      obj.pushKV("flags", flags);
129  
130      AppendLastProcessedBlock(obj, *pwallet);
131      return obj;
132  },
133      };
134  }
135  
136  static RPCHelpMan listwalletdir()
137  {
138      return RPCHelpMan{"listwalletdir",
139                  "Returns a list of wallets in the wallet directory.\n",
140                  {},
141                  RPCResult{
142                      RPCResult::Type::OBJ, "", "",
143                      {
144                          {RPCResult::Type::ARR, "wallets", "",
145                          {
146                              {RPCResult::Type::OBJ, "", "",
147                              {
148                                  {RPCResult::Type::STR, "name", "The wallet name"},
149                                  {RPCResult::Type::ARR, "warnings", /*optional=*/true, "Warning messages, if any, related to loading the wallet.",
150                                  {
151                                      {RPCResult::Type::STR, "", ""},
152                                  }},
153                              }},
154                          }},
155                      }
156                  },
157                  RPCExamples{
158                      HelpExampleCli("listwalletdir", "")
159              + HelpExampleRpc("listwalletdir", "")
160                  },
161          [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
162  {
163      UniValue wallets(UniValue::VARR);
164      for (const auto& [path, db_type] : ListDatabases(GetWalletDir())) {
165          UniValue wallet(UniValue::VOBJ);
166          wallet.pushKV("name", path.utf8string());
167                  UniValue warnings(UniValue::VARR);
168          if (db_type == "bdb") {
169              warnings.push_back("This wallet is a legacy wallet and will need to be migrated with migratewallet before it can be loaded");
170          }
171          wallet.pushKV("warnings", warnings);
172          wallets.push_back(std::move(wallet));
173      }
174  
175      UniValue result(UniValue::VOBJ);
176      result.pushKV("wallets", std::move(wallets));
177      return result;
178  },
179      };
180  }
181  
182  static RPCHelpMan listwallets()
183  {
184      return RPCHelpMan{"listwallets",
185                  "Returns a list of currently loaded wallets.\n"
186                  "For full information on the wallet, use \"getwalletinfo\"\n",
187                  {},
188                  RPCResult{
189                      RPCResult::Type::ARR, "", "",
190                      {
191                          {RPCResult::Type::STR, "walletname", "the wallet name"},
192                      }
193                  },
194                  RPCExamples{
195                      HelpExampleCli("listwallets", "")
196              + HelpExampleRpc("listwallets", "")
197                  },
198          [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
199  {
200      UniValue obj(UniValue::VARR);
201  
202      WalletContext& context = EnsureWalletContext(request.context);
203      for (const std::shared_ptr<CWallet>& wallet : GetWallets(context)) {
204          LOCK(wallet->cs_wallet);
205          obj.push_back(wallet->GetName());
206      }
207  
208      return obj;
209  },
210      };
211  }
212  
213  static RPCHelpMan loadwallet()
214  {
215      return RPCHelpMan{
216          "loadwallet",
217          "Loads a wallet from a wallet file or directory."
218                  "\nNote that all wallet command-line options used when starting bitcoind will be"
219                  "\napplied to the new wallet.\n",
220                  {
221                      {"filename", RPCArg::Type::STR, RPCArg::Optional::NO, "The path to the directory of the wallet to be loaded, either absolute or relative to the \"wallets\" directory. The \"wallets\" directory is set by the -walletdir option and defaults to the \"wallets\" folder within the data directory."},
222                      {"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."},
223                  },
224                  RPCResult{
225                      RPCResult::Type::OBJ, "", "",
226                      {
227                          {RPCResult::Type::STR, "name", "The wallet name if loaded successfully."},
228                          {RPCResult::Type::ARR, "warnings", /*optional=*/true, "Warning messages, if any, related to loading the wallet.",
229                          {
230                              {RPCResult::Type::STR, "", ""},
231                          }},
232                      }
233                  },
234                  RPCExamples{
235                      "\nLoad wallet from the wallet dir:\n"
236                      + HelpExampleCli("loadwallet", "\"walletname\"")
237                      + HelpExampleRpc("loadwallet", "\"walletname\"")
238                      + "\nLoad wallet using absolute path (Unix):\n"
239                      + HelpExampleCli("loadwallet", "\"/path/to/walletname/\"")
240                      + HelpExampleRpc("loadwallet", "\"/path/to/walletname/\"")
241                      + "\nLoad wallet using absolute path (Windows):\n"
242                      + HelpExampleCli("loadwallet", "\"DriveLetter:\\path\\to\\walletname\\\"")
243                      + HelpExampleRpc("loadwallet", "\"DriveLetter:\\path\\to\\walletname\\\"")
244                  },
245          [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
246  {
247      WalletContext& context = EnsureWalletContext(request.context);
248      const std::string name(request.params[0].get_str());
249  
250      DatabaseOptions options;
251      DatabaseStatus status;
252      ReadDatabaseArgs(*context.args, options);
253      options.require_existing = true;
254      bilingual_str error;
255      std::vector<bilingual_str> warnings;
256      std::optional<bool> load_on_start = request.params[1].isNull() ? std::nullopt : std::optional<bool>(request.params[1].get_bool());
257  
258      {
259          LOCK(context.wallets_mutex);
260          if (std::any_of(context.wallets.begin(), context.wallets.end(), [&name](const auto& wallet) { return wallet->GetName() == name; })) {
261              throw JSONRPCError(RPC_WALLET_ALREADY_LOADED, "Wallet \"" + name + "\" is already loaded.");
262          }
263      }
264  
265      std::shared_ptr<CWallet> const wallet = LoadWallet(context, name, load_on_start, options, status, error, warnings);
266  
267      HandleWalletError(wallet, status, error);
268  
269      UniValue obj(UniValue::VOBJ);
270      obj.pushKV("name", wallet->GetName());
271      PushWarnings(warnings, obj);
272  
273      return obj;
274  },
275      };
276  }
277  
278  static RPCHelpMan setwalletflag()
279  {
280              std::string flags;
281              for (auto& it : STRING_TO_WALLET_FLAG)
282                  if (it.second & MUTABLE_WALLET_FLAGS)
283                      flags += (flags == "" ? "" : ", ") + it.first;
284  
285      return RPCHelpMan{
286          "setwalletflag",
287          "Change the state of the given wallet flag for a wallet.\n",
288                  {
289                      {"flag", RPCArg::Type::STR, RPCArg::Optional::NO, "The name of the flag to change. Current available flags: " + flags},
290                      {"value", RPCArg::Type::BOOL, RPCArg::Default{true}, "The new state."},
291                  },
292                  RPCResult{
293                      RPCResult::Type::OBJ, "", "",
294                      {
295                          {RPCResult::Type::STR, "flag_name", "The name of the flag that was modified"},
296                          {RPCResult::Type::BOOL, "flag_state", "The new state of the flag"},
297                          {RPCResult::Type::STR, "warnings", /*optional=*/true, "Any warnings associated with the change"},
298                      }
299                  },
300                  RPCExamples{
301                      HelpExampleCli("setwalletflag", "avoid_reuse")
302                    + HelpExampleRpc("setwalletflag", "\"avoid_reuse\"")
303                  },
304          [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
305  {
306      std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
307      if (!pwallet) return UniValue::VNULL;
308  
309      std::string flag_str = request.params[0].get_str();
310      bool value = request.params[1].isNull() || request.params[1].get_bool();
311  
312      if (!STRING_TO_WALLET_FLAG.contains(flag_str)) {
313          throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Unknown wallet flag: %s", flag_str));
314      }
315  
316      auto flag = STRING_TO_WALLET_FLAG.at(flag_str);
317  
318      if (!(flag & MUTABLE_WALLET_FLAGS)) {
319          throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Wallet flag is immutable: %s", flag_str));
320      }
321  
322      UniValue res(UniValue::VOBJ);
323  
324      if (pwallet->IsWalletFlagSet(flag) == value) {
325          throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Wallet flag is already set to %s: %s", value ? "true" : "false", flag_str));
326      }
327  
328      res.pushKV("flag_name", flag_str);
329      res.pushKV("flag_state", value);
330  
331      if (value) {
332          pwallet->SetWalletFlag(flag);
333      } else {
334          pwallet->UnsetWalletFlag(flag);
335      }
336  
337      if (flag && value && WALLET_FLAG_CAVEATS.contains(flag)) {
338          res.pushKV("warnings", WALLET_FLAG_CAVEATS.at(flag));
339      }
340  
341      return res;
342  },
343      };
344  }
345  
346  static RPCHelpMan createwallet()
347  {
348      return RPCHelpMan{
349          "createwallet",
350          "Creates and loads a new wallet.\n",
351          {
352              {"wallet_name", RPCArg::Type::STR, RPCArg::Optional::NO, "The name for the new wallet. If this is a path, the wallet will be created at the path location."},
353              {"disable_private_keys", RPCArg::Type::BOOL, RPCArg::Default{false}, "Disable the possibility of private keys (only watchonlys are possible in this mode)."},
354              {"blank", RPCArg::Type::BOOL, RPCArg::Default{false}, "Create a blank wallet. A blank wallet has no keys."},
355              {"passphrase", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "Encrypt the wallet with this passphrase."},
356              {"avoid_reuse", RPCArg::Type::BOOL, RPCArg::Default{false}, "Keep track of coin reuse, and treat dirty and clean coins differently with privacy considerations in mind."},
357              {"descriptors", RPCArg::Type::BOOL, RPCArg::Default{true}, "If set, must be \"true\""},
358              {"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."},
359              {"external_signer", RPCArg::Type::BOOL, RPCArg::Default{false}, "Use an external signer such as a hardware wallet. Requires -signer to be configured. Wallet creation will fail if keys cannot be fetched. Requires disable_private_keys and descriptors set to true."},
360          },
361          RPCResult{
362              RPCResult::Type::OBJ, "", "",
363              {
364                  {RPCResult::Type::STR, "name", "The wallet name if created successfully. If the wallet was created using a full path, the wallet_name will be the full path."},
365                  {RPCResult::Type::ARR, "warnings", /*optional=*/true, "Warning messages, if any, related to creating and loading the wallet.",
366                  {
367                      {RPCResult::Type::STR, "", ""},
368                  }},
369              }
370          },
371          RPCExamples{
372              HelpExampleCli("createwallet", "\"testwallet\"")
373              + HelpExampleRpc("createwallet", "\"testwallet\"")
374              + HelpExampleCliNamed("createwallet", {{"wallet_name", "descriptors"}, {"avoid_reuse", true}, {"load_on_startup", true}})
375              + HelpExampleRpcNamed("createwallet", {{"wallet_name", "descriptors"}, {"avoid_reuse", true}, {"load_on_startup", true}})
376          },
377          [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
378  {
379      WalletContext& context = EnsureWalletContext(request.context);
380      uint64_t flags = 0;
381      if (!request.params[1].isNull() && request.params[1].get_bool()) {
382          flags |= WALLET_FLAG_DISABLE_PRIVATE_KEYS;
383      }
384  
385      if (!request.params[2].isNull() && request.params[2].get_bool()) {
386          flags |= WALLET_FLAG_BLANK_WALLET;
387      }
388      SecureString passphrase;
389      passphrase.reserve(100);
390      std::vector<bilingual_str> warnings;
391      if (!request.params[3].isNull()) {
392          passphrase = std::string_view{request.params[3].get_str()};
393          if (passphrase.empty()) {
394              // Empty string means unencrypted
395              warnings.emplace_back(Untranslated("Empty string given as passphrase, wallet will not be encrypted."));
396          }
397      }
398  
399      if (!request.params[4].isNull() && request.params[4].get_bool()) {
400          flags |= WALLET_FLAG_AVOID_REUSE;
401      }
402      flags |= WALLET_FLAG_DESCRIPTORS;
403      if (!self.Arg<bool>("descriptors")) {
404          throw JSONRPCError(RPC_WALLET_ERROR, "descriptors argument must be set to \"true\"; it is no longer possible to create a legacy wallet.");
405      }
406      if (!request.params[7].isNull() && request.params[7].get_bool()) {
407  #ifdef ENABLE_EXTERNAL_SIGNER
408          flags |= WALLET_FLAG_EXTERNAL_SIGNER;
409  #else
410          throw JSONRPCError(RPC_WALLET_ERROR, "Compiled without external signing support (required for external signing)");
411  #endif
412      }
413  
414      DatabaseOptions options;
415      DatabaseStatus status;
416      ReadDatabaseArgs(*context.args, options);
417      options.require_create = true;
418      options.create_flags = flags;
419      options.create_passphrase = passphrase;
420      bilingual_str error;
421      std::optional<bool> load_on_start = request.params[6].isNull() ? std::nullopt : std::optional<bool>(request.params[6].get_bool());
422      const std::shared_ptr<CWallet> wallet = CreateWallet(context, request.params[0].get_str(), load_on_start, options, status, error, warnings);
423      HandleWalletError(wallet, status, error);
424  
425      UniValue obj(UniValue::VOBJ);
426      obj.pushKV("name", wallet->GetName());
427      PushWarnings(warnings, obj);
428  
429      return obj;
430  },
431      };
432  }
433  
434  static RPCHelpMan unloadwallet()
435  {
436      return RPCHelpMan{"unloadwallet",
437                  "Unloads the wallet referenced by the request endpoint or the wallet_name argument.\n"
438                  "If both are specified, they must be identical.",
439                  {
440                      {"wallet_name", RPCArg::Type::STR, RPCArg::DefaultHint{"the wallet name from the RPC endpoint"}, "The name of the wallet to unload. If provided both here and in the RPC endpoint, the two must be identical."},
441                      {"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."},
442                  },
443                  RPCResult{RPCResult::Type::OBJ, "", "", {
444                      {RPCResult::Type::ARR, "warnings", /*optional=*/true, "Warning messages, if any, related to unloading the wallet.",
445                      {
446                          {RPCResult::Type::STR, "", ""},
447                      }},
448                  }},
449                  RPCExamples{
450                      HelpExampleCli("unloadwallet", "wallet_name")
451              + HelpExampleRpc("unloadwallet", "wallet_name")
452                  },
453          [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
454  {
455      const std::string wallet_name{EnsureUniqueWalletName(request, self.MaybeArg<std::string_view>("wallet_name"))};
456  
457      WalletContext& context = EnsureWalletContext(request.context);
458      std::shared_ptr<CWallet> wallet = GetWallet(context, wallet_name);
459      if (!wallet) {
460          throw JSONRPCError(RPC_WALLET_NOT_FOUND, "Requested wallet does not exist or is not loaded");
461      }
462  
463      std::vector<bilingual_str> warnings;
464      {
465          WalletRescanReserver reserver(*wallet);
466          if (!reserver.reserve()) {
467              throw JSONRPCError(RPC_WALLET_ERROR, "Wallet is currently rescanning. Abort existing rescan or wait.");
468          }
469  
470          // Release the "main" shared pointer and prevent further notifications.
471          // Note that any attempt to load the same wallet would fail until the wallet
472          // is destroyed (see CheckUniqueFileid).
473          std::optional<bool> load_on_start{self.MaybeArg<bool>("load_on_startup")};
474          if (!RemoveWallet(context, wallet, load_on_start, warnings)) {
475              throw JSONRPCError(RPC_MISC_ERROR, "Requested wallet already unloaded");
476          }
477      }
478  
479      WaitForDeleteWallet(std::move(wallet));
480  
481      UniValue result(UniValue::VOBJ);
482      PushWarnings(warnings, result);
483  
484      return result;
485  },
486      };
487  }
488  
489  RPCHelpMan simulaterawtransaction()
490  {
491      return RPCHelpMan{
492          "simulaterawtransaction",
493          "Calculate the balance change resulting in the signing and broadcasting of the given transaction(s).\n",
494          {
495              {"rawtxs", RPCArg::Type::ARR, RPCArg::Optional::OMITTED, "An array of hex strings of raw transactions.\n",
496                  {
497                      {"rawtx", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, ""},
498                  },
499              },
500              {"options", RPCArg::Type::OBJ_NAMED_PARAMS, RPCArg::Optional::OMITTED, "",
501                  {
502                      {"include_watchonly", RPCArg::Type::BOOL, RPCArg::Default{false}, "(DEPRECATED) No longer used"},
503                  },
504              },
505          },
506          RPCResult{
507              RPCResult::Type::OBJ, "", "",
508              {
509                  {RPCResult::Type::STR_AMOUNT, "balance_change", "The wallet balance change (negative means decrease)."},
510              }
511          },
512          RPCExamples{
513              HelpExampleCli("simulaterawtransaction", "[\"myhex\"]")
514              + HelpExampleRpc("simulaterawtransaction", "[\"myhex\"]")
515          },
516      [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
517  {
518      const std::shared_ptr<const CWallet> rpc_wallet = GetWalletForJSONRPCRequest(request);
519      if (!rpc_wallet) return UniValue::VNULL;
520      const CWallet& wallet = *rpc_wallet;
521  
522      LOCK(wallet.cs_wallet);
523  
524      const auto& txs = request.params[0].get_array();
525      CAmount changes{0};
526      std::map<COutPoint, CAmount> new_utxos; // UTXO:s that were made available in transaction array
527      std::set<COutPoint> spent;
528  
529      for (size_t i = 0; i < txs.size(); ++i) {
530          CMutableTransaction mtx;
531          if (!DecodeHexTx(mtx, txs[i].get_str(), /*try_no_witness=*/ true, /*try_witness=*/ true)) {
532              throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Transaction hex string decoding failure.");
533          }
534  
535          // Fetch previous transactions (inputs)
536          std::map<COutPoint, Coin> coins;
537          for (const CTxIn& txin : mtx.vin) {
538              coins[txin.prevout]; // Create empty map entry keyed by prevout.
539          }
540          wallet.chain().findCoins(coins);
541  
542          // Fetch debit; we are *spending* these; if the transaction is signed and
543          // broadcast, we will lose everything in these
544          for (const auto& txin : mtx.vin) {
545              const auto& outpoint = txin.prevout;
546              if (spent.contains(outpoint)) {
547                  throw JSONRPCError(RPC_INVALID_PARAMETER, "Transaction(s) are spending the same output more than once");
548              }
549              if (new_utxos.contains(outpoint)) {
550                  changes -= new_utxos.at(outpoint);
551                  new_utxos.erase(outpoint);
552              } else {
553                  if (coins.at(outpoint).IsSpent()) {
554                      throw JSONRPCError(RPC_INVALID_PARAMETER, "One or more transaction inputs are missing or have been spent already");
555                  }
556                  changes -= wallet.GetDebit(txin);
557              }
558              spent.insert(outpoint);
559          }
560  
561          // Iterate over outputs; we are *receiving* these, if the wallet considers
562          // them "mine"; if the transaction is signed and broadcast, we will receive
563          // everything in these
564          // Also populate new_utxos in case these are spent in later transactions
565  
566          const auto& hash = mtx.GetHash();
567          for (size_t i = 0; i < mtx.vout.size(); ++i) {
568              const auto& txout = mtx.vout[i];
569              bool is_mine = wallet.IsMine(txout);
570              changes += new_utxos[COutPoint(hash, i)] = is_mine ? txout.nValue : 0;
571          }
572      }
573  
574      UniValue result(UniValue::VOBJ);
575      result.pushKV("balance_change", ValueFromAmount(changes));
576  
577      return result;
578  }
579      };
580  }
581  
582  static RPCHelpMan migratewallet()
583  {
584      return RPCHelpMan{
585          "migratewallet",
586          "Migrate the wallet to a descriptor wallet.\n"
587          "A new wallet backup will need to be made.\n"
588          "\nThe migration process will create a backup of the wallet before migrating. This backup\n"
589          "file will be named <wallet name>-<timestamp>.legacy.bak and can be found in the directory\n"
590          "for this wallet. In the event of an incorrect migration, the backup can be restored using restorewallet."
591          "\nEncrypted wallets must have the passphrase provided as an argument to this call.\n"
592          "\nThis RPC may take a long time to complete. Increasing the RPC client timeout is recommended.",
593          {
594              {"wallet_name", RPCArg::Type::STR, RPCArg::DefaultHint{"the wallet name from the RPC endpoint"}, "The name of the wallet to migrate. If provided both here and in the RPC endpoint, the two must be identical."},
595              {"passphrase", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "The wallet passphrase"},
596          },
597          RPCResult{
598              RPCResult::Type::OBJ, "", "",
599              {
600                  {RPCResult::Type::STR, "wallet_name", "The name of the primary migrated wallet"},
601                  {RPCResult::Type::STR, "watchonly_name", /*optional=*/true, "The name of the migrated wallet containing the watchonly scripts"},
602                  {RPCResult::Type::STR, "solvables_name", /*optional=*/true, "The name of the migrated wallet containing solvable but not watched scripts"},
603                  {RPCResult::Type::STR, "backup_path", "The location of the backup of the original wallet"},
604              }
605          },
606          RPCExamples{
607              HelpExampleCli("migratewallet", "")
608              + HelpExampleRpc("migratewallet", "")
609          },
610          [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
611          {
612              const std::string wallet_name{EnsureUniqueWalletName(request, self.MaybeArg<std::string_view>("wallet_name"))};
613  
614              SecureString wallet_pass;
615              wallet_pass.reserve(100);
616              if (!request.params[1].isNull()) {
617                  wallet_pass = std::string_view{request.params[1].get_str()};
618              }
619  
620              WalletContext& context = EnsureWalletContext(request.context);
621              util::Result<MigrationResult> res = MigrateLegacyToDescriptor(wallet_name, wallet_pass, context);
622              if (!res) {
623                  throw JSONRPCError(RPC_WALLET_ERROR, util::ErrorString(res).original);
624              }
625  
626              UniValue r{UniValue::VOBJ};
627              r.pushKV("wallet_name", res->wallet_name);
628              if (res->watchonly_wallet) {
629                  r.pushKV("watchonly_name", res->watchonly_wallet->GetName());
630              }
631              if (res->solvables_wallet) {
632                  r.pushKV("solvables_name", res->solvables_wallet->GetName());
633              }
634              r.pushKV("backup_path", res->backup_path.utf8string());
635  
636              return r;
637          },
638      };
639  }
640  
641  RPCHelpMan gethdkeys()
642  {
643      return RPCHelpMan{
644          "gethdkeys",
645          "List all BIP 32 HD keys in the wallet and which descriptors use them.\n",
646          {
647              {"options", RPCArg::Type::OBJ_NAMED_PARAMS, RPCArg::Optional::OMITTED, "", {
648                  {"active_only", RPCArg::Type::BOOL, RPCArg::Default{false}, "Show the keys for only active descriptors"},
649                  {"private", RPCArg::Type::BOOL, RPCArg::Default{false}, "Show private keys"}
650              }},
651          },
652          RPCResult{RPCResult::Type::ARR, "", "", {
653              {
654                  {RPCResult::Type::OBJ, "", "", {
655                      {RPCResult::Type::STR, "xpub", "The extended public key"},
656                      {RPCResult::Type::BOOL, "has_private", "Whether the wallet has the private key for this xpub"},
657                      {RPCResult::Type::STR, "xprv", /*optional=*/true, "The extended private key if \"private\" is true"},
658                      {RPCResult::Type::ARR, "descriptors", "Array of descriptor objects that use this HD key",
659                      {
660                          {RPCResult::Type::OBJ, "", "", {
661                              {RPCResult::Type::STR, "desc", "Descriptor string representation"},
662                              {RPCResult::Type::BOOL, "active", "Whether this descriptor is currently used to generate new addresses"},
663                          }},
664                      }},
665                  }},
666              }
667          }},
668          RPCExamples{
669              HelpExampleCli("gethdkeys", "") + HelpExampleRpc("gethdkeys", "")
670              + HelpExampleCliNamed("gethdkeys", {{"active_only", "true"}, {"private", "true"}}) + HelpExampleRpcNamed("gethdkeys", {{"active_only", "true"}, {"private", "true"}})
671          },
672          [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
673          {
674              const std::shared_ptr<const CWallet> wallet = GetWalletForJSONRPCRequest(request);
675              if (!wallet) return UniValue::VNULL;
676  
677              LOCK(wallet->cs_wallet);
678  
679              UniValue options{request.params[0].isNull() ? UniValue::VOBJ : request.params[0]};
680              const bool active_only{options.exists("active_only") ? options["active_only"].get_bool() : false};
681              const bool priv{options.exists("private") ? options["private"].get_bool() : false};
682              if (priv) {
683                  EnsureWalletIsUnlocked(*wallet);
684              }
685  
686  
687              std::set<ScriptPubKeyMan*> spkms;
688              if (active_only) {
689                  spkms = wallet->GetActiveScriptPubKeyMans();
690              } else {
691                  spkms = wallet->GetAllScriptPubKeyMans();
692              }
693  
694              std::map<CExtPubKey, std::set<std::tuple<std::string, bool, bool>>> wallet_xpubs;
695              std::map<CExtPubKey, CExtKey> wallet_xprvs;
696              for (auto* spkm : spkms) {
697                  auto* desc_spkm{dynamic_cast<DescriptorScriptPubKeyMan*>(spkm)};
698                  CHECK_NONFATAL(desc_spkm);
699                  LOCK(desc_spkm->cs_desc_man);
700                  WalletDescriptor w_desc = desc_spkm->GetWalletDescriptor();
701  
702                  // Retrieve the pubkeys from the descriptor
703                  std::set<CPubKey> desc_pubkeys;
704                  std::set<CExtPubKey> desc_xpubs;
705                  w_desc.descriptor->GetPubKeys(desc_pubkeys, desc_xpubs);
706                  for (const CExtPubKey& xpub : desc_xpubs) {
707                      std::string desc_str;
708                      bool ok = desc_spkm->GetDescriptorString(desc_str, false);
709                      CHECK_NONFATAL(ok);
710                      wallet_xpubs[xpub].emplace(desc_str, wallet->IsActiveScriptPubKeyMan(*spkm), desc_spkm->HasPrivKey(xpub.pubkey.GetID()));
711                      if (std::optional<CKey> key = priv ? desc_spkm->GetKey(xpub.pubkey.GetID()) : std::nullopt) {
712                          wallet_xprvs[xpub] = CExtKey(xpub, *key);
713                      }
714                  }
715              }
716  
717              UniValue response(UniValue::VARR);
718              for (const auto& [xpub, descs] : wallet_xpubs) {
719                  bool has_xprv = false;
720                  UniValue descriptors(UniValue::VARR);
721                  for (const auto& [desc, active, has_priv] : descs) {
722                      UniValue d(UniValue::VOBJ);
723                      d.pushKV("desc", desc);
724                      d.pushKV("active", active);
725                      has_xprv |= has_priv;
726  
727                      descriptors.push_back(std::move(d));
728                  }
729                  UniValue xpub_info(UniValue::VOBJ);
730                  xpub_info.pushKV("xpub", EncodeExtPubKey(xpub));
731                  xpub_info.pushKV("has_private", has_xprv);
732                  if (priv) {
733                      xpub_info.pushKV("xprv", EncodeExtKey(wallet_xprvs.at(xpub)));
734                  }
735                  xpub_info.pushKV("descriptors", std::move(descriptors));
736  
737                  response.push_back(std::move(xpub_info));
738              }
739  
740              return response;
741          },
742      };
743  }
744  
745  static RPCHelpMan createwalletdescriptor()
746  {
747      return RPCHelpMan{"createwalletdescriptor",
748          "Creates the wallet's descriptor for the given address type. "
749          "The address type must be one that the wallet does not already have a descriptor for."
750          + HELP_REQUIRING_PASSPHRASE,
751          {
752              {"type", RPCArg::Type::STR, RPCArg::Optional::NO, "The address type the descriptor will produce. Options are " + FormatAllOutputTypes() + "."},
753              {"options", RPCArg::Type::OBJ_NAMED_PARAMS, RPCArg::Optional::OMITTED, "", {
754                  {"internal", RPCArg::Type::BOOL, RPCArg::DefaultHint{"Both external and internal will be generated unless this parameter is specified"}, "Whether to only make one descriptor that is internal (if parameter is true) or external (if parameter is false)"},
755                  {"hdkey", RPCArg::Type::STR, RPCArg::DefaultHint{"The HD key used by all other active descriptors"}, "The HD key that the wallet knows the private key of, listed using 'gethdkeys', to use for this descriptor's key"},
756              }},
757          },
758          RPCResult{
759              RPCResult::Type::OBJ, "", "",
760              {
761                  {RPCResult::Type::ARR, "descs", "The public descriptors that were added to the wallet",
762                      {{RPCResult::Type::STR, "", ""}}
763                  }
764              },
765          },
766          RPCExamples{
767              HelpExampleCli("createwalletdescriptor", "bech32m")
768              + HelpExampleRpc("createwalletdescriptor", "bech32m")
769          },
770          [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
771          {
772              std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
773              if (!pwallet) return UniValue::VNULL;
774  
775              std::optional<OutputType> output_type = ParseOutputType(request.params[0].get_str());
776              if (!output_type) {
777                  throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Unknown address type '%s'", request.params[0].get_str()));
778              }
779  
780              UniValue options{request.params[1].isNull() ? UniValue::VOBJ : request.params[1]};
781              UniValue internal_only{options["internal"]};
782              UniValue hdkey{options["hdkey"]};
783  
784              std::vector<bool> internals;
785              if (internal_only.isNull()) {
786                  internals.push_back(false);
787                  internals.push_back(true);
788              } else {
789                  internals.push_back(internal_only.get_bool());
790              }
791  
792              LOCK(pwallet->cs_wallet);
793              EnsureWalletIsUnlocked(*pwallet);
794  
795              CExtPubKey xpub;
796              if (hdkey.isNull()) {
797                  std::set<CExtPubKey> active_xpubs = pwallet->GetActiveHDPubKeys();
798                  if (active_xpubs.size() != 1) {
799                      throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Unable to determine which HD key to use from active descriptors. Please specify with 'hdkey'");
800                  }
801                  xpub = *active_xpubs.begin();
802              } else {
803                  xpub = DecodeExtPubKey(hdkey.get_str());
804                  if (!xpub.pubkey.IsValid()) {
805                      throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Unable to parse HD key. Please provide a valid xpub");
806                  }
807              }
808  
809              std::optional<CKey> key = pwallet->GetKey(xpub.pubkey.GetID());
810              if (!key) {
811                  throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Private key for %s is not known", EncodeExtPubKey(xpub)));
812              }
813              CExtKey active_hdkey(xpub, *key);
814  
815              std::vector<std::reference_wrapper<DescriptorScriptPubKeyMan>> spkms;
816              WalletBatch batch{pwallet->GetDatabase()};
817              for (bool internal : internals) {
818                  WalletDescriptor w_desc = GenerateWalletDescriptor(xpub, *output_type, internal);
819                  uint256 w_id = DescriptorID(*w_desc.descriptor);
820                  if (!pwallet->GetScriptPubKeyMan(w_id)) {
821                      spkms.emplace_back(pwallet->SetupDescriptorScriptPubKeyMan(batch, active_hdkey, *output_type, internal));
822                  }
823              }
824              if (spkms.empty()) {
825                  throw JSONRPCError(RPC_WALLET_ERROR, "Descriptor already exists");
826              }
827  
828              // Fetch each descspkm from the wallet in order to get the descriptor strings
829              UniValue descs{UniValue::VARR};
830              for (const auto& spkm : spkms) {
831                  std::string desc_str;
832                  bool ok = spkm.get().GetDescriptorString(desc_str, false);
833                  CHECK_NONFATAL(ok);
834                  descs.push_back(desc_str);
835              }
836              UniValue out{UniValue::VOBJ};
837              out.pushKV("descs", std::move(descs));
838              return out;
839          }
840      };
841  }
842  
843  // addresses
844  RPCHelpMan getaddressinfo();
845  RPCHelpMan getnewaddress();
846  RPCHelpMan getrawchangeaddress();
847  RPCHelpMan setlabel();
848  RPCHelpMan listaddressgroupings();
849  RPCHelpMan keypoolrefill();
850  RPCHelpMan getaddressesbylabel();
851  RPCHelpMan listlabels();
852  #ifdef ENABLE_EXTERNAL_SIGNER
853  RPCHelpMan walletdisplayaddress();
854  #endif // ENABLE_EXTERNAL_SIGNER
855  
856  // backup
857  RPCHelpMan importprunedfunds();
858  RPCHelpMan removeprunedfunds();
859  RPCHelpMan importdescriptors();
860  RPCHelpMan listdescriptors();
861  RPCHelpMan backupwallet();
862  RPCHelpMan restorewallet();
863  
864  // coins
865  RPCHelpMan getreceivedbyaddress();
866  RPCHelpMan getreceivedbylabel();
867  RPCHelpMan getbalance();
868  RPCHelpMan lockunspent();
869  RPCHelpMan listlockunspent();
870  RPCHelpMan getbalances();
871  RPCHelpMan listunspent();
872  
873  // encryption
874  RPCHelpMan walletpassphrase();
875  RPCHelpMan walletpassphrasechange();
876  RPCHelpMan walletlock();
877  RPCHelpMan encryptwallet();
878  
879  // spend
880  RPCHelpMan sendtoaddress();
881  RPCHelpMan sendmany();
882  RPCHelpMan fundrawtransaction();
883  RPCHelpMan bumpfee();
884  RPCHelpMan psbtbumpfee();
885  RPCHelpMan send();
886  RPCHelpMan sendall();
887  RPCHelpMan walletprocesspsbt();
888  RPCHelpMan walletcreatefundedpsbt();
889  RPCHelpMan signrawtransactionwithwallet();
890  
891  // signmessage
892  RPCHelpMan signmessage();
893  
894  // transactions
895  RPCHelpMan listreceivedbyaddress();
896  RPCHelpMan listreceivedbylabel();
897  RPCHelpMan listtransactions();
898  RPCHelpMan listsinceblock();
899  RPCHelpMan gettransaction();
900  RPCHelpMan abandontransaction();
901  RPCHelpMan rescanblockchain();
902  RPCHelpMan abortrescan();
903  
904  std::span<const CRPCCommand> GetWalletRPCCommands()
905  {
906      static const CRPCCommand commands[]{
907          {"rawtransactions", &fundrawtransaction},
908          {"wallet", &abandontransaction},
909          {"wallet", &abortrescan},
910          {"wallet", &backupwallet},
911          {"wallet", &bumpfee},
912          {"wallet", &psbtbumpfee},
913          {"wallet", &createwallet},
914          {"wallet", &createwalletdescriptor},
915          {"wallet", &restorewallet},
916          {"wallet", &encryptwallet},
917          {"wallet", &getaddressesbylabel},
918          {"wallet", &getaddressinfo},
919          {"wallet", &getbalance},
920          {"wallet", &gethdkeys},
921          {"wallet", &getnewaddress},
922          {"wallet", &getrawchangeaddress},
923          {"wallet", &getreceivedbyaddress},
924          {"wallet", &getreceivedbylabel},
925          {"wallet", &gettransaction},
926          {"wallet", &getbalances},
927          {"wallet", &getwalletinfo},
928          {"wallet", &importdescriptors},
929          {"wallet", &importprunedfunds},
930          {"wallet", &keypoolrefill},
931          {"wallet", &listaddressgroupings},
932          {"wallet", &listdescriptors},
933          {"wallet", &listlabels},
934          {"wallet", &listlockunspent},
935          {"wallet", &listreceivedbyaddress},
936          {"wallet", &listreceivedbylabel},
937          {"wallet", &listsinceblock},
938          {"wallet", &listtransactions},
939          {"wallet", &listunspent},
940          {"wallet", &listwalletdir},
941          {"wallet", &listwallets},
942          {"wallet", &loadwallet},
943          {"wallet", &lockunspent},
944          {"wallet", &migratewallet},
945          {"wallet", &removeprunedfunds},
946          {"wallet", &rescanblockchain},
947          {"wallet", &send},
948          {"wallet", &sendmany},
949          {"wallet", &sendtoaddress},
950          {"wallet", &setlabel},
951          {"wallet", &setwalletflag},
952          {"wallet", &signmessage},
953          {"wallet", &signrawtransactionwithwallet},
954          {"wallet", &simulaterawtransaction},
955          {"wallet", &sendall},
956          {"wallet", &unloadwallet},
957          {"wallet", &walletcreatefundedpsbt},
958  #ifdef ENABLE_EXTERNAL_SIGNER
959          {"wallet", &walletdisplayaddress},
960  #endif // ENABLE_EXTERNAL_SIGNER
961          {"wallet", &walletlock},
962          {"wallet", &walletpassphrase},
963          {"wallet", &walletpassphrasechange},
964          {"wallet", &walletprocesspsbt},
965      };
966      return commands;
967  }
968  } // namespace wallet