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 : 1713 : bool GetAvoidReuseFlag(const CWallet& wallet, const UniValue& param) {
23 : 1713 : bool can_avoid_reuse = wallet.IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE);
24 [ + + ]: 1713 : bool avoid_reuse = param.isNull() ? can_avoid_reuse : param.get_bool();
25 : :
26 [ - + ]: 1713 : 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 : 1713 : return avoid_reuse;
31 : : }
32 : :
33 : 354 : std::string EnsureUniqueWalletName(const JSONRPCRequest& request, std::optional<std::string_view> wallet_name)
34 : : {
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 [ + - + - ]: 12 : "The RPC endpoint wallet and the wallet name parameter specify different wallets");
40 : : }
41 [ - + ]: 374 : 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 [ + - + - ]: 10 : "Either the RPC endpoint wallet or the wallet name parameter must be provided");
48 : : }
49 : :
50 : 156 : return std::string{*wallet_name};
51 : : }
52 : :
53 : 19673 : std::optional<std::string> GetWalletNameFromJSONRPCRequest(const JSONRPCRequest& request)
54 : : {
55 [ - + - + : 19673 : if (request.URI.starts_with(WALLET_ENDPOINT_BASE)) {
+ + ]
56 : : // wallet endpoint was used
57 : 14757 : return UrlDecode(std::string_view{request.URI}.substr(WALLET_ENDPOINT_BASE.size()));
58 : : }
59 : 4916 : return std::nullopt;
60 : : }
61 : :
62 : 19319 : std::shared_ptr<CWallet> GetWalletForJSONRPCRequest(const JSONRPCRequest& request)
63 : : {
64 : 19319 : CHECK_NONFATAL(request.mode == JSONRPCRequest::EXECUTE);
65 : 19319 : WalletContext& context = EnsureWalletContext(request.context);
66 : :
67 [ + + ]: 19319 : if (auto wallet_name{GetWalletNameFromJSONRPCRequest(request)}) {
68 [ + - ]: 14564 : std::shared_ptr<CWallet> pwallet{GetWallet(context, *wallet_name)};
69 [ + + + - : 14575 : if (!pwallet) throw JSONRPCError(RPC_WALLET_NOT_FOUND, "Requested wallet does not exist or is not loaded");
+ - ]
70 : 14553 : return pwallet;
71 : 14575 : }
72 : :
73 : 4755 : size_t count{0};
74 : 4755 : auto wallet = GetDefaultWallet(context, count);
75 [ + + - + ]: 4755 : if (wallet) return wallet;
76 : :
77 [ + + ]: 23 : if (count == 0) {
78 : 11 : throw JSONRPCError(
79 [ + - + - ]: 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)");
80 : : }
81 : 12 : throw JSONRPCError(RPC_WALLET_NOT_SPECIFIED,
82 [ + - + - ]: 24 : "Multiple wallets are loaded. Please select which wallet to use by requesting the RPC through the /wallet/<walletname> URI path.");
83 : 4732 : }
84 : :
85 : 4192 : void EnsureWalletIsUnlocked(const CWallet& wallet)
86 : : {
87 [ + + ]: 4192 : if (wallet.IsLocked()) {
88 [ + - + - ]: 30 : throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Please enter the wallet passphrase with walletpassphrase first.");
89 : : }
90 : 4177 : }
91 : :
92 : 20573 : WalletContext& EnsureWalletContext(const std::any& context)
93 : : {
94 : 20573 : auto wallet_context = util::AnyPtr<WalletContext>(context);
95 [ - + ]: 20573 : if (!wallet_context) {
96 [ # # # # ]: 0 : throw JSONRPCError(RPC_INTERNAL_ERROR, "Wallet context not found");
97 : : }
98 : 20573 : return *wallet_context;
99 : : }
100 : :
101 : 11874 : std::string LabelFromValue(const UniValue& value)
102 : : {
103 [ + + + - ]: 11874 : static const std::string empty_string;
104 [ + + - + ]: 11874 : if (value.isNull()) return empty_string;
105 : :
106 : 344 : const std::string& label{value.get_str()};
107 [ + + ]: 344 : if (label == "*")
108 [ + - + - ]: 12 : throw JSONRPCError(RPC_WALLET_INVALID_LABEL_NAME, "Invalid label name");
109 [ - + ]: 338 : return label;
110 : : }
111 : :
112 : 36797 : void PushParentDescriptors(const CWallet& wallet, const CScript& script_pubkey, UniValue& entry)
113 : : {
114 : 36797 : UniValue parent_descs(UniValue::VARR);
115 [ + - + + ]: 73595 : for (const auto& desc: wallet.GetWalletDescriptors(script_pubkey)) {
116 : 36798 : std::string desc_str;
117 : 36798 : FlatSigningProvider dummy_provider;
118 [ + - + - : 36798 : if (!CHECK_NONFATAL(desc.descriptor->ToNormalizedString(dummy_provider, desc_str, &desc.cache))) continue;
- + ]
119 [ + - + - ]: 36798 : parent_descs.push_back(desc_str);
120 : 73595 : }
121 [ + - + - ]: 73594 : entry.pushKV("parent_descs", std::move(parent_descs));
122 : 36797 : }
123 : :
124 : 787 : void HandleWalletError(const std::shared_ptr<CWallet>& wallet, DatabaseStatus& status, bilingual_str& error)
125 : : {
126 [ + + ]: 787 : 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 : 46 : RPCErrorCode code = RPC_WALLET_ERROR;
130 [ + - + + : 46 : switch (status) {
- + ]
131 : 15 : case DatabaseStatus::FAILED_NOT_FOUND:
132 : 15 : 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 : 4 : 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 : : default: // RPC_WALLET_ERROR is returned for all other cases.
150 : : break;
151 : : }
152 [ + - ]: 46 : throw JSONRPCError(code, error.original);
153 : : }
154 : 741 : }
155 : :
156 : 1544 : void AppendLastProcessedBlock(UniValue& entry, const CWallet& wallet)
157 : : {
158 : 1544 : AssertLockHeld(wallet.cs_wallet);
159 : 1544 : UniValue lastprocessedblock{UniValue::VOBJ};
160 [ + - + - : 3088 : lastprocessedblock.pushKV("hash", wallet.GetLastBlockHash().GetHex());
+ - + - ]
161 [ + - + - : 3088 : lastprocessedblock.pushKV("height", wallet.GetLastBlockHeight());
+ - ]
162 [ + - + - ]: 3088 : entry.pushKV("lastprocessedblock", std::move(lastprocessedblock));
163 : 1544 : }
164 : :
165 : : } // namespace wallet
|