/root/bitcoin/src/wallet/rpc/util.cpp
Line | Count | Source (jump to first uncovered line) |
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 | 0 | bool GetAvoidReuseFlag(const CWallet& wallet, const UniValue& param) { |
22 | 0 | bool can_avoid_reuse = wallet.IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE); |
23 | 0 | bool avoid_reuse = param.isNull() ? can_avoid_reuse : param.get_bool(); |
24 | |
|
25 | 0 | if (avoid_reuse && !can_avoid_reuse) { |
26 | 0 | throw JSONRPCError(RPC_WALLET_ERROR, "wallet does not have the \"avoid reuse\" feature enabled"); |
27 | 0 | } |
28 | | |
29 | 0 | return avoid_reuse; |
30 | 0 | } |
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 | | bool ParseIncludeWatchonly(const UniValue& include_watchonly, const CWallet& wallet) |
37 | 0 | { |
38 | 0 | if (include_watchonly.isNull()) { |
39 | | // if include_watchonly isn't explicitly set, then check if we have a watchonly wallet |
40 | 0 | return wallet.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS); |
41 | 0 | } |
42 | | |
43 | | // otherwise return whatever include_watchonly was set to |
44 | 0 | return include_watchonly.get_bool(); |
45 | 0 | } |
46 | | |
47 | | bool GetWalletNameFromJSONRPCRequest(const JSONRPCRequest& request, std::string& wallet_name) |
48 | 0 | { |
49 | 0 | if (request.URI.starts_with(WALLET_ENDPOINT_BASE)) { |
50 | | // wallet endpoint was used |
51 | 0 | wallet_name = UrlDecode(std::string_view{request.URI}.substr(WALLET_ENDPOINT_BASE.size())); |
52 | 0 | return true; |
53 | 0 | } |
54 | 0 | return false; |
55 | 0 | } |
56 | | |
57 | | std::shared_ptr<CWallet> GetWalletForJSONRPCRequest(const JSONRPCRequest& request) |
58 | 0 | { |
59 | 0 | CHECK_NONFATAL(request.mode == JSONRPCRequest::EXECUTE); |
60 | 0 | WalletContext& context = EnsureWalletContext(request.context); |
61 | |
|
62 | 0 | std::string wallet_name; |
63 | 0 | if (GetWalletNameFromJSONRPCRequest(request, wallet_name)) { |
64 | 0 | std::shared_ptr<CWallet> pwallet = GetWallet(context, wallet_name); |
65 | 0 | if (!pwallet) throw JSONRPCError(RPC_WALLET_NOT_FOUND, "Requested wallet does not exist or is not loaded"); |
66 | 0 | return pwallet; |
67 | 0 | } |
68 | | |
69 | 0 | size_t count{0}; |
70 | 0 | auto wallet = GetDefaultWallet(context, count); |
71 | 0 | if (wallet) return wallet; |
72 | | |
73 | 0 | if (count == 0) { |
74 | 0 | throw JSONRPCError( |
75 | 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)"); |
76 | 0 | } |
77 | 0 | throw JSONRPCError(RPC_WALLET_NOT_SPECIFIED, |
78 | 0 | "Multiple wallets are loaded. Please select which wallet to use by requesting the RPC through the /wallet/<walletname> URI path."); |
79 | 0 | } |
80 | | |
81 | | void EnsureWalletIsUnlocked(const CWallet& wallet) |
82 | 0 | { |
83 | 0 | if (wallet.IsLocked()) { |
84 | 0 | throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Please enter the wallet passphrase with walletpassphrase first."); |
85 | 0 | } |
86 | 0 | } |
87 | | |
88 | | WalletContext& EnsureWalletContext(const std::any& context) |
89 | 0 | { |
90 | 0 | auto wallet_context = util::AnyPtr<WalletContext>(context); |
91 | 0 | if (!wallet_context) { |
92 | 0 | throw JSONRPCError(RPC_INTERNAL_ERROR, "Wallet context not found"); |
93 | 0 | } |
94 | 0 | return *wallet_context; |
95 | 0 | } |
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 | | LegacyScriptPubKeyMan& EnsureLegacyScriptPubKeyMan(CWallet& wallet, bool also_create) |
99 | 0 | { |
100 | 0 | LegacyScriptPubKeyMan* spk_man = wallet.GetLegacyScriptPubKeyMan(); |
101 | 0 | if (!spk_man && also_create) { |
102 | 0 | spk_man = wallet.GetOrCreateLegacyScriptPubKeyMan(); |
103 | 0 | } |
104 | 0 | if (!spk_man) { |
105 | 0 | throw JSONRPCError(RPC_WALLET_ERROR, "Only legacy wallets are supported by this command"); |
106 | 0 | } |
107 | 0 | return *spk_man; |
108 | 0 | } |
109 | | |
110 | | const LegacyScriptPubKeyMan& EnsureConstLegacyScriptPubKeyMan(const CWallet& wallet) |
111 | 0 | { |
112 | 0 | const LegacyScriptPubKeyMan* spk_man = wallet.GetLegacyScriptPubKeyMan(); |
113 | 0 | if (!spk_man) { |
114 | 0 | throw JSONRPCError(RPC_WALLET_ERROR, "Only legacy wallets are supported by this command"); |
115 | 0 | } |
116 | 0 | return *spk_man; |
117 | 0 | } |
118 | | |
119 | | std::string LabelFromValue(const UniValue& value) |
120 | 0 | { |
121 | 0 | static const std::string empty_string; |
122 | 0 | if (value.isNull()) return empty_string; |
123 | | |
124 | 0 | const std::string& label{value.get_str()}; |
125 | 0 | if (label == "*") |
126 | 0 | throw JSONRPCError(RPC_WALLET_INVALID_LABEL_NAME, "Invalid label name"); |
127 | 0 | return label; |
128 | 0 | } |
129 | | |
130 | | void PushParentDescriptors(const CWallet& wallet, const CScript& script_pubkey, UniValue& entry) |
131 | 0 | { |
132 | 0 | UniValue parent_descs(UniValue::VARR); |
133 | 0 | for (const auto& desc: wallet.GetWalletDescriptors(script_pubkey)) { |
134 | 0 | parent_descs.push_back(desc.descriptor->ToString()); |
135 | 0 | } |
136 | 0 | entry.pushKV("parent_descs", std::move(parent_descs)); |
137 | 0 | } |
138 | | |
139 | | void HandleWalletError(const std::shared_ptr<CWallet> wallet, DatabaseStatus& status, bilingual_str& error) |
140 | 0 | { |
141 | 0 | 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 | 0 | RPCErrorCode code = RPC_WALLET_ERROR; |
145 | 0 | switch (status) { |
146 | 0 | case DatabaseStatus::FAILED_NOT_FOUND: |
147 | 0 | case DatabaseStatus::FAILED_BAD_FORMAT: |
148 | 0 | code = RPC_WALLET_NOT_FOUND; |
149 | 0 | break; |
150 | 0 | case DatabaseStatus::FAILED_ALREADY_LOADED: |
151 | 0 | code = RPC_WALLET_ALREADY_LOADED; |
152 | 0 | break; |
153 | 0 | case DatabaseStatus::FAILED_ALREADY_EXISTS: |
154 | 0 | code = RPC_WALLET_ALREADY_EXISTS; |
155 | 0 | break; |
156 | 0 | case DatabaseStatus::FAILED_INVALID_BACKUP_FILE: |
157 | 0 | code = RPC_INVALID_PARAMETER; |
158 | 0 | break; |
159 | 0 | default: // RPC_WALLET_ERROR is returned for all other cases. |
160 | 0 | break; |
161 | 0 | } |
162 | 0 | throw JSONRPCError(code, error.original); |
163 | 0 | } |
164 | 0 | } |
165 | | |
166 | | void AppendLastProcessedBlock(UniValue& entry, const CWallet& wallet) |
167 | 0 | { |
168 | 0 | AssertLockHeld(wallet.cs_wallet); |
169 | 0 | UniValue lastprocessedblock{UniValue::VOBJ}; |
170 | 0 | lastprocessedblock.pushKV("hash", wallet.GetLastBlockHash().GetHex()); |
171 | 0 | lastprocessedblock.pushKV("height", wallet.GetLastBlockHeight()); |
172 | 0 | entry.pushKV("lastprocessedblock", std::move(lastprocessedblock)); |
173 | 0 | } |
174 | | |
175 | | } // namespace wallet |