util.cpp raw

   1  // Copyright (c) 2011-present The Limenka developers
   2  // Distributed under the MIT software license, see the accompanying
   3  // file COPYING or http://www.opensource.org/licenses/mit-license.php.
   4  
   5  #include <wallet/rpc/util.h>
   6  
   7  #include <common/url.h>
   8  #include <httprpc.h>
   9  #include <rpc/util.h>
  10  #include <util/any.h>
  11  #include <util/translation.h>
  12  #include <wallet/context.h>
  13  #include <wallet/wallet.h>
  14  
  15  #include <string_view>
  16  #include <univalue.h>
  17  
  18  namespace wallet {
  19  static const std::string WALLET_ENDPOINT_BASE = "/wallet/";
  20  const std::string HELP_REQUIRING_PASSPHRASE{"\nRequires wallet passphrase to be set with walletpassphrase call if wallet is encrypted.\n"};
  21  
  22  bool GetAvoidReuseFlag(const CWallet& wallet, const UniValue& param) {
  23      bool can_avoid_reuse = wallet.IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE);
  24      bool avoid_reuse = param.isNull() ? can_avoid_reuse : param.get_bool();
  25  
  26      if (avoid_reuse && !can_avoid_reuse) {
  27          throw JSONRPCError(RPC_WALLET_ERROR, "wallet does not have the \"avoid reuse\" feature enabled");
  28      }
  29  
  30      return avoid_reuse;
  31  }
  32  
  33  /** Used by RPC commands that have an include_watchonly parameter.
  34   *  We default to true for watchonly wallets if include_watchonly isn't
  35   *  explicitly set.
  36   */
  37  bool ParseIncludeWatchonly(const UniValue& include_watchonly, const CWallet& wallet)
  38  {
  39      if (include_watchonly.isNull()) {
  40          // if include_watchonly isn't explicitly set, then check if we have a watchonly wallet
  41          return wallet.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS);
  42      }
  43  
  44      // otherwise return whatever include_watchonly was set to
  45      return include_watchonly.get_bool();
  46  }
  47  
  48  std::string EnsureUniqueWalletName(const JSONRPCRequest& request, const std::string* wallet_name)
  49  {
  50      std::string endpoint_wallet;
  51      if (GetWalletNameFromJSONRPCRequest(request, endpoint_wallet)) {
  52          // wallet endpoint was used
  53          if (wallet_name && *wallet_name != endpoint_wallet) {
  54              throw JSONRPCError(RPC_INVALID_PARAMETER,
  55                  "The RPC endpoint wallet and the wallet name parameter specify different wallets");
  56          }
  57          return endpoint_wallet;
  58      }
  59  
  60      // Not a wallet endpoint; parameter must be provided
  61      if (!wallet_name) {
  62          throw JSONRPCError(RPC_INVALID_PARAMETER,
  63              "Either the RPC endpoint wallet or the wallet name parameter must be provided");
  64      }
  65  
  66      return *wallet_name;
  67  }
  68  
  69  bool GetWalletNameFromJSONRPCRequest(const JSONRPCRequest& request, std::string& wallet_name)
  70  {
  71      if (request.URI.starts_with(WALLET_ENDPOINT_BASE)) {
  72          // wallet endpoint was used
  73          wallet_name = UrlDecode(std::string_view{request.URI}.substr(WALLET_ENDPOINT_BASE.size()));
  74          return true;
  75      }
  76      return false;
  77  }
  78  
  79  std::shared_ptr<CWallet> GetWalletForJSONRPCRequest(const JSONRPCRequest& request)
  80  {
  81      CHECK_NONFATAL(request.mode == JSONRPCRequest::EXECUTE);
  82      WalletContext& context = EnsureWalletContext(request.context);
  83  
  84      bool have_wallet_restriction;
  85      std::string authorized_wallet_name;
  86      have_wallet_restriction = GetWalletRestrictionFromJSONRPCRequest(request, authorized_wallet_name);
  87  
  88      bool have_requested_wallet;
  89      std::string requested_wallet_name;
  90      have_requested_wallet = GetWalletNameFromJSONRPCRequest(request, requested_wallet_name);
  91  
  92      std::shared_ptr<CWallet> pwallet;
  93      size_t count{0};
  94  
  95      if (!have_wallet_restriction) {
  96          // Any wallet is permitted; select by endpoint, or use the sole wallet
  97          if (have_requested_wallet) {
  98              pwallet = GetWallet(context, requested_wallet_name);
  99          } else {
 100              auto wallet = GetDefaultWallet(context, count);
 101              if (wallet) pwallet = wallet;
 102          }
 103      } else if (authorized_wallet_name == "-") {
 104          // Block wallet access always
 105      } else if ((!have_requested_wallet) || requested_wallet_name == authorized_wallet_name) {
 106          // Select specifically the authorized wallet
 107          pwallet = GetWallet(context, authorized_wallet_name);
 108      }
 109  
 110      if (pwallet) {
 111          return pwallet;
 112      }
 113  
 114      if (have_requested_wallet) {
 115          throw JSONRPCError(RPC_WALLET_NOT_FOUND, "Requested wallet does not exist or is not loaded");
 116      }
 117      if (have_wallet_restriction
 118      ? (authorized_wallet_name == "-" || !GetWallet(context, authorized_wallet_name))
 119      : (count == 0)
 120       ) {
 121          throw JSONRPCError(
 122              RPC_WALLET_NOT_FOUND, "No wallet is loaded. Load a wallet using loadwallet or create a new one with createwallet. (Note: A default wallet is no longer automatically created)");
 123      }
 124      throw JSONRPCError(RPC_WALLET_NOT_SPECIFIED,
 125          "Multiple wallets are loaded. Please select which wallet to use by requesting the RPC through the /wallet/<walletname> URI path.");
 126  }
 127  
 128  void EnsureWalletIsUnlocked(const CWallet& wallet)
 129  {
 130      if (wallet.IsLocked()) {
 131          throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Please enter the wallet passphrase with walletpassphrase first.");
 132      }
 133  }
 134  
 135  WalletContext& EnsureWalletContext(const std::any& context)
 136  {
 137      auto wallet_context = util::AnyPtr<WalletContext>(context);
 138      if (!wallet_context) {
 139          throw JSONRPCError(RPC_INTERNAL_ERROR, "Wallet context not found");
 140      }
 141      return *wallet_context;
 142  }
 143  
 144  // also_create should only be set to true only when the RPC is expected to add things to a blank wallet and make it no longer blank
 145  LegacyScriptPubKeyMan& EnsureLegacyScriptPubKeyMan(CWallet& wallet, bool also_create)
 146  {
 147      LegacyScriptPubKeyMan* spk_man = wallet.GetLegacyScriptPubKeyMan();
 148      if (!spk_man && also_create) {
 149          spk_man = wallet.GetOrCreateLegacyScriptPubKeyMan();
 150      }
 151      if (!spk_man) {
 152          throw JSONRPCError(RPC_WALLET_ERROR, "Only legacy wallets are supported by this command");
 153      }
 154      return *spk_man;
 155  }
 156  
 157  const LegacyScriptPubKeyMan& EnsureConstLegacyScriptPubKeyMan(const CWallet& wallet)
 158  {
 159      const LegacyScriptPubKeyMan* spk_man = wallet.GetLegacyScriptPubKeyMan();
 160      if (!spk_man) {
 161          throw JSONRPCError(RPC_WALLET_ERROR, "Only legacy wallets are supported by this command");
 162      }
 163      return *spk_man;
 164  }
 165  
 166  std::string LabelFromValue(const UniValue& value)
 167  {
 168      static const std::string empty_string;
 169      if (value.isNull()) return empty_string;
 170  
 171      const std::string& label{value.get_str()};
 172      if (label == "*")
 173          throw JSONRPCError(RPC_WALLET_INVALID_LABEL_NAME, "Invalid label name");
 174      return label;
 175  }
 176  
 177  void PushParentDescriptors(const CWallet& wallet, const CScript& script_pubkey, UniValue& entry)
 178  {
 179      UniValue parent_descs(UniValue::VARR);
 180      for (const auto& desc: wallet.GetWalletDescriptors(script_pubkey)) {
 181          parent_descs.push_back(desc.descriptor->ToString());
 182      }
 183      entry.pushKV("parent_descs", std::move(parent_descs));
 184  }
 185  
 186  void HandleWalletError(const std::shared_ptr<CWallet> wallet, DatabaseStatus& status, bilingual_str& error)
 187  {
 188      if (!wallet) {
 189          // Map bad format to not found, since bad format is returned when the
 190          // wallet directory exists, but doesn't contain a data file.
 191          RPCErrorCode code = RPC_WALLET_ERROR;
 192          switch (status) {
 193              case DatabaseStatus::FAILED_NOT_FOUND:
 194              case DatabaseStatus::FAILED_BAD_FORMAT:
 195                  code = RPC_WALLET_NOT_FOUND;
 196                  break;
 197              case DatabaseStatus::FAILED_ALREADY_LOADED:
 198                  code = RPC_WALLET_ALREADY_LOADED;
 199                  break;
 200              case DatabaseStatus::FAILED_ALREADY_EXISTS:
 201                  code = RPC_WALLET_ALREADY_EXISTS;
 202                  break;
 203              case DatabaseStatus::FAILED_INVALID_BACKUP_FILE:
 204                  code = RPC_INVALID_PARAMETER;
 205                  break;
 206              default: // RPC_WALLET_ERROR is returned for all other cases.
 207                  break;
 208          }
 209          throw JSONRPCError(code, error.original);
 210      }
 211  }
 212  
 213  void AppendLastProcessedBlock(UniValue& entry, const CWallet& wallet)
 214  {
 215      AssertLockHeld(wallet.cs_wallet);
 216      UniValue lastprocessedblock{UniValue::VOBJ};
 217      lastprocessedblock.pushKV("hash", wallet.GetLastBlockHash().GetHex());
 218      lastprocessedblock.pushKV("height", wallet.GetLastBlockHeight());
 219      entry.pushKV("lastprocessedblock", std::move(lastprocessedblock));
 220  }
 221  
 222  } // namespace wallet
 223