Branch data 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 : :
18 : : namespace wallet {
19 : : static const std::string WALLET_ENDPOINT_BASE = "/wallet/";
20 [ + - ]: 2 : 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 : 2 : std::istringstream iss(str);
28 [ + - ][ + - ]: 6 : iss.imbue(loc);
[ + - ][ - + ]
[ # # ][ # # ]
29 [ + - ][ + - ]: 2 : boost::posix_time::ptime ptime(boost::date_time::not_a_date_time);
[ + - ][ # # ]
30 [ + - ][ + - ]: 2 : 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 : 0 : 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 : : }
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 [ # # ]: 2 : if (wallet.IsLocked()) {
99 [ # # ][ # # ]: 0 : 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 [ # # ]: 2 : 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 : : 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 [ + - ]: 2 : code = RPC_WALLET_NOT_FOUND;
164 [ + - ]: 2 : break;
165 [ + - ]: 2 : case DatabaseStatus::FAILED_ALREADY_LOADED:
166 [ + - ]: 2 : code = RPC_WALLET_ALREADY_LOADED;
167 [ + - ]: 2 : break;
168 [ + - ]: 2 : case DatabaseStatus::FAILED_ALREADY_EXISTS:
169 [ + - ]: 2 : code = RPC_WALLET_ALREADY_EXISTS;
170 [ + - ]: 2 : 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
|