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