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 <optional>
15 : : #include <string_view>
16 : : #include <univalue.h>
17 : :
18 : : namespace wallet {
19 : : static const std::string WALLET_ENDPOINT_BASE = "/wallet/";
20 : : const std::string HELP_REQUIRING_PASSPHRASE{"\nRequires wallet passphrase to be set with walletpassphrase call if wallet is encrypted.\n"};
21 : :
22 : 1699 : bool GetAvoidReuseFlag(const CWallet& wallet, const UniValue& param) {
23 : 1699 : bool can_avoid_reuse = wallet.IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE);
24 [ + + ]: 1699 : bool avoid_reuse = param.isNull() ? can_avoid_reuse : param.get_bool();
25 : :
26 [ - + ]: 1699 : if (avoid_reuse && !can_avoid_reuse) {
27 [ # # # # ]: 0 : throw JSONRPCError(RPC_WALLET_ERROR, "wallet does not have the \"avoid reuse\" feature enabled");
28 : : }
29 : :
30 : 1699 : return avoid_reuse;
31 : : }
32 : :
33 : 339 : std::string EnsureUniqueWalletName(const JSONRPCRequest& request, std::optional<std::string_view> wallet_name)
34 : : {
35 [ + - ]: 339 : std::string endpoint_wallet;
36 [ + - + + ]: 339 : if (GetWalletNameFromJSONRPCRequest(request, endpoint_wallet)) {
37 : : // wallet endpoint was used
38 [ + + + + ]: 202 : if (wallet_name && *wallet_name != endpoint_wallet) {
39 : 6 : throw JSONRPCError(RPC_INVALID_PARAMETER,
40 [ + - + - ]: 12 : "The RPC endpoint wallet and the wallet name parameter specify different wallets");
41 : : }
42 : 187 : return endpoint_wallet;
43 : : }
44 : :
45 : : // Not a wallet endpoint; parameter must be provided
46 [ + + ]: 146 : if (!wallet_name) {
47 : 5 : throw JSONRPCError(RPC_INVALID_PARAMETER,
48 [ + - + - ]: 10 : "Either the RPC endpoint wallet or the wallet name parameter must be provided");
49 : : }
50 : :
51 [ + - ]: 141 : return std::string{*wallet_name};
52 : 328 : }
53 : :
54 : 19395 : bool GetWalletNameFromJSONRPCRequest(const JSONRPCRequest& request, std::string& wallet_name)
55 : : {
56 [ - + - + : 19395 : if (request.URI.starts_with(WALLET_ENDPOINT_BASE)) {
+ + ]
57 : : // wallet endpoint was used
58 : 14513 : wallet_name = UrlDecode(std::string_view{request.URI}.substr(WALLET_ENDPOINT_BASE.size()));
59 : 14513 : return true;
60 : : }
61 : : return false;
62 : : }
63 : :
64 : 19056 : std::shared_ptr<CWallet> GetWalletForJSONRPCRequest(const JSONRPCRequest& request)
65 : : {
66 : 19056 : CHECK_NONFATAL(request.mode == JSONRPCRequest::EXECUTE);
67 : 19056 : WalletContext& context = EnsureWalletContext(request.context);
68 : :
69 [ + - ]: 19056 : std::string wallet_name;
70 [ + - + + ]: 19056 : if (GetWalletNameFromJSONRPCRequest(request, wallet_name)) {
71 [ + - ]: 14320 : std::shared_ptr<CWallet> pwallet = GetWallet(context, wallet_name);
72 [ + + + - : 14331 : if (!pwallet) throw JSONRPCError(RPC_WALLET_NOT_FOUND, "Requested wallet does not exist or is not loaded");
+ - ]
73 : : return pwallet;
74 : 11 : }
75 : :
76 : 4736 : size_t count{0};
77 [ + - ]: 4736 : auto wallet = GetDefaultWallet(context, count);
78 [ + + - + ]: 4736 : if (wallet) return wallet;
79 : :
80 [ + + ]: 23 : if (count == 0) {
81 : 11 : throw JSONRPCError(
82 [ + - + - ]: 22 : 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)");
83 : : }
84 : 12 : throw JSONRPCError(RPC_WALLET_NOT_SPECIFIED,
85 [ + - + - ]: 24 : "Multiple wallets are loaded. Please select which wallet to use by requesting the RPC through the /wallet/<walletname> URI path.");
86 : 23758 : }
87 : :
88 : 4069 : void EnsureWalletIsUnlocked(const CWallet& wallet)
89 : : {
90 [ + + ]: 4069 : if (wallet.IsLocked()) {
91 [ + - + - ]: 30 : throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Please enter the wallet passphrase with walletpassphrase first.");
92 : : }
93 : 4054 : }
94 : :
95 : 20254 : WalletContext& EnsureWalletContext(const std::any& context)
96 : : {
97 : 20254 : auto wallet_context = util::AnyPtr<WalletContext>(context);
98 [ - + ]: 20254 : if (!wallet_context) {
99 [ # # # # ]: 0 : throw JSONRPCError(RPC_INTERNAL_ERROR, "Wallet context not found");
100 : : }
101 : 20254 : return *wallet_context;
102 : : }
103 : :
104 : 11772 : std::string LabelFromValue(const UniValue& value)
105 : : {
106 [ + + + - ]: 11772 : static const std::string empty_string;
107 [ + + - + ]: 11772 : if (value.isNull()) return empty_string;
108 : :
109 : 340 : const std::string& label{value.get_str()};
110 [ + + ]: 340 : if (label == "*")
111 [ + - + - ]: 12 : throw JSONRPCError(RPC_WALLET_INVALID_LABEL_NAME, "Invalid label name");
112 [ - + ]: 334 : return label;
113 : : }
114 : :
115 : 36806 : void PushParentDescriptors(const CWallet& wallet, const CScript& script_pubkey, UniValue& entry)
116 : : {
117 : 36806 : UniValue parent_descs(UniValue::VARR);
118 [ + - + + ]: 73613 : for (const auto& desc: wallet.GetWalletDescriptors(script_pubkey)) {
119 : 36807 : std::string desc_str;
120 : 36807 : FlatSigningProvider dummy_provider;
121 [ + - + - : 36807 : if (!CHECK_NONFATAL(desc.descriptor->ToNormalizedString(dummy_provider, desc_str, &desc.cache))) continue;
- + ]
122 [ + - + - ]: 36807 : parent_descs.push_back(desc_str);
123 : 73613 : }
124 [ + - + - ]: 73612 : entry.pushKV("parent_descs", std::move(parent_descs));
125 : 36806 : }
126 : :
127 : 178 : void HandleWalletError(const std::shared_ptr<CWallet> wallet, DatabaseStatus& status, bilingual_str& error)
128 : : {
129 [ + + ]: 178 : if (!wallet) {
130 : : // Map bad format to not found, since bad format is returned when the
131 : : // wallet directory exists, but doesn't contain a data file.
132 : 27 : RPCErrorCode code = RPC_WALLET_ERROR;
133 [ + - + + : 27 : switch (status) {
+ ]
134 : 15 : case DatabaseStatus::FAILED_NOT_FOUND:
135 : 15 : case DatabaseStatus::FAILED_BAD_FORMAT:
136 : 15 : case DatabaseStatus::FAILED_LEGACY_DISABLED:
137 : 15 : code = RPC_WALLET_NOT_FOUND;
138 : 15 : break;
139 : 0 : case DatabaseStatus::FAILED_ALREADY_LOADED:
140 : 0 : code = RPC_WALLET_ALREADY_LOADED;
141 : 0 : break;
142 : 1 : case DatabaseStatus::FAILED_ALREADY_EXISTS:
143 : 1 : code = RPC_WALLET_ALREADY_EXISTS;
144 : 1 : break;
145 : 1 : case DatabaseStatus::FAILED_INVALID_BACKUP_FILE:
146 : 1 : code = RPC_INVALID_PARAMETER;
147 : 1 : break;
148 : : default: // RPC_WALLET_ERROR is returned for all other cases.
149 : : break;
150 : : }
151 [ + - ]: 27 : throw JSONRPCError(code, error.original);
152 : : }
153 : 151 : }
154 : :
155 : 1533 : void AppendLastProcessedBlock(UniValue& entry, const CWallet& wallet)
156 : : {
157 : 1533 : AssertLockHeld(wallet.cs_wallet);
158 : 1533 : UniValue lastprocessedblock{UniValue::VOBJ};
159 [ + - + - : 3066 : lastprocessedblock.pushKV("hash", wallet.GetLastBlockHash().GetHex());
+ - + - ]
160 [ + - + - : 3066 : lastprocessedblock.pushKV("height", wallet.GetLastBlockHeight());
+ - ]
161 [ + - + - ]: 3066 : entry.pushKV("lastprocessedblock", std::move(lastprocessedblock));
162 : 1533 : }
163 : :
164 : : } // namespace wallet
|