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