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