/ src / wallet / rpc / util.cpp
util.cpp
  1  // Copyright (c) 2011-2022 The Bitcoin Core developers
  2  // Distributed under the MIT software license, see the accompanying
  3  // file COPYING or http://www.opensource.org/licenses/mit-license.php.
  4  
  5  #include <wallet/rpc/util.h>
  6  
  7  #include <common/url.h>
  8  #include <rpc/util.h>
  9  #include <util/any.h>
 10  #include <util/translation.h>
 11  #include <wallet/context.h>
 12  #include <wallet/wallet.h>
 13  
 14  #include <univalue.h>
 15  
 16  #include <boost/date_time/posix_time/posix_time.hpp>
 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  int64_t ParseISO8601DateTime(const std::string& str)
 23  {
 24      static const boost::posix_time::ptime epoch = boost::posix_time::from_time_t(0);
 25      static const std::locale loc(std::locale::classic(),
 26          new boost::posix_time::time_input_facet("%Y-%m-%dT%H:%M:%SZ"));
 27      std::istringstream iss(str);
 28      iss.imbue(loc);
 29      boost::posix_time::ptime ptime(boost::date_time::not_a_date_time);
 30      iss >> ptime;
 31      if (ptime.is_not_a_date_time() || epoch > ptime)
 32          return 0;
 33      return (ptime - epoch).total_seconds();
 34  }
 35  
 36  bool GetAvoidReuseFlag(const CWallet& wallet, const UniValue& param) {
 37      bool can_avoid_reuse = wallet.IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE);
 38      bool avoid_reuse = param.isNull() ? can_avoid_reuse : param.get_bool();
 39  
 40      if (avoid_reuse && !can_avoid_reuse) {
 41          throw JSONRPCError(RPC_WALLET_ERROR, "wallet does not have the \"avoid reuse\" feature enabled");
 42      }
 43  
 44      return avoid_reuse;
 45  }
 46  
 47  /** Used by RPC commands that have an include_watchonly parameter.
 48   *  We default to true for watchonly wallets if include_watchonly isn't
 49   *  explicitly set.
 50   */
 51  bool ParseIncludeWatchonly(const UniValue& include_watchonly, const CWallet& wallet)
 52  {
 53      if (include_watchonly.isNull()) {
 54          // if include_watchonly isn't explicitly set, then check if we have a watchonly wallet
 55          return wallet.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS);
 56      }
 57  
 58      // otherwise return whatever include_watchonly was set to
 59      return include_watchonly.get_bool();
 60  }
 61  
 62  bool GetWalletNameFromJSONRPCRequest(const JSONRPCRequest& request, std::string& wallet_name)
 63  {
 64      if (URL_DECODE && request.URI.substr(0, WALLET_ENDPOINT_BASE.size()) == WALLET_ENDPOINT_BASE) {
 65          // wallet endpoint was used
 66          wallet_name = URL_DECODE(request.URI.substr(WALLET_ENDPOINT_BASE.size()));
 67          return true;
 68      }
 69      return false;
 70  }
 71  
 72  std::shared_ptr<CWallet> GetWalletForJSONRPCRequest(const JSONRPCRequest& request)
 73  {
 74      CHECK_NONFATAL(request.mode == JSONRPCRequest::EXECUTE);
 75      WalletContext& context = EnsureWalletContext(request.context);
 76  
 77      std::string wallet_name;
 78      if (GetWalletNameFromJSONRPCRequest(request, wallet_name)) {
 79          std::shared_ptr<CWallet> pwallet = GetWallet(context, wallet_name);
 80          if (!pwallet) throw JSONRPCError(RPC_WALLET_NOT_FOUND, "Requested wallet does not exist or is not loaded");
 81          return pwallet;
 82      }
 83  
 84      size_t count{0};
 85      auto wallet = GetDefaultWallet(context, count);
 86      if (wallet) return wallet;
 87  
 88      if (count == 0) {
 89          throw JSONRPCError(
 90              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)");
 91      }
 92      throw JSONRPCError(RPC_WALLET_NOT_SPECIFIED,
 93          "Wallet file not specified (must request wallet RPC through /wallet/<filename> uri-path).");
 94  }
 95  
 96  void EnsureWalletIsUnlocked(const CWallet& wallet)
 97  {
 98      if (wallet.IsLocked()) {
 99          throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Please enter the wallet passphrase with walletpassphrase first.");
100      }
101  }
102  
103  WalletContext& EnsureWalletContext(const std::any& context)
104  {
105      auto wallet_context = util::AnyPtr<WalletContext>(context);
106      if (!wallet_context) {
107          throw JSONRPCError(RPC_INTERNAL_ERROR, "Wallet context not found");
108      }
109      return *wallet_context;
110  }
111  
112  // 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
113  LegacyScriptPubKeyMan& EnsureLegacyScriptPubKeyMan(CWallet& wallet, bool also_create)
114  {
115      LegacyScriptPubKeyMan* spk_man = wallet.GetLegacyScriptPubKeyMan();
116      if (!spk_man && also_create) {
117          spk_man = wallet.GetOrCreateLegacyScriptPubKeyMan();
118      }
119      if (!spk_man) {
120          throw JSONRPCError(RPC_WALLET_ERROR, "Only legacy wallets are supported by this command");
121      }
122      return *spk_man;
123  }
124  
125  const LegacyScriptPubKeyMan& EnsureConstLegacyScriptPubKeyMan(const CWallet& wallet)
126  {
127      const LegacyScriptPubKeyMan* spk_man = wallet.GetLegacyScriptPubKeyMan();
128      if (!spk_man) {
129          throw JSONRPCError(RPC_WALLET_ERROR, "Only legacy wallets are supported by this command");
130      }
131      return *spk_man;
132  }
133  
134  std::string LabelFromValue(const UniValue& value)
135  {
136      static const std::string empty_string;
137      if (value.isNull()) return empty_string;
138  
139      const std::string& label{value.get_str()};
140      if (label == "*")
141          throw JSONRPCError(RPC_WALLET_INVALID_LABEL_NAME, "Invalid label name");
142      return label;
143  }
144  
145  void PushParentDescriptors(const CWallet& wallet, const CScript& script_pubkey, UniValue& entry)
146  {
147      UniValue parent_descs(UniValue::VARR);
148      for (const auto& desc: wallet.GetWalletDescriptors(script_pubkey)) {
149          parent_descs.push_back(desc.descriptor->ToString());
150      }
151      entry.pushKV("parent_descs", parent_descs);
152  }
153  
154  void HandleWalletError(const std::shared_ptr<CWallet> wallet, DatabaseStatus& status, bilingual_str& error)
155  {
156      if (!wallet) {
157          // Map bad format to not found, since bad format is returned when the
158          // wallet directory exists, but doesn't contain a data file.
159          RPCErrorCode code = RPC_WALLET_ERROR;
160          switch (status) {
161              case DatabaseStatus::FAILED_NOT_FOUND:
162              case DatabaseStatus::FAILED_BAD_FORMAT:
163                  code = RPC_WALLET_NOT_FOUND;
164                  break;
165              case DatabaseStatus::FAILED_ALREADY_LOADED:
166                  code = RPC_WALLET_ALREADY_LOADED;
167                  break;
168              case DatabaseStatus::FAILED_ALREADY_EXISTS:
169                  code = RPC_WALLET_ALREADY_EXISTS;
170                  break;
171              case DatabaseStatus::FAILED_INVALID_BACKUP_FILE:
172                  code = RPC_INVALID_PARAMETER;
173                  break;
174              default: // RPC_WALLET_ERROR is returned for all other cases.
175                  break;
176          }
177          throw JSONRPCError(code, error.original);
178      }
179  }
180  
181  void AppendLastProcessedBlock(UniValue& entry, const CWallet& wallet) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet)
182  {
183      AssertLockHeld(wallet.cs_wallet);
184      UniValue lastprocessedblock{UniValue::VOBJ};
185      lastprocessedblock.pushKV("hash", wallet.GetLastBlockHash().GetHex());
186      lastprocessedblock.pushKV("height", wallet.GetLastBlockHeight());
187      entry.pushKV("lastprocessedblock", lastprocessedblock);
188  }
189  
190  } // namespace wallet