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