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