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