Branch data Line data Source code
1 : : // Copyright (c) 2010 Satoshi Nakamoto
2 : : // Copyright (c) 2009-present The Bitcoin Core developers
3 : : // Distributed under the MIT software license, see the accompanying
4 : : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
5 : :
6 : : #include <bitcoin-build-config.h> // IWYU pragma: keep
7 : :
8 : : #include <wallet/rpc/wallet.h>
9 : :
10 : : #include <coins.h>
11 : : #include <core_io.h>
12 : : #include <key.h>
13 : : #include <key_io.h>
14 : : #include <rpc/server.h>
15 : : #include <rpc/util.h>
16 : : #include <univalue.h>
17 : : #include <util/bip32.h>
18 : : #include <util/translation.h>
19 : : #include <wallet/context.h>
20 : : #include <wallet/export.h>
21 : : #include <wallet/receive.h>
22 : : #include <wallet/rpc/util.h>
23 : : #include <wallet/scan.h>
24 : : #include <wallet/wallet.h>
25 : : #include <wallet/walletutil.h>
26 : :
27 : : #include <algorithm>
28 : : #include <optional>
29 : : #include <string_view>
30 : :
31 : :
32 : : namespace wallet {
33 : :
34 : : using HDPubKeyMap = CWallet::HDPubKeyMap;
35 : : using HDKeyFilter = CWallet::HDKeyFilter;
36 : :
37 : : static const std::map<uint64_t, std::string> WALLET_FLAG_CAVEATS{
38 : : {WALLET_FLAG_AVOID_REUSE,
39 : : "You need to rescan the blockchain in order to correctly mark used "
40 : : "destinations in the past. Until this is done, some destinations may "
41 : : "be considered unused, even if the opposite is the case."},
42 : : };
43 : :
44 : 14 : static RPCMethod getwalletinfo()
45 : : {
46 : 14 : return RPCMethod{"getwalletinfo",
47 [ + - ]: 28 : "Returns an object containing various wallet state info.\n",
48 : : {},
49 [ + - ]: 28 : RPCResult{
50 [ + - ]: 28 : RPCResult::Type::OBJ, "", "",
51 : : {
52 : : {
53 [ + - + - ]: 28 : {RPCResult::Type::STR, "walletname", "the wallet name"},
54 [ + - + - ]: 28 : {RPCResult::Type::NUM, "walletversion", "(DEPRECATED) only related to unsupported legacy wallet, returns the latest version 169900 for backwards compatibility"},
55 [ + - + - ]: 28 : {RPCResult::Type::STR, "format", "the database format (only sqlite)"},
56 [ + - + - ]: 28 : {RPCResult::Type::NUM, "txcount", "the total number of transactions in the wallet"},
57 [ + - + - ]: 28 : {RPCResult::Type::NUM, "keypoolsize", "how many new keys are pre-generated (only counts external keys)"},
58 [ + - + - ]: 28 : {RPCResult::Type::NUM, "keypoolsize_hd_internal", "how many new keys are pre-generated for internal use (used for change outputs; 0 if external keys are used for change)"},
59 [ + - + - ]: 28 : {RPCResult::Type::NUM_TIME, "unlocked_until", /*optional=*/true, "the " + UNIX_EPOCH_TIME + " until which the wallet is unlocked for transfers, or 0 if the wallet is locked (only present for passphrase-encrypted wallets)"},
60 [ + - + - ]: 28 : {RPCResult::Type::BOOL, "private_keys_enabled", "false if privatekeys are disabled for this wallet (enforced watch-only wallet)"},
61 [ + - + - ]: 28 : {RPCResult::Type::BOOL, "avoid_reuse", "whether this wallet tracks clean/dirty coins in terms of reuse"},
62 [ + - + - ]: 28 : {RPCResult::Type::OBJ, "scanning", "current scanning details, or false if no scan is in progress",
63 : : {
64 [ + - + - ]: 28 : {RPCResult::Type::NUM, "duration", "elapsed seconds since scan start"},
65 [ + - + - ]: 28 : {RPCResult::Type::NUM, "progress", "scanning progress percentage [0.0, 1.0]"},
66 : : }, {.skip_type_check=true}, },
67 [ + - + - ]: 28 : {RPCResult::Type::BOOL, "descriptors", "whether this wallet uses descriptors for output script management"},
68 [ + - + - ]: 28 : {RPCResult::Type::BOOL, "external_signer", "whether this wallet is configured to use an external signer such as a hardware wallet"},
69 [ + - + - ]: 28 : {RPCResult::Type::BOOL, "blank", "Whether this wallet intentionally does not contain any keys, scripts, or descriptors"},
70 [ + - + - ]: 28 : {RPCResult::Type::NUM_TIME, "birthtime", /*optional=*/true, "The start time for blocks scanning. It could be modified by (re)importing any descriptor with an earlier timestamp."},
71 [ + - + - ]: 28 : {RPCResult::Type::ARR, "flags", "The flags currently set on the wallet",
72 : : {
73 [ + - + - ]: 28 : {RPCResult::Type::STR, "flag", "The name of the flag"},
74 : : }},
75 : : RESULT_LAST_PROCESSED_BLOCK,
76 : : }},
77 [ + - + - : 574 : },
+ - + - +
+ + + + +
- - - - -
- ]
78 : 14 : RPCExamples{
79 [ + - + - : 28 : HelpExampleCli("getwalletinfo", "")
+ - ]
80 [ + - + - : 56 : + HelpExampleRpc("getwalletinfo", "")
+ - + - ]
81 [ + - ]: 14 : },
82 : 14 : [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
83 : : {
84 [ # # ]: 0 : const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
85 [ # # ]: 0 : if (!pwallet) return UniValue::VNULL;
86 : :
87 : : // Make sure the results are valid at least up to the most recent block
88 : : // the user could have gotten from another RPC command prior to now
89 [ # # ]: 0 : pwallet->BlockUntilSyncedToCurrentChain();
90 : :
91 [ # # ]: 0 : LOCK(pwallet->cs_wallet);
92 : :
93 : 0 : UniValue obj(UniValue::VOBJ);
94 : :
95 : 0 : const int latest_legacy_wallet_minversion{169900};
96 : :
97 [ # # ]: 0 : size_t kpExternalSize = pwallet->KeypoolCountExternalKeys();
98 [ # # # # : 0 : obj.pushKV("walletname", pwallet->GetName());
# # ]
99 [ # # # # : 0 : obj.pushKV("walletversion", latest_legacy_wallet_minversion);
# # ]
100 [ # # # # : 0 : obj.pushKV("format", pwallet->GetDatabase().Format());
# # # # ]
101 [ # # # # : 0 : obj.pushKV("txcount", pwallet->mapWallet.size());
# # ]
102 [ # # # # : 0 : obj.pushKV("keypoolsize", kpExternalSize);
# # ]
103 [ # # # # : 0 : obj.pushKV("keypoolsize_hd_internal", pwallet->GetKeyPoolSize() - kpExternalSize);
# # # # ]
104 : :
105 [ # # # # ]: 0 : if (pwallet->HasEncryptionKeys()) {
106 [ # # # # : 0 : obj.pushKV("unlocked_until", pwallet->nRelockTime);
# # ]
107 : : }
108 [ # # # # : 0 : obj.pushKV("private_keys_enabled", !pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS));
# # # # ]
109 [ # # # # : 0 : obj.pushKV("avoid_reuse", pwallet->IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE));
# # # # ]
110 [ # # # # ]: 0 : if (pwallet->Scanner().IsScanning()) {
111 : 0 : UniValue scanning(UniValue::VOBJ);
112 [ # # # # : 0 : scanning.pushKV("duration", Ticks<std::chrono::seconds>(pwallet->Scanner().ScanningDuration()));
# # # # ]
113 [ # # # # : 0 : scanning.pushKV("progress", pwallet->Scanner().ScanningProgress());
# # # # ]
114 [ # # # # ]: 0 : obj.pushKV("scanning", std::move(scanning));
115 : 0 : } else {
116 [ # # # # : 0 : obj.pushKV("scanning", false);
# # ]
117 : : }
118 [ # # # # : 0 : obj.pushKV("descriptors", pwallet->IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS));
# # # # ]
119 [ # # # # : 0 : obj.pushKV("external_signer", pwallet->IsWalletFlagSet(WALLET_FLAG_EXTERNAL_SIGNER));
# # # # ]
120 [ # # # # : 0 : obj.pushKV("blank", pwallet->IsWalletFlagSet(WALLET_FLAG_BLANK_WALLET));
# # # # ]
121 [ # # ]: 0 : if (int64_t birthtime = pwallet->GetBirthTime(); birthtime != UNKNOWN_TIME) {
122 [ # # # # : 0 : obj.pushKV("birthtime", birthtime);
# # ]
123 : : }
124 : :
125 : : // Push known flags
126 : 0 : UniValue flags(UniValue::VARR);
127 [ # # ]: 0 : uint64_t wallet_flags = pwallet->GetWalletFlags();
128 [ # # ]: 0 : for (uint64_t i = 0; i < 64; ++i) {
129 : 0 : uint64_t flag = uint64_t{1} << i;
130 [ # # ]: 0 : if (flag & wallet_flags) {
131 [ # # ]: 0 : if (flag & KNOWN_WALLET_FLAGS) {
132 [ # # # # : 0 : flags.push_back(WALLET_FLAG_TO_STRING.at(WalletFlags{flag}));
# # ]
133 : : } else {
134 [ # # # # : 0 : flags.push_back(strprintf("unknown_flag_%u", i));
# # ]
135 : : }
136 : : }
137 : : }
138 [ # # # # : 0 : obj.pushKV("flags", flags);
# # ]
139 : :
140 [ # # ]: 0 : AppendLastProcessedBlock(obj, *pwallet);
141 : 0 : return obj;
142 [ # # ]: 0 : },
143 [ + - + - ]: 56 : };
144 [ + - + - : 504 : }
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - -
- - - ]
145 : :
146 : 14 : static RPCMethod listwalletdir()
147 : : {
148 : 14 : return RPCMethod{"listwalletdir",
149 [ + - ]: 28 : "Returns a list of wallets in the wallet directory.\n",
150 : : {},
151 [ + - ]: 28 : RPCResult{
152 [ + - ]: 28 : RPCResult::Type::OBJ, "", "",
153 : : {
154 [ + - + - ]: 28 : {RPCResult::Type::ARR, "wallets", "",
155 : : {
156 [ + - + - ]: 28 : {RPCResult::Type::OBJ, "", "",
157 : : {
158 [ + - + - ]: 28 : {RPCResult::Type::STR, "name", "The wallet name"},
159 [ + - + - ]: 28 : {RPCResult::Type::ARR, "warnings", "Warning messages related to loading the wallet (may be empty).",
160 : : {
161 [ + - + - ]: 28 : {RPCResult::Type::STR, "", ""},
162 : : }},
163 : : }},
164 : : }},
165 : : }
166 [ + - + - : 210 : },
+ - + - +
- + + + +
+ + + + -
- - - - -
- - ]
167 : 14 : RPCExamples{
168 [ + - + - : 28 : HelpExampleCli("listwalletdir", "")
+ - ]
169 [ + - + - : 56 : + HelpExampleRpc("listwalletdir", "")
+ - + - ]
170 [ + - ]: 14 : },
171 : 14 : [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
172 : : {
173 : 0 : UniValue wallets(UniValue::VARR);
174 [ # # # # : 0 : for (const auto& [path, db_type] : ListDatabases(GetWalletDir())) {
# # ]
175 : 0 : UniValue wallet(UniValue::VOBJ);
176 [ # # # # : 0 : wallet.pushKV("name", path.utf8string());
# # # # ]
177 : 0 : UniValue warnings(UniValue::VARR);
178 [ # # ]: 0 : if (db_type == "bdb") {
179 [ # # # # ]: 0 : warnings.push_back("This wallet is a legacy wallet and will need to be migrated with migratewallet before it can be loaded");
180 : : }
181 [ # # # # : 0 : wallet.pushKV("warnings", warnings);
# # ]
182 [ # # ]: 0 : wallets.push_back(std::move(wallet));
183 : 0 : }
184 : :
185 : 0 : UniValue result(UniValue::VOBJ);
186 [ # # # # ]: 0 : result.pushKV("wallets", std::move(wallets));
187 : 0 : return result;
188 : 0 : },
189 [ + - + - ]: 56 : };
190 [ + - + - : 140 : }
+ - + - +
- - - ]
191 : :
192 : 14 : static RPCMethod listwallets()
193 : : {
194 : 14 : return RPCMethod{"listwallets",
195 [ + - ]: 28 : "Returns a list of currently loaded wallets.\n"
196 : : "For full information on the wallet, use \"getwalletinfo\"\n",
197 : : {},
198 [ + - ]: 28 : RPCResult{
199 [ + - ]: 28 : RPCResult::Type::ARR, "", "",
200 : : {
201 [ + - + - ]: 28 : {RPCResult::Type::STR, "walletname", "the wallet name"},
202 : : }
203 [ + - + - : 56 : },
+ + - - ]
204 : 14 : RPCExamples{
205 [ + - + - : 28 : HelpExampleCli("listwallets", "")
+ - ]
206 [ + - + - : 56 : + HelpExampleRpc("listwallets", "")
+ - + - ]
207 [ + - ]: 14 : },
208 : 14 : [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
209 : : {
210 : 0 : UniValue obj(UniValue::VARR);
211 : :
212 [ # # ]: 0 : WalletContext& context = EnsureWalletContext(request.context);
213 [ # # # # ]: 0 : for (const std::shared_ptr<CWallet>& wallet : GetWallets(context)) {
214 [ # # ]: 0 : LOCK(wallet->cs_wallet);
215 [ # # # # : 0 : obj.push_back(wallet->GetName());
# # ]
216 : 0 : }
217 : :
218 : 0 : return obj;
219 : 0 : },
220 [ + - + - ]: 56 : };
221 [ + - ]: 28 : }
222 : :
223 : 14 : static RPCMethod loadwallet()
224 : : {
225 : 14 : return RPCMethod{
226 : 14 : "loadwallet",
227 [ + - ]: 28 : "Loads a wallet from a wallet file or directory."
228 : : "\nNote that all wallet command-line options used when starting bitcoind will be"
229 : : "\napplied to the new wallet.\n",
230 : : {
231 [ + - + - ]: 28 : {"filename", RPCArg::Type::STR, RPCArg::Optional::NO, "The path to the directory of the wallet to be loaded, either absolute or relative to the \"wallets\" directory. The \"wallets\" directory is set by the -walletdir option and defaults to the \"wallets\" folder within the data directory."},
232 [ + - + - ]: 28 : {"load_on_startup", RPCArg::Type::BOOL, RPCArg::Optional::OMITTED, "Save wallet name to persistent settings and load on startup. True to add wallet to startup list, false to remove, null to leave unchanged."},
233 : : },
234 [ + - ]: 28 : RPCResult{
235 [ + - + - ]: 28 : RPCResult::Type::OBJ, "", "",
236 : : {
237 [ + - + - ]: 28 : {RPCResult::Type::STR, "name", "The wallet name if loaded successfully."},
238 [ + - + - ]: 28 : {RPCResult::Type::ARR, "warnings", /*optional=*/true, "Warning messages, if any, related to loading the wallet.",
239 : : {
240 [ + - + - ]: 28 : {RPCResult::Type::STR, "", ""},
241 : : }},
242 : : }
243 [ + - + - : 126 : },
+ - + + +
+ - - -
- ]
244 : 14 : RPCExamples{
245 : : "\nLoad wallet from the wallet dir:\n"
246 [ + - + - : 28 : + HelpExampleCli("loadwallet", "\"walletname\"")
+ - + - ]
247 [ + - + - : 56 : + HelpExampleRpc("loadwallet", "\"walletname\"")
+ - + - ]
248 : 14 : + "\nLoad wallet using absolute path (Unix):\n"
249 [ + - + - : 56 : + HelpExampleCli("loadwallet", "\"/path/to/walletname/\"")
+ - + - ]
250 [ + - + - : 56 : + HelpExampleRpc("loadwallet", "\"/path/to/walletname/\"")
+ - + - ]
251 : 14 : + "\nLoad wallet using absolute path (Windows):\n"
252 [ + - + - : 56 : + HelpExampleCli("loadwallet", "\"DriveLetter:\\path\\to\\walletname\\\"")
+ - + - ]
253 [ + - + - : 56 : + HelpExampleRpc("loadwallet", R"("DriveLetter:\\path\\to\\walletname")")
+ - + - ]
254 [ + - ]: 14 : },
255 : 14 : [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
256 : : {
257 : 0 : WalletContext& context = EnsureWalletContext(request.context);
258 [ # # ]: 0 : const std::string name(request.params[0].get_str());
259 : :
260 [ # # ]: 0 : DatabaseOptions options;
261 : 0 : DatabaseStatus status;
262 [ # # ]: 0 : ReadDatabaseArgs(*context.args, options);
263 : 0 : options.require_existing = true;
264 [ # # ]: 0 : bilingual_str error;
265 : 0 : std::vector<bilingual_str> warnings;
266 [ # # # # : 0 : std::optional<bool> load_on_start = request.params[1].isNull() ? std::nullopt : std::optional<bool>(request.params[1].get_bool());
# # # # ]
267 : :
268 : 0 : {
269 [ # # ]: 0 : LOCK(context.wallets_mutex);
270 [ # # # # ]: 0 : if (std::any_of(context.wallets.begin(), context.wallets.end(), [&name](const auto& wallet) { return wallet->GetName() == name; })) {
271 [ # # # # ]: 0 : throw JSONRPCError(RPC_WALLET_ALREADY_LOADED, "Wallet \"" + name + "\" is already loaded.");
272 : : }
273 : 0 : }
274 : :
275 [ # # ]: 0 : std::shared_ptr<CWallet> const wallet = LoadWallet(context, name, load_on_start, options, status, error, warnings);
276 : :
277 [ # # ]: 0 : HandleWalletError(wallet, status, error);
278 : :
279 : 0 : UniValue obj(UniValue::VOBJ);
280 [ # # # # : 0 : obj.pushKV("name", wallet->GetName());
# # ]
281 [ # # ]: 0 : PushWarnings(warnings, obj);
282 : :
283 [ # # ]: 0 : return obj;
284 : 0 : },
285 [ + - + - : 84 : };
+ + - - ]
286 [ + - + - : 140 : }
+ - + - +
- - - -
- ]
287 : :
288 : 14 : static RPCMethod setwalletflag()
289 : : {
290 : 14 : std::string flags;
291 [ + + ]: 112 : for (auto& it : STRING_TO_WALLET_FLAG)
292 [ + + ]: 98 : if (it.second & MUTABLE_WALLET_FLAGS)
293 [ - + + - ]: 28 : flags += (flags == "" ? "" : ", ") + it.first;
294 : :
295 : 14 : return RPCMethod{
296 [ + - ]: 28 : "setwalletflag",
297 [ + - ]: 28 : "Change the state of the given wallet flag for a wallet.\n",
298 : : {
299 [ + - + - ]: 28 : {"flag", RPCArg::Type::STR, RPCArg::Optional::NO, "The name of the flag to change. Current available flags: " + flags},
300 [ + - + - : 42 : {"value", RPCArg::Type::BOOL, RPCArg::Default{true}, "The new state."},
+ - ]
301 : : },
302 [ + - ]: 28 : RPCResult{
303 [ + - + - ]: 28 : RPCResult::Type::OBJ, "", "",
304 : : {
305 [ + - + - ]: 28 : {RPCResult::Type::STR, "flag_name", "The name of the flag that was modified"},
306 [ + - + - ]: 28 : {RPCResult::Type::BOOL, "flag_state", "The new state of the flag"},
307 [ + - + - ]: 28 : {RPCResult::Type::STR, "warnings", /*optional=*/true, "Any warnings associated with the change"},
308 : : }
309 [ + - + - : 112 : },
+ + - - ]
310 : 14 : RPCExamples{
311 [ + - + - : 28 : HelpExampleCli("setwalletflag", "avoid_reuse")
+ - ]
312 [ + - + - : 56 : + HelpExampleRpc("setwalletflag", "\"avoid_reuse\"")
+ - + - ]
313 [ + - ]: 14 : },
314 : 14 : [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
315 : : {
316 : 0 : std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
317 [ # # ]: 0 : if (!pwallet) return UniValue::VNULL;
318 : :
319 [ # # # # : 0 : std::string flag_str = request.params[0].get_str();
# # ]
320 [ # # # # : 0 : bool value = request.params[1].isNull() || request.params[1].get_bool();
# # # # #
# ]
321 : :
322 [ # # ]: 0 : if (!STRING_TO_WALLET_FLAG.contains(flag_str)) {
323 [ # # # # ]: 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Unknown wallet flag: %s", flag_str));
324 : : }
325 : :
326 [ # # ]: 0 : auto flag = STRING_TO_WALLET_FLAG.at(flag_str);
327 : :
328 [ # # ]: 0 : if (!(flag & MUTABLE_WALLET_FLAGS)) {
329 [ # # # # ]: 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Wallet flag is immutable: %s", flag_str));
330 : : }
331 : :
332 : 0 : UniValue res(UniValue::VOBJ);
333 : :
334 [ # # # # ]: 0 : if (pwallet->IsWalletFlagSet(flag) == value) {
335 [ # # # # : 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Wallet flag is already set to %s: %s", value ? "true" : "false", flag_str));
# # ]
336 : : }
337 : :
338 [ # # # # : 0 : res.pushKV("flag_name", flag_str);
# # ]
339 [ # # # # : 0 : res.pushKV("flag_state", value);
# # ]
340 : :
341 [ # # ]: 0 : if (value) {
342 [ # # ]: 0 : pwallet->SetWalletFlag(flag);
343 : : } else {
344 [ # # ]: 0 : pwallet->UnsetWalletFlag(flag);
345 : : }
346 : :
347 [ # # # # : 0 : if (flag && value && WALLET_FLAG_CAVEATS.contains(flag)) {
# # ]
348 [ # # # # : 0 : res.pushKV("warnings", WALLET_FLAG_CAVEATS.at(flag));
# # # # ]
349 : : }
350 : :
351 : 0 : return res;
352 : 0 : },
353 [ + - + - : 70 : };
+ + - - ]
354 [ + - + - : 154 : }
+ - + - +
- - - -
- ]
355 : :
356 : 14 : static RPCMethod createwallet()
357 : : {
358 : 14 : return RPCMethod{
359 : 14 : "createwallet",
360 [ + - ]: 28 : "Creates and loads a new wallet.\n",
361 : : {
362 [ + - + - ]: 28 : {"wallet_name", RPCArg::Type::STR, RPCArg::Optional::NO, "The name for the new wallet. If this is a path, the wallet will be created at the path location."},
363 [ + - + - : 42 : {"disable_private_keys", RPCArg::Type::BOOL, RPCArg::Default{false}, "Disable the possibility of private keys (only watchonlys are possible in this mode)."},
+ - ]
364 [ + - + - : 42 : {"blank", RPCArg::Type::BOOL, RPCArg::Default{false}, "Create a blank wallet. A blank wallet has no keys."},
+ - ]
365 [ + - + - ]: 28 : {"passphrase", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "Encrypt the wallet with this passphrase."},
366 [ + - + - : 42 : {"avoid_reuse", RPCArg::Type::BOOL, RPCArg::Default{false}, "Keep track of coin reuse, and treat dirty and clean coins differently with privacy considerations in mind."},
+ - ]
367 [ + - + - : 42 : {"descriptors", RPCArg::Type::BOOL, RPCArg::Default{true}, "If set, must be \"true\""},
+ - ]
368 [ + - + - ]: 28 : {"load_on_startup", RPCArg::Type::BOOL, RPCArg::Optional::OMITTED, "Save wallet name to persistent settings and load on startup. True to add wallet to startup list, false to remove, null to leave unchanged."},
369 [ + - + - : 42 : {"external_signer", RPCArg::Type::BOOL, RPCArg::Default{false}, "Use an external signer such as a hardware wallet. Requires -signer to be configured. Wallet creation will fail if keys cannot be fetched. Requires disable_private_keys and descriptors set to true."},
+ - ]
370 : : },
371 [ + - ]: 28 : RPCResult{
372 [ + - + - ]: 28 : RPCResult::Type::OBJ, "", "",
373 : : {
374 [ + - + - ]: 28 : {RPCResult::Type::STR, "name", "The wallet name if created successfully. If the wallet was created using a full path, the wallet_name will be the full path."},
375 [ + - + - ]: 28 : {RPCResult::Type::ARR, "warnings", /*optional=*/true, "Warning messages, if any, related to creating and loading the wallet.",
376 : : {
377 [ + - + - ]: 28 : {RPCResult::Type::STR, "", ""},
378 : : }},
379 : : }
380 [ + - + - : 126 : },
+ - + + +
+ - - -
- ]
381 : 14 : RPCExamples{
382 [ + - + - : 28 : HelpExampleCli("createwallet", "\"testwallet\"")
+ - ]
383 [ + - + - : 56 : + HelpExampleRpc("createwallet", "\"testwallet\"")
+ - + - ]
384 [ + - + - : 98 : + HelpExampleCliNamed("createwallet", {{"wallet_name", "descriptors"}, {"avoid_reuse", true}, {"load_on_startup", true}})
+ - + - +
+ - - ]
385 [ + - + - : 98 : + HelpExampleRpcNamed("createwallet", {{"wallet_name", "descriptors"}, {"avoid_reuse", true}, {"load_on_startup", true}})
+ - + - +
+ - - ]
386 [ + - ]: 14 : },
387 : 14 : [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
388 : : {
389 : 0 : WalletContext& context = EnsureWalletContext(request.context);
390 : 0 : uint64_t flags = 0;
391 [ # # # # ]: 0 : if (!request.params[1].isNull() && request.params[1].get_bool()) {
392 : : flags |= WALLET_FLAG_DISABLE_PRIVATE_KEYS;
393 : : }
394 : :
395 [ # # # # ]: 0 : if (!request.params[2].isNull() && request.params[2].get_bool()) {
396 : 0 : flags |= WALLET_FLAG_BLANK_WALLET;
397 : : }
398 [ # # ]: 0 : SecureString passphrase;
399 [ # # ]: 0 : passphrase.reserve(100);
400 : 0 : std::vector<bilingual_str> warnings;
401 [ # # # # ]: 0 : if (!request.params[3].isNull()) {
402 [ # # # # : 0 : passphrase = std::string_view{request.params[3].get_str()};
# # # # ]
403 [ # # ]: 0 : if (passphrase.empty()) {
404 : : // Empty string means unencrypted
405 [ # # # # : 0 : warnings.emplace_back(Untranslated("Empty string given as passphrase, wallet will not be encrypted."));
# # ]
406 : : }
407 : : }
408 : :
409 [ # # # # : 0 : if (!request.params[4].isNull() && request.params[4].get_bool()) {
# # # # #
# ]
410 : 0 : flags |= WALLET_FLAG_AVOID_REUSE;
411 : : }
412 : 0 : flags |= WALLET_FLAG_DESCRIPTORS;
413 [ # # # # ]: 0 : if (!self.Arg<bool>("descriptors")) {
414 [ # # # # ]: 0 : throw JSONRPCError(RPC_WALLET_ERROR, "descriptors argument must be set to \"true\"; it is no longer possible to create a legacy wallet.");
415 : : }
416 [ # # # # : 0 : if (!request.params[7].isNull() && request.params[7].get_bool()) {
# # # # #
# ]
417 : : #ifdef ENABLE_EXTERNAL_SIGNER
418 : 0 : flags |= WALLET_FLAG_EXTERNAL_SIGNER;
419 : : #else
420 : : throw JSONRPCError(RPC_WALLET_ERROR, "Compiled without external signing support (required for external signing)");
421 : : #endif
422 : : }
423 : :
424 [ # # ]: 0 : DatabaseOptions options;
425 : 0 : DatabaseStatus status;
426 [ # # ]: 0 : ReadDatabaseArgs(*context.args, options);
427 : 0 : options.require_create = true;
428 : 0 : options.create_flags = flags;
429 [ # # ]: 0 : options.create_passphrase = passphrase;
430 [ # # ]: 0 : bilingual_str error;
431 [ # # # # : 0 : std::optional<bool> load_on_start = request.params[6].isNull() ? std::nullopt : std::optional<bool>(request.params[6].get_bool());
# # # # ]
432 [ # # # # : 0 : const std::shared_ptr<CWallet> wallet = CreateWallet(context, request.params[0].get_str(), load_on_start, options, status, error, warnings);
# # ]
433 [ # # ]: 0 : HandleWalletError(wallet, status, error);
434 : :
435 : 0 : UniValue obj(UniValue::VOBJ);
436 [ # # # # : 0 : obj.pushKV("name", wallet->GetName());
# # ]
437 [ # # ]: 0 : PushWarnings(warnings, obj);
438 : :
439 [ # # ]: 0 : return obj;
440 : 0 : },
441 [ + - + - : 168 : };
+ + - - ]
442 [ + - + - : 336 : }
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
- - - - -
- - - ]
443 : :
444 : 14 : static RPCMethod unloadwallet()
445 : : {
446 : 14 : return RPCMethod{"unloadwallet",
447 [ + - ]: 28 : "Unloads the wallet referenced by the request endpoint or the wallet_name argument.\n"
448 : : "If both are specified, they must be identical.",
449 : : {
450 [ + - + - : 42 : {"wallet_name", RPCArg::Type::STR, RPCArg::DefaultHint{"the wallet name from the RPC endpoint"}, "The name of the wallet to unload. If provided both here and in the RPC endpoint, the two must be identical."},
+ - ]
451 [ + - + - ]: 28 : {"load_on_startup", RPCArg::Type::BOOL, RPCArg::Optional::OMITTED, "Save wallet name to persistent settings and load on startup. True to add wallet to startup list, false to remove, null to leave unchanged."},
452 : : },
453 [ + - + - : 56 : RPCResult{RPCResult::Type::OBJ, "", "", {
+ - ]
454 [ + - + - ]: 28 : {RPCResult::Type::ARR, "warnings", /*optional=*/true, "Warning messages, if any, related to unloading the wallet.",
455 : : {
456 [ + - + - ]: 28 : {RPCResult::Type::STR, "", ""},
457 : : }},
458 [ + - + - : 98 : }},
+ - + + +
+ - - -
- ]
459 : 14 : RPCExamples{
460 [ + - + - : 28 : HelpExampleCli("unloadwallet", "wallet_name")
+ - ]
461 [ + - + - : 56 : + HelpExampleRpc("unloadwallet", R"("wallet_name")")
+ - + - ]
462 [ + - ]: 14 : },
463 : 14 : [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
464 : : {
465 : 0 : const std::string wallet_name{EnsureUniqueWalletName(request, self.MaybeArg<std::string_view>("wallet_name"))};
466 : :
467 [ # # ]: 0 : WalletContext& context = EnsureWalletContext(request.context);
468 [ # # ]: 0 : std::shared_ptr<CWallet> wallet = GetWallet(context, wallet_name);
469 [ # # ]: 0 : if (!wallet) {
470 [ # # # # ]: 0 : throw JSONRPCError(RPC_WALLET_NOT_FOUND, "Requested wallet does not exist or is not loaded");
471 : : }
472 : :
473 : 0 : std::vector<bilingual_str> warnings;
474 : 0 : {
475 [ # # ]: 0 : WalletRescanReserver reserver(*wallet);
476 [ # # # # ]: 0 : if (!reserver.reserve()) {
477 [ # # # # ]: 0 : throw JSONRPCError(RPC_WALLET_ERROR, "Wallet is currently rescanning. Abort existing rescan or wait.");
478 : : }
479 : :
480 : : // Release the "main" shared pointer and prevent further notifications.
481 : : // Note that any attempt to load the same wallet would fail until the wallet
482 : : // is destroyed (see CheckUniqueFileid).
483 [ # # ]: 0 : std::optional<bool> load_on_start{self.MaybeArg<bool>("load_on_startup")};
484 [ # # # # ]: 0 : if (!RemoveWallet(context, wallet, load_on_start, warnings)) {
485 [ # # # # ]: 0 : throw JSONRPCError(RPC_MISC_ERROR, "Requested wallet already unloaded");
486 : : }
487 : 0 : }
488 : :
489 [ # # ]: 0 : WaitForDeleteWallet(std::move(wallet));
490 : :
491 : 0 : UniValue result(UniValue::VOBJ);
492 [ # # ]: 0 : PushWarnings(warnings, result);
493 : :
494 : 0 : return result;
495 [ # # ]: 0 : },
496 [ + - + - : 84 : };
+ + - - ]
497 [ + - + - : 112 : }
+ - + - -
- ]
498 : :
499 : 14 : RPCMethod simulaterawtransaction()
500 : : {
501 : 14 : return RPCMethod{
502 : 14 : "simulaterawtransaction",
503 [ + - ]: 28 : "Calculate the balance change resulting in the signing and broadcasting of the given transaction(s).\n",
504 : : {
505 [ + - + - ]: 28 : {"rawtxs", RPCArg::Type::ARR, RPCArg::Optional::NO, "An array of hex strings of raw transactions.\n",
506 : : {
507 [ + - + - ]: 28 : {"rawtx", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, ""},
508 : : },
509 : : },
510 [ + - + - ]: 28 : {"options", RPCArg::Type::OBJ_NAMED_PARAMS, RPCArg::Optional::OMITTED, "",
511 : : {
512 [ + - + - : 42 : {"include_watchonly", RPCArg::Type::BOOL, RPCArg::Default{false}, "(DEPRECATED) No longer used"},
+ - ]
513 : : },
514 : : },
515 : : },
516 [ + - ]: 28 : RPCResult{
517 [ + - + - ]: 28 : RPCResult::Type::OBJ, "", "",
518 : : {
519 [ + - + - ]: 28 : {RPCResult::Type::STR_AMOUNT, "balance_change", "The wallet balance change (negative means decrease)."},
520 : : }
521 [ + - + - : 56 : },
+ + - - ]
522 : 14 : RPCExamples{
523 [ + - + - : 28 : HelpExampleCli("simulaterawtransaction", "[\"myhex\"]")
+ - ]
524 [ + - + - : 56 : + HelpExampleRpc("simulaterawtransaction", "[\"myhex\"]")
+ - + - ]
525 [ + - ]: 14 : },
526 : 14 : [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
527 : : {
528 [ # # ]: 0 : const std::shared_ptr<const CWallet> rpc_wallet = GetWalletForJSONRPCRequest(request);
529 [ # # ]: 0 : if (!rpc_wallet) return UniValue::VNULL;
530 [ # # ]: 0 : const CWallet& wallet = *rpc_wallet;
531 : :
532 [ # # ]: 0 : LOCK(wallet.cs_wallet);
533 : :
534 [ # # # # ]: 0 : const auto& txs = request.params[0].get_array();
535 : 0 : CAmount changes{0};
536 : 0 : std::map<COutPoint, CAmount> new_utxos; // UTXO:s that were made available in transaction array
537 : 0 : std::set<COutPoint> spent;
538 : :
539 [ # # # # ]: 0 : for (size_t i = 0; i < txs.size(); ++i) {
540 [ # # ]: 0 : CMutableTransaction mtx;
541 [ # # # # : 0 : if (!DecodeHexTx(mtx, txs[i].get_str(), /*try_no_witness=*/ true, /*try_witness=*/ true)) {
# # # # ]
542 [ # # # # ]: 0 : throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Transaction hex string decoding failure.");
543 : : }
544 : :
545 : : // Fetch previous transactions (inputs)
546 : 0 : std::map<COutPoint, Coin> coins;
547 [ # # ]: 0 : for (const CTxIn& txin : mtx.vin) {
548 [ # # ]: 0 : coins[txin.prevout]; // Create empty map entry keyed by prevout.
549 : : }
550 [ # # ]: 0 : wallet.chain().findCoins(coins);
551 : :
552 : : // Fetch debit; we are *spending* these; if the transaction is signed and
553 : : // broadcast, we will lose everything in these
554 [ # # ]: 0 : for (const auto& txin : mtx.vin) {
555 : 0 : const auto& outpoint = txin.prevout;
556 [ # # ]: 0 : if (spent.contains(outpoint)) {
557 [ # # # # ]: 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, "Transaction(s) are spending the same output more than once");
558 : : }
559 [ # # ]: 0 : if (new_utxos.contains(outpoint)) {
560 [ # # ]: 0 : changes -= new_utxos.at(outpoint);
561 : 0 : new_utxos.erase(outpoint);
562 : : } else {
563 [ # # # # ]: 0 : if (coins.at(outpoint).IsSpent()) {
564 [ # # # # ]: 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, "One or more transaction inputs are missing or have been spent already");
565 : : }
566 [ # # ]: 0 : changes -= wallet.GetDebit(txin);
567 : : }
568 [ # # ]: 0 : spent.insert(outpoint);
569 : : }
570 : :
571 : : // Iterate over outputs; we are *receiving* these, if the wallet considers
572 : : // them "mine"; if the transaction is signed and broadcast, we will receive
573 : : // everything in these
574 : : // Also populate new_utxos in case these are spent in later transactions
575 : :
576 [ # # ]: 0 : const auto& hash = mtx.GetHash();
577 [ # # # # ]: 0 : for (size_t i = 0; i < mtx.vout.size(); ++i) {
578 [ # # ]: 0 : const auto& txout = mtx.vout[i];
579 [ # # ]: 0 : bool is_mine = wallet.IsMine(txout);
580 [ # # # # ]: 0 : changes += new_utxos[COutPoint(hash, i)] = is_mine ? txout.nValue : 0;
581 : : }
582 : 0 : }
583 : :
584 : 0 : UniValue result(UniValue::VOBJ);
585 [ # # # # : 0 : result.pushKV("balance_change", ValueFromAmount(changes));
# # ]
586 : :
587 : 0 : return result;
588 [ # # ]: 0 : }
589 [ + - + - : 168 : };
+ - + - +
+ + + + +
- - - - -
- ]
590 [ + - + - : 140 : }
+ - + - +
- - - ]
591 : :
592 : 14 : static RPCMethod migratewallet()
593 : : {
594 : 14 : return RPCMethod{
595 : 14 : "migratewallet",
596 [ + - ]: 28 : "Migrate the wallet to a descriptor wallet.\n"
597 : : "A new wallet backup will need to be made.\n"
598 : : "\nThe migration process will create a backup of the wallet before migrating. This backup\n"
599 : : "file will be named <wallet name>-<timestamp>.legacy.bak and can be found in the directory\n"
600 : : "for this wallet. In the event of an incorrect migration, the backup can be restored using restorewallet."
601 : : "\nEncrypted wallets must have the passphrase provided as an argument to this call.\n"
602 : : "\nThis RPC may take a long time to complete. Increasing the RPC client timeout is recommended.",
603 : : {
604 [ + - + - : 42 : {"wallet_name", RPCArg::Type::STR, RPCArg::DefaultHint{"the wallet name from the RPC endpoint"}, "The name of the wallet to migrate. If provided both here and in the RPC endpoint, the two must be identical."},
+ - ]
605 [ + - + - ]: 28 : {"passphrase", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "The wallet passphrase"},
606 [ + - + - : 42 : {"load_wallet", RPCArg::Type::BOOL, RPCArg::Default{true}, "Load the wallet after migration."},
+ - ]
607 : : },
608 [ + - ]: 28 : RPCResult{
609 [ + - + - ]: 28 : RPCResult::Type::OBJ, "", "",
610 : : {
611 [ + - + - ]: 28 : {RPCResult::Type::STR, "wallet_name", "The name of the primary migrated wallet"},
612 [ + - + - ]: 28 : {RPCResult::Type::STR, "watchonly_name", /*optional=*/true, "The name of the migrated wallet containing the watchonly scripts"},
613 [ + - + - ]: 28 : {RPCResult::Type::STR, "solvables_name", /*optional=*/true, "The name of the migrated wallet containing solvable but not watched scripts"},
614 [ + - + - ]: 28 : {RPCResult::Type::STR, "backup_path", "The location of the backup of the original wallet"},
615 : : }
616 [ + - + - : 140 : },
+ + - - ]
617 : 14 : RPCExamples{
618 [ + - + - : 28 : HelpExampleCli("migratewallet", "")
+ - ]
619 [ + - + - : 56 : + HelpExampleRpc("migratewallet", "")
+ - + - ]
620 [ + - ]: 14 : },
621 : 14 : [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
622 : : {
623 : 0 : const std::string wallet_name{EnsureUniqueWalletName(request, self.MaybeArg<std::string_view>("wallet_name"))};
624 : :
625 [ # # ]: 0 : SecureString wallet_pass;
626 [ # # ]: 0 : wallet_pass.reserve(100);
627 [ # # # # ]: 0 : if (!request.params[1].isNull()) {
628 [ # # # # : 0 : wallet_pass = std::string_view{request.params[1].get_str()};
# # # # ]
629 : : }
630 : :
631 [ # # ]: 0 : const bool loadwallet = self.Arg<bool>("load_wallet");
632 : :
633 [ # # ]: 0 : WalletContext& context = EnsureWalletContext(request.context);
634 [ # # ]: 0 : util::Result<MigrationResult> res = MigrateLegacyToDescriptor(wallet_name, wallet_pass, context, loadwallet);
635 [ # # ]: 0 : if (!res) {
636 [ # # # # ]: 0 : throw JSONRPCError(RPC_WALLET_ERROR, util::ErrorString(res).original);
637 : : }
638 : :
639 : 0 : UniValue r{UniValue::VOBJ};
640 [ # # # # : 0 : r.pushKV("wallet_name", res->wallet_name);
# # ]
641 [ # # ]: 0 : if (res->watchonly_wallet_name.has_value()) {
642 [ # # # # : 0 : r.pushKV("watchonly_name", res->watchonly_wallet_name.value());
# # ]
643 : : }
644 [ # # ]: 0 : if (res->solvables_wallet_name.has_value()) {
645 [ # # # # : 0 : r.pushKV("solvables_name", res->solvables_wallet_name.value());
# # ]
646 : : }
647 [ # # # # : 0 : r.pushKV("backup_path", res->backup_path.utf8string());
# # # # ]
648 : :
649 : 0 : return r;
650 : 0 : },
651 [ + - + - : 98 : };
+ + - - ]
652 [ + - + - : 196 : }
+ - + - +
- + - + -
- - - - ]
653 : :
654 : 14 : RPCMethod gethdkeys()
655 : : {
656 : 14 : return RPCMethod{
657 : 14 : "gethdkeys",
658 [ + - ]: 28 : "List all BIP 32 HD keys in the wallet and which descriptors use them.\n",
659 : : {
660 [ + - + - ]: 28 : {"options", RPCArg::Type::OBJ_NAMED_PARAMS, RPCArg::Optional::OMITTED, "", {
661 [ + - + - : 42 : {"active_only", RPCArg::Type::BOOL, RPCArg::Default{false}, "Show the keys for only active descriptors"},
+ - ]
662 [ + - + - : 42 : {"private", RPCArg::Type::BOOL, RPCArg::Default{false}, "Show private keys"}
+ - ]
663 : : }},
664 : : },
665 [ + - + - : 56 : RPCResult{RPCResult::Type::ARR, "", "", {
+ - ]
666 : : {
667 [ + - + - ]: 28 : {RPCResult::Type::OBJ, "", "", {
668 [ + - + - ]: 28 : {RPCResult::Type::STR, "xpub", "The extended public key"},
669 [ + - + - ]: 28 : {RPCResult::Type::BOOL, "has_private", "Whether the wallet has the private key for this xpub"},
670 [ + - + - ]: 28 : {RPCResult::Type::STR, "xprv", /*optional=*/true, "The extended private key if \"private\" is true"},
671 [ + - + - ]: 28 : {RPCResult::Type::ARR, "descriptors", "Array of descriptor objects that use this HD key",
672 : : {
673 [ + - + - ]: 28 : {RPCResult::Type::OBJ, "", "", {
674 [ + - + - ]: 28 : {RPCResult::Type::STR, "desc", "Descriptor string public representation"},
675 [ + - + - ]: 28 : {RPCResult::Type::BOOL, "active", "Whether this descriptor is currently used to generate new addresses"},
676 : : }},
677 : : }},
678 : : }},
679 : : }
680 [ + - + - : 294 : }},
+ - + - +
- + + + +
+ + + + -
- - - - -
- - ]
681 : 14 : RPCExamples{
682 [ + - + - : 28 : HelpExampleCli("gethdkeys", "") + HelpExampleRpc("gethdkeys", "")
+ - + - +
- + - +
- ]
683 [ + - + - : 140 : + HelpExampleCliNamed("gethdkeys", {{"active_only", "true"}, {"private", "true"}}) + HelpExampleRpcNamed("gethdkeys", {{"active_only", "true"}, {"private", "true"}})
+ - + - +
- + - + -
+ - + + +
+ - - -
- ]
684 [ + - ]: 14 : },
685 : 14 : [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
686 : : {
687 [ # # ]: 0 : const std::shared_ptr<const CWallet> wallet = GetWalletForJSONRPCRequest(request);
688 [ # # ]: 0 : if (!wallet) return UniValue::VNULL;
689 : :
690 [ # # ]: 0 : LOCK(wallet->cs_wallet);
691 : :
692 [ # # # # : 0 : UniValue options{request.params[0].isNull() ? UniValue::VOBJ : request.params[0]};
# # # # ]
693 [ # # # # : 0 : const bool active_only{options.exists("active_only") ? options["active_only"].get_bool() : false};
# # # # #
# ]
694 [ # # # # : 0 : const bool priv{options.exists("private") ? options["private"].get_bool() : false};
# # # # #
# ]
695 [ # # ]: 0 : if (priv) {
696 [ # # ]: 0 : EnsureWalletIsUnlocked(*wallet);
697 : : }
698 : :
699 [ # # ]: 0 : std::map<CExtPubKey, std::set<std::tuple<std::string, bool, bool>>> wallet_xpubs;
700 : 0 : std::map<CExtPubKey, CExtKey> wallet_xprvs;
701 [ # # # # : 0 : for (const auto& [xpub, spkms] : wallet->GetHDPubKeys(active_only ? HDKeyFilter::Active : HDKeyFilter::All)) {
# # ]
702 [ # # ]: 0 : for (auto* desc_spkm : spkms) {
703 [ # # ]: 0 : LOCK(desc_spkm->cs_desc_man);
704 [ # # ]: 0 : std::string desc_str;
705 [ # # ]: 0 : bool ok = desc_spkm->GetDescriptorString(desc_str, /*priv=*/false);
706 [ # # ]: 0 : CHECK_NONFATAL(ok);
707 [ # # # # : 0 : wallet_xpubs[xpub].emplace(desc_str, wallet->IsActiveScriptPubKeyMan(*desc_spkm), desc_spkm->HasPrivKey(xpub.pubkey.GetID()));
# # # # #
# ]
708 [ # # # # : 0 : if (std::optional<CKey> key = priv ? desc_spkm->GetKey(xpub.pubkey.GetID()) : std::nullopt) {
# # # # ]
709 [ # # # # ]: 0 : wallet_xprvs[xpub] = CExtKey(xpub, *key);
710 : 0 : }
711 [ # # ]: 0 : }
712 : 0 : }
713 : :
714 : 0 : UniValue response(UniValue::VARR);
715 [ # # ]: 0 : for (const auto& [xpub, descs] : wallet_xpubs) {
716 : 0 : bool has_xprv = false;
717 : 0 : UniValue descriptors(UniValue::VARR);
718 [ # # ]: 0 : for (const auto& [desc, active, has_priv] : descs) {
719 : 0 : UniValue d(UniValue::VOBJ);
720 [ # # # # : 0 : d.pushKV("desc", desc);
# # ]
721 [ # # # # : 0 : d.pushKV("active", active);
# # ]
722 : 0 : has_xprv |= has_priv;
723 : :
724 [ # # ]: 0 : descriptors.push_back(std::move(d));
725 : 0 : }
726 : 0 : UniValue xpub_info(UniValue::VOBJ);
727 [ # # # # : 0 : xpub_info.pushKV("xpub", EncodeExtPubKey(xpub));
# # # # ]
728 [ # # # # : 0 : xpub_info.pushKV("has_private", has_xprv);
# # ]
729 [ # # # # ]: 0 : if (priv && has_xprv) {
730 [ # # # # : 0 : xpub_info.pushKV("xprv", EncodeExtKey(wallet_xprvs.at(xpub)));
# # # # #
# ]
731 : : }
732 [ # # # # ]: 0 : xpub_info.pushKV("descriptors", std::move(descriptors));
733 : :
734 [ # # ]: 0 : response.push_back(std::move(xpub_info));
735 : 0 : }
736 : :
737 : 0 : return response;
738 [ # # ]: 0 : },
739 [ + - + - : 126 : };
+ - + + +
+ - - -
- ]
740 [ + - + - : 336 : }
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- - - - -
- - - - -
- ]
741 : :
742 : 14 : static RPCMethod createwalletdescriptor()
743 : : {
744 : 14 : return RPCMethod{"createwalletdescriptor",
745 : : "Creates the wallet's descriptor for the given address type. "
746 : : "The address type must be one that the wallet does not already have a descriptor for."
747 [ + - ]: 28 : + HELP_REQUIRING_PASSPHRASE,
748 : : {
749 [ + - + - : 28 : {"type", RPCArg::Type::STR, RPCArg::Optional::NO, "The address type the descriptor will produce. Options are " + FormatAllOutputTypes() + "."},
+ - ]
750 [ + - + - ]: 28 : {"options", RPCArg::Type::OBJ_NAMED_PARAMS, RPCArg::Optional::OMITTED, "", {
751 [ + - + - : 42 : {"internal", RPCArg::Type::BOOL, RPCArg::DefaultHint{"Both external and internal will be generated unless this parameter is specified"}, "Whether to only make one descriptor that is internal (if parameter is true) or external (if parameter is false)"},
+ - ]
752 [ + - + - : 42 : {"hdkey", RPCArg::Type::STR, RPCArg::DefaultHint{"The HD key used by all other active descriptors"}, "The HD key that the wallet knows the private key of, listed using 'gethdkeys', to use for this descriptor's key"},
+ - ]
753 : : }},
754 : : },
755 [ + - ]: 28 : RPCResult{
756 [ + - + - ]: 28 : RPCResult::Type::OBJ, "", "",
757 : : {
758 [ + - + - ]: 28 : {RPCResult::Type::ARR, "descs", "The public descriptors that were added to the wallet",
759 [ + - + - ]: 28 : {{RPCResult::Type::STR, "", ""}}
760 : : }
761 : : },
762 [ + - + - : 98 : },
+ - + + +
+ - - -
- ]
763 : 14 : RPCExamples{
764 [ + - + - : 28 : HelpExampleCli("createwalletdescriptor", "bech32m")
+ - ]
765 [ + - + - : 56 : + HelpExampleRpc("createwalletdescriptor", R"("bech32m")")
+ - + - ]
766 [ + - ]: 14 : },
767 : 14 : [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
768 : : {
769 : 0 : std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
770 [ # # ]: 0 : if (!pwallet) return UniValue::VNULL;
771 : :
772 [ # # # # : 0 : std::optional<OutputType> output_type = ParseOutputType(request.params[0].get_str());
# # # # ]
773 [ # # ]: 0 : if (!output_type) {
774 [ # # # # : 0 : throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Unknown address type '%s'", request.params[0].get_str()));
# # # # ]
775 : : }
776 : :
777 [ # # # # : 0 : UniValue options{request.params[1].isNull() ? UniValue::VOBJ : request.params[1]};
# # # # ]
778 [ # # # # : 0 : UniValue internal_only{options["internal"]};
# # ]
779 [ # # # # : 0 : UniValue hdkey{options["hdkey"]};
# # ]
780 : :
781 : 0 : std::vector<bool> internals;
782 [ # # ]: 0 : if (internal_only.isNull()) {
783 [ # # ]: 0 : internals.push_back(false);
784 [ # # ]: 0 : internals.push_back(true);
785 : : } else {
786 [ # # # # ]: 0 : internals.push_back(internal_only.get_bool());
787 : : }
788 : :
789 [ # # ]: 0 : LOCK(pwallet->cs_wallet);
790 [ # # ]: 0 : EnsureWalletIsUnlocked(*pwallet);
791 : :
792 [ # # ]: 0 : CExtPubKey xpub;
793 [ # # ]: 0 : if (hdkey.isNull()) {
794 [ # # ]: 0 : HDPubKeyMap active_xpubs = pwallet->GetHDPubKeys(HDKeyFilter::Active);
795 [ # # ]: 0 : if (active_xpubs.size() != 1) {
796 [ # # # # ]: 0 : throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Unable to determine which HD key to use from active descriptors. Please specify with 'hdkey'");
797 : : }
798 : 0 : xpub = active_xpubs.begin()->first;
799 : 0 : } else {
800 [ # # # # ]: 0 : xpub = DecodeExtPubKey(hdkey.get_str());
801 [ # # ]: 0 : if (!xpub.pubkey.IsValid()) {
802 [ # # # # ]: 0 : throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Unable to parse HD key. Please provide a valid xpub");
803 : : }
804 : : }
805 : :
806 [ # # # # ]: 0 : std::optional<CKey> key = pwallet->GetKey(xpub.pubkey.GetID());
807 [ # # ]: 0 : if (!key) {
808 [ # # # # : 0 : throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Private key for %s is not known", EncodeExtPubKey(xpub)));
# # ]
809 : : }
810 [ # # ]: 0 : CExtKey active_hdkey(xpub, *key);
811 : :
812 : 0 : std::vector<std::reference_wrapper<DescriptorScriptPubKeyMan>> spkms;
813 [ # # ]: 0 : WalletBatch batch{pwallet->GetDatabase()};
814 [ # # # # : 0 : for (bool internal : internals) {
# # ]
815 [ # # ]: 0 : WalletDescriptor w_desc = GenerateWalletDescriptor(xpub, *output_type, internal);
816 [ # # # # ]: 0 : if (!pwallet->GetDescriptorScriptPubKeyMan(w_desc)) {
817 [ # # # # ]: 0 : spkms.emplace_back(pwallet->SetupDescriptorScriptPubKeyMan(batch, active_hdkey, *output_type, internal));
818 : : }
819 : 0 : }
820 [ # # ]: 0 : if (spkms.empty()) {
821 [ # # # # ]: 0 : throw JSONRPCError(RPC_WALLET_ERROR, "Descriptor already exists");
822 : : }
823 : :
824 : : // Fetch each descspkm from the wallet in order to get the descriptor strings
825 : 0 : UniValue descs{UniValue::VARR};
826 [ # # ]: 0 : for (const auto& spkm : spkms) {
827 [ # # ]: 0 : std::string desc_str;
828 [ # # ]: 0 : bool ok = spkm.get().GetDescriptorString(desc_str, false);
829 [ # # ]: 0 : CHECK_NONFATAL(ok);
830 [ # # # # ]: 0 : descs.push_back(desc_str);
831 : 0 : }
832 : 0 : UniValue out{UniValue::VOBJ};
833 [ # # # # ]: 0 : out.pushKV("descs", std::move(descs));
834 : 0 : return out;
835 [ # # ]: 0 : }
836 [ + - + - : 140 : };
+ - + + +
+ - - -
- ]
837 [ + - + - : 168 : }
+ - + - +
- + - - -
- - ]
838 : :
839 : 14 : RPCMethod addhdkey()
840 : : {
841 : 14 : return RPCMethod{
842 : 14 : "addhdkey",
843 [ + - ]: 28 : "Add a BIP 32 HD key to the wallet that can be used with 'createwalletdescriptor'\n",
844 : : {
845 [ + - + - : 42 : {"hdkey", RPCArg::Type::STR, RPCArg::DefaultHint{"Automatically generated new key"}, "The BIP 32 extended private key to add. If none is provided, a randomly generated one will be added."},
+ - ]
846 : : },
847 [ + - ]: 28 : RPCResult{
848 [ + - + - ]: 28 : RPCResult::Type::OBJ, "", "",
849 : : {
850 [ + - + - ]: 28 : {RPCResult::Type::STR, "xpub", "The xpub of the HD key that was added to the wallet"}
851 : : },
852 [ + - + - : 56 : },
+ + - - ]
853 : 14 : RPCExamples{
854 [ + - + - : 28 : HelpExampleCli("addhdkey", "xprv") + HelpExampleRpc("addhdkey", R"("xprv")")
+ - + - +
- + - +
- ]
855 [ + - ]: 14 : },
856 : 14 : [&](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
857 : : {
858 : 0 : std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
859 [ # # ]: 0 : if (!wallet) return UniValue::VNULL;
860 : :
861 [ # # # # ]: 0 : if (wallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
862 [ # # # # ]: 0 : throw JSONRPCError(RPC_WALLET_ERROR, "addhdkey is not available for wallets without private keys");
863 : : }
864 : :
865 [ # # ]: 0 : EnsureWalletIsUnlocked(*wallet);
866 : :
867 : 0 : std::optional<CExtKey> hdkey;
868 [ # # # # ]: 0 : if (!request.params[0].isNull()) {
869 [ # # # # : 0 : hdkey = DecodeExtKey(request.params[0].get_str());
# # ]
870 [ # # ]: 0 : if (!hdkey->key.IsValid()) {
871 : : // Check if the user gave us an xpub and give a more descriptive error if so
872 [ # # # # : 0 : CExtPubKey xpub = DecodeExtPubKey(request.params[0].get_str());
# # ]
873 [ # # ]: 0 : if (xpub.pubkey.IsValid()) {
874 [ # # # # ]: 0 : throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Extended public key (xpub) provided, but extended private key (xprv) is required");
875 : : } else {
876 [ # # # # ]: 0 : throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Could not parse HD key");
877 : : }
878 : 0 : }
879 : : }
880 : :
881 [ # # ]: 0 : auto res = wallet->AddHDKey(hdkey);
882 [ # # ]: 0 : if (!res) {
883 [ # # ]: 0 : if (res.error().code == wallet::WalletErrorCode::UnlockNeeded) {
884 [ # # ]: 0 : throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, res.error().message.original);
885 : : }
886 [ # # ]: 0 : throw JSONRPCError(RPC_WALLET_ERROR, res.error().message.original);
887 : : }
888 : :
889 : 0 : UniValue response(UniValue::VOBJ);
890 [ # # # # : 0 : response.pushKV("xpub", EncodeExtPubKey(*res));
# # # # ]
891 : 0 : return response;
892 : 0 : },
893 [ + - + - : 70 : };
+ + - - ]
894 [ + - + - ]: 56 : }
895 : :
896 : 14 : static RPCMethod exportwatchonlywallet()
897 : : {
898 : 14 : return RPCMethod{"exportwatchonlywallet",
899 [ + - ]: 28 : "Creates a wallet file at the specified destination containing a watchonly version "
900 : : "of the current wallet. This watchonly wallet contains the wallet's public descriptors, "
901 : : "its transactions, and address book data. Descriptors that use hardened derivation will "
902 : : "only have a limited number of derived keys included in the export due to hardened "
903 : : "derivation requiring private keys. Descriptors with unhardened derivation do not have "
904 : : "this limitation. The watchonly wallet can be imported into another node using 'restorewallet'.",
905 : : {
906 [ + - + - ]: 28 : {"destination", RPCArg::Type::STR, RPCArg::Optional::NO, "The path to the filename the exported watchonly wallet will be saved to"},
907 : : },
908 [ + - ]: 28 : RPCResult{
909 [ + - + - ]: 28 : RPCResult::Type::OBJ, "", "",
910 : : {
911 [ + - + - ]: 28 : {RPCResult::Type::STR, "exported_file", "The full path that the file has been exported to"},
912 : : },
913 [ + - + - : 56 : },
+ + - - ]
914 : 14 : RPCExamples{
915 [ + - + - : 28 : HelpExampleCli("exportwatchonlywallet", "\"/path/to/export.dat\"")
+ - ]
916 [ + - + - : 56 : + HelpExampleRpc("exportwatchonlywallet", "\"/path/to/export.dat\"")
+ - + - ]
917 [ + - ]: 14 : },
918 : 14 : [&](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
919 : : {
920 : 0 : std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
921 [ # # ]: 0 : if (!pwallet) return UniValue::VNULL;
922 [ # # ]: 0 : WalletContext& context = EnsureWalletContext(request.context);
923 : :
924 [ # # # # : 0 : std::string dest = request.params[0].get_str();
# # ]
925 : :
926 [ # # ]: 0 : LOCK(pwallet->cs_wallet);
927 [ # # ]: 0 : pwallet->TopUpKeyPool();
928 [ # # # # ]: 0 : util::Result<std::string> exported = ExportWatchOnlyWallet(*pwallet, fs::PathFromString(dest), context);
929 [ # # ]: 0 : if (!exported) {
930 [ # # # # ]: 0 : throw JSONRPCError(RPC_WALLET_ERROR, util::ErrorString(exported).original);
931 : : }
932 : 0 : UniValue out{UniValue::VOBJ};
933 [ # # # # : 0 : out.pushKV("exported_file", *exported);
# # ]
934 : 0 : return out;
935 [ # # ]: 0 : }
936 [ + - + - : 70 : };
+ + - - ]
937 [ + - + - ]: 56 : }
938 : :
939 : 14 : RPCMethod derivehdkey()
940 : : {
941 : 14 : return RPCMethod{
942 : 14 : "derivehdkey",
943 : : "Derive extended public or private key from HD key in the wallet at a given path.\n"
944 : : "Derivation uses wallet private key material.\n"
945 [ + - ]: 28 : + HELP_REQUIRING_PASSPHRASE,
946 : : {
947 [ + - + - ]: 28 : {"path", RPCArg::Type::STR, RPCArg::Optional::NO, "BIP 32 derivation path with at least one hardened step."},
948 [ + - + - ]: 28 : {"options", RPCArg::Type::OBJ_NAMED_PARAMS, RPCArg::Optional::OMITTED, "", {
949 [ + - + - : 42 : {"private", RPCArg::Type::BOOL, RPCArg::Default{false}, "Show private key"},
+ - ]
950 [ + - + - : 42 : {"hdkey", RPCArg::Type::STR, RPCArg::DefaultHint{"Either the HD key of an unused(KEY) descriptor, or any other active descriptor."}, "The HD key that the wallet knows the private key of, listed using 'gethdkeys', to use for derivation"},
+ - ]
951 : : }},
952 : : },
953 [ + - ]: 28 : RPCResult{
954 [ + - + - ]: 28 : RPCResult::Type::OBJ, "", "", {
955 [ + - + - ]: 28 : {RPCResult::Type::STR, "origin", "Fingerprint and path for use in descriptors"},
956 [ + - + - ]: 28 : {RPCResult::Type::STR, "xpub", "The extended public key"},
957 [ + - + - ]: 28 : {RPCResult::Type::STR, "xprv", /*optional=*/true, "The extended private key if \"private\" is true"},
958 : : },
959 [ + - + - : 112 : },
+ + - - ]
960 : 14 : RPCExamples{
961 [ + - + - : 28 : HelpExampleCli("derivehdkey", "m/87h/0h/0h") + HelpExampleRpc("derivehdkey", "\"m/87h/0h/0h\"")
+ - + - +
- + - +
- ]
962 [ + - + - : 84 : + HelpExampleCliNamed("derivehdkey", {{"path", "m/87h/0h/0h"}, {"private", "true"}})
+ - + - +
+ - - ]
963 [ + - + - : 84 : + HelpExampleRpcNamed("derivehdkey", {{"path", "m/87h/0h/0h"}, {"private", "true"}})
+ - + - +
+ - - ]
964 [ + - ]: 14 : },
965 : 14 : [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
966 : : {
967 [ # # ]: 0 : const std::shared_ptr<const CWallet> wallet = GetWalletForJSONRPCRequest(request);
968 [ # # ]: 0 : if (!wallet) return UniValue::VNULL;
969 : :
970 [ # # # # ]: 0 : if (wallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
971 : : // Watch-only wallets can't contain unused(KEY) descriptors
972 [ # # # # ]: 0 : throw JSONRPCError(RPC_WALLET_ERROR, "derivehdkey is not available for watch-only wallets");
973 : : }
974 : :
975 [ # # # # : 0 : std::vector<uint32_t> path = ParsePathBIP32(request.params[0].get_str());
# # ]
976 [ # # # # : 0 : UniValue options{request.params[1].isNull() ? UniValue::VOBJ : request.params[1]};
# # # # ]
977 [ # # # # : 0 : const bool priv{options.exists("private") ? options["private"].get_bool() : false};
# # # # #
# ]
978 [ # # # # : 0 : UniValue hdkey{options["hdkey"]};
# # ]
979 [ # # # # : 0 : if (!HasHardenedDerivation(path)) {
# # ]
980 [ # # # # ]: 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, "Derivation path requires at least one hardened step");
981 : : }
982 : :
983 [ # # ]: 0 : LOCK(wallet->cs_wallet);
984 : :
985 : : // The RPC requires a hardened derivation step, so always unlock
986 : : // the wallet.
987 [ # # ]: 0 : EnsureWalletIsUnlocked(*wallet);
988 : :
989 [ # # ]: 0 : CExtPubKey xpub;
990 [ # # ]: 0 : if (!hdkey.isNull()) {
991 [ # # # # ]: 0 : xpub = DecodeExtPubKey(hdkey.get_str());
992 [ # # ]: 0 : if (!xpub.pubkey.IsValid()) {
993 [ # # # # ]: 0 : throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Unable to parse HD key. Please provide a valid xpub");
994 : : }
995 : :
996 : : // Accept an xpub from an active or unused(KEY) descriptor, but
997 : : // not from a (used) inactive one.
998 [ # # ]: 0 : std::set<CExtPubKey> xpub_candidates;
999 [ # # # # ]: 0 : for (const auto& candidate : wallet->GetHDPubKeys(HDKeyFilter::UnusedKey)) {
1000 [ # # ]: 0 : xpub_candidates.insert(candidate.first);
1001 : 0 : }
1002 [ # # # # ]: 0 : for (const auto& candidate : wallet->GetHDPubKeys(HDKeyFilter::Active)) {
1003 [ # # ]: 0 : xpub_candidates.insert(candidate.first);
1004 : 0 : }
1005 [ # # ]: 0 : if (!xpub_candidates.contains(xpub)) {
1006 [ # # # # ]: 0 : throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "HD key is not used by an active or unused(KEY) descriptor");
1007 : : }
1008 : 0 : }
1009 : :
1010 : : // If hdkey was not specified, try to look it up. First consider
1011 : : // unused(KEY) descriptors. Otherwise look for active descriptors.
1012 [ # # ]: 0 : if (hdkey.isNull()) {
1013 [ # # ]: 0 : HDPubKeyMap wallet_xpubs{wallet->GetHDPubKeys(HDKeyFilter::UnusedKey)};
1014 : :
1015 [ # # ]: 0 : if (wallet_xpubs.size() > 1) {
1016 [ # # # # ]: 0 : throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Unable to determine which HD key to use. Please specify with 'hdkey'");
1017 [ # # ]: 0 : } else if (wallet_xpubs.size() == 1) {
1018 : 0 : xpub = wallet_xpubs.begin()->first;
1019 : : } else {
1020 [ # # ]: 0 : HDPubKeyMap active_xpubs = wallet->GetHDPubKeys(HDKeyFilter::Active);
1021 [ # # ]: 0 : if (active_xpubs.empty()) {
1022 [ # # # # ]: 0 : throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No active or unused(KEY) descriptor found");
1023 : : }
1024 : :
1025 [ # # ]: 0 : if (active_xpubs.size() > 1) {
1026 [ # # # # ]: 0 : throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Unable to determine which HD key to use from active descriptors. Please specify with 'hdkey'");
1027 : : }
1028 : :
1029 : 0 : xpub = active_xpubs.begin()->first;
1030 : 0 : }
1031 : 0 : }
1032 : :
1033 [ # # ]: 0 : std::optional<CExtKey> xprv{wallet->GetExtKey(xpub)};
1034 [ # # ]: 0 : if (!xprv) {
1035 [ # # # # : 0 : throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Private key for %s is not known", EncodeExtPubKey(xpub)));
# # ]
1036 : : }
1037 : :
1038 [ # # ]: 0 : std::optional<std::pair<CExtKey, KeyOriginInfo>> child{DeriveExtKey(*xprv, path)};
1039 [ # # ]: 0 : if (!child) {
1040 [ # # # # ]: 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, "Unable to derive HD key at the requested path");
1041 : : }
1042 : :
1043 : 0 : UniValue res{UniValue::VOBJ};
1044 : :
1045 [ # # ]: 0 : const std::string fingerprint{HexStr(child->second.fingerprint)};
1046 : :
1047 [ # # # # : 0 : res.pushKV("origin", strprintf("[%s%s]", fingerprint, FormatHDKeypath(child->second.path)));
# # # # #
# ]
1048 [ # # # # : 0 : res.pushKV("xpub", EncodeExtPubKey(child->first.Neuter()));
# # # # #
# ]
1049 [ # # ]: 0 : if (priv) {
1050 [ # # # # : 0 : res.pushKV("xprv", EncodeExtKey(child->first));
# # # # ]
1051 : : }
1052 : 0 : return res;
1053 [ # # # # ]: 0 : },
1054 [ + - + - : 140 : };
+ - + + +
+ - - -
- ]
1055 [ + - + - : 224 : }
+ - + - +
- + - + -
+ - + - +
- + - - -
- - - - -
- - - ]
1056 : :
1057 : : // addresses
1058 : : RPCMethod getaddressinfo();
1059 : : RPCMethod getnewaddress();
1060 : : RPCMethod getrawchangeaddress();
1061 : : RPCMethod setlabel();
1062 : : RPCMethod listaddressgroupings();
1063 : : RPCMethod keypoolrefill();
1064 : : RPCMethod getaddressesbylabel();
1065 : : RPCMethod listlabels();
1066 : : #ifdef ENABLE_EXTERNAL_SIGNER
1067 : : RPCMethod walletdisplayaddress();
1068 : : #endif // ENABLE_EXTERNAL_SIGNER
1069 : :
1070 : : // backup
1071 : : RPCMethod importprunedfunds();
1072 : : RPCMethod removeprunedfunds();
1073 : : RPCMethod importdescriptors();
1074 : : RPCMethod listdescriptors();
1075 : : RPCMethod backupwallet();
1076 : : RPCMethod restorewallet();
1077 : :
1078 : : // coins
1079 : : RPCMethod getreceivedbyaddress();
1080 : : RPCMethod getreceivedbylabel();
1081 : : RPCMethod getbalance();
1082 : : RPCMethod lockunspent();
1083 : : RPCMethod listlockunspent();
1084 : : RPCMethod getbalances();
1085 : : RPCMethod listunspent();
1086 : :
1087 : : // encryption
1088 : : RPCMethod walletpassphrase();
1089 : : RPCMethod walletpassphrasechange();
1090 : : RPCMethod walletlock();
1091 : : RPCMethod encryptwallet();
1092 : :
1093 : : // spend
1094 : : RPCMethod sendtoaddress();
1095 : : RPCMethod sendmany();
1096 : : RPCMethod fundrawtransaction();
1097 : : RPCMethod bumpfee();
1098 : : RPCMethod psbtbumpfee();
1099 : : RPCMethod send();
1100 : : RPCMethod sendall();
1101 : : RPCMethod walletprocesspsbt();
1102 : : RPCMethod walletcreatefundedpsbt();
1103 : : RPCMethod signrawtransactionwithwallet();
1104 : :
1105 : : // signmessage
1106 : : RPCMethod signmessage();
1107 : :
1108 : : // transactions
1109 : : RPCMethod listreceivedbyaddress();
1110 : : RPCMethod listreceivedbylabel();
1111 : : RPCMethod listtransactions();
1112 : : RPCMethod listrawtransactions();
1113 : : RPCMethod listsinceblock();
1114 : : RPCMethod gettransaction();
1115 : : RPCMethod abandontransaction();
1116 : : RPCMethod rescanblockchain();
1117 : : RPCMethod abortrescan();
1118 : :
1119 : 19 : std::span<const CRPCCommand> GetWalletRPCCommands()
1120 : : {
1121 : 19 : static const CRPCCommand commands[]{
1122 [ + - ]: 14 : {"rawtransactions", &fundrawtransaction},
1123 [ + - ]: 14 : {"wallet", &abandontransaction},
1124 [ + - ]: 14 : {"wallet", &abortrescan},
1125 [ + - ]: 14 : {"wallet", &addhdkey},
1126 [ + - ]: 14 : {"wallet", &backupwallet},
1127 [ + - ]: 14 : {"wallet", &bumpfee},
1128 [ + - ]: 14 : {"wallet", &psbtbumpfee},
1129 [ + - ]: 14 : {"wallet", &createwallet},
1130 [ + - ]: 14 : {"wallet", &createwalletdescriptor},
1131 [ + - ]: 14 : {"wallet", &derivehdkey},
1132 [ + - ]: 14 : {"wallet", &restorewallet},
1133 [ + - ]: 14 : {"wallet", &encryptwallet},
1134 [ + - ]: 14 : {"wallet", &exportwatchonlywallet},
1135 [ + - ]: 14 : {"wallet", &getaddressesbylabel},
1136 [ + - ]: 14 : {"wallet", &getaddressinfo},
1137 [ + - ]: 14 : {"wallet", &getbalance},
1138 [ + - ]: 14 : {"wallet", &gethdkeys},
1139 [ + - ]: 14 : {"wallet", &getnewaddress},
1140 [ + - ]: 14 : {"wallet", &getrawchangeaddress},
1141 [ + - ]: 14 : {"wallet", &getreceivedbyaddress},
1142 [ + - ]: 14 : {"wallet", &getreceivedbylabel},
1143 [ + - ]: 14 : {"wallet", &gettransaction},
1144 [ + - ]: 14 : {"wallet", &getbalances},
1145 [ + - ]: 14 : {"wallet", &getwalletinfo},
1146 [ + - ]: 14 : {"wallet", &importdescriptors},
1147 [ + - ]: 14 : {"wallet", &importprunedfunds},
1148 [ + - ]: 14 : {"wallet", &keypoolrefill},
1149 [ + - ]: 14 : {"wallet", &listaddressgroupings},
1150 [ + - ]: 14 : {"wallet", &listdescriptors},
1151 [ + - ]: 14 : {"wallet", &listlabels},
1152 [ + - ]: 14 : {"wallet", &listlockunspent},
1153 [ + - ]: 14 : {"wallet", &listreceivedbyaddress},
1154 [ + - ]: 14 : {"wallet", &listreceivedbylabel},
1155 [ + - ]: 14 : {"wallet", &listsinceblock},
1156 [ + - ]: 14 : {"wallet", &listtransactions},
1157 [ + - ]: 14 : {"wallet", &listrawtransactions},
1158 [ + - ]: 14 : {"wallet", &listunspent},
1159 [ + - ]: 14 : {"wallet", &listwalletdir},
1160 [ + - ]: 14 : {"wallet", &listwallets},
1161 [ + - ]: 14 : {"wallet", &loadwallet},
1162 [ + - ]: 14 : {"wallet", &lockunspent},
1163 [ + - ]: 14 : {"wallet", &migratewallet},
1164 [ + - ]: 14 : {"wallet", &removeprunedfunds},
1165 [ + - ]: 14 : {"wallet", &rescanblockchain},
1166 [ + - ]: 14 : {"wallet", &send},
1167 [ + - ]: 14 : {"wallet", &sendmany},
1168 [ + - ]: 14 : {"wallet", &sendtoaddress},
1169 [ + - ]: 14 : {"wallet", &setlabel},
1170 [ + - ]: 14 : {"wallet", &setwalletflag},
1171 [ + - ]: 14 : {"wallet", &signmessage},
1172 [ + - ]: 14 : {"wallet", &signrawtransactionwithwallet},
1173 [ + - ]: 14 : {"wallet", &simulaterawtransaction},
1174 [ + - ]: 14 : {"wallet", &sendall},
1175 [ + - ]: 14 : {"wallet", &unloadwallet},
1176 [ + - ]: 14 : {"wallet", &walletcreatefundedpsbt},
1177 : : #ifdef ENABLE_EXTERNAL_SIGNER
1178 [ + - ]: 14 : {"wallet", &walletdisplayaddress},
1179 : : #endif // ENABLE_EXTERNAL_SIGNER
1180 [ + - ]: 14 : {"wallet", &walletlock},
1181 [ + - ]: 14 : {"wallet", &walletpassphrase},
1182 [ + - ]: 14 : {"wallet", &walletpassphrasechange},
1183 [ + - ]: 14 : {"wallet", &walletprocesspsbt},
1184 [ + + + - : 439 : };
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
- - ]
1185 : 19 : return commands;
1186 : : }
1187 : : } // namespace wallet
|