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