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