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