Coverage Report

Created: 2026-04-29 19:21

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/tmp/bitcoin/src/wallet/rpc/wallet.cpp
Line
Count
Source
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 <core_io.h>
9
#include <key_io.h>
10
#include <rpc/server.h>
11
#include <rpc/util.h>
12
#include <univalue.h>
13
#include <util/translation.h>
14
#include <wallet/context.h>
15
#include <wallet/receive.h>
16
#include <wallet/rpc/util.h>
17
#include <wallet/rpc/wallet.h>
18
#include <wallet/wallet.h>
19
#include <wallet/walletutil.h>
20
21
#include <optional>
22
#include <string_view>
23
24
25
namespace wallet {
26
27
static const std::map<uint64_t, std::string> WALLET_FLAG_CAVEATS{
28
    {WALLET_FLAG_AVOID_REUSE,
29
     "You need to rescan the blockchain in order to correctly mark used "
30
     "destinations in the past. Until this is done, some destinations may "
31
     "be considered unused, even if the opposite is the case."},
32
};
33
34
static RPCMethod getwalletinfo()
35
1.26k
{
36
1.26k
    return RPCMethod{"getwalletinfo",
37
1.26k
                "Returns an object containing various wallet state info.\n",
38
1.26k
                {},
39
1.26k
                RPCResult{
40
1.26k
                    RPCResult::Type::OBJ, "", "",
41
1.26k
                    {
42
1.26k
                        {
43
1.26k
                        {RPCResult::Type::STR, "walletname", "the wallet name"},
44
1.26k
                        {RPCResult::Type::NUM, "walletversion", "(DEPRECATED) only related to unsupported legacy wallet, returns the latest version 169900 for backwards compatibility"},
45
1.26k
                        {RPCResult::Type::STR, "format", "the database format (only sqlite)"},
46
1.26k
                        {RPCResult::Type::NUM, "txcount", "the total number of transactions in the wallet"},
47
1.26k
                        {RPCResult::Type::NUM, "keypoolsize", "how many new keys are pre-generated (only counts external keys)"},
48
1.26k
                        {RPCResult::Type::NUM, "keypoolsize_hd_internal", /*optional=*/true, "how many new keys are pre-generated for internal use (used for change outputs, only appears if the wallet is using this feature, otherwise external keys are used)"},
49
1.26k
                        {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)"},
50
1.26k
                        {RPCResult::Type::BOOL, "private_keys_enabled", "false if privatekeys are disabled for this wallet (enforced watch-only wallet)"},
51
1.26k
                        {RPCResult::Type::BOOL, "avoid_reuse", "whether this wallet tracks clean/dirty coins in terms of reuse"},
52
1.26k
                        {RPCResult::Type::OBJ, "scanning", "current scanning details, or false if no scan is in progress",
53
1.26k
                        {
54
1.26k
                            {RPCResult::Type::NUM, "duration", "elapsed seconds since scan start"},
55
1.26k
                            {RPCResult::Type::NUM, "progress", "scanning progress percentage [0.0, 1.0]"},
56
1.26k
                        }, {.skip_type_check=true}, },
57
1.26k
                        {RPCResult::Type::BOOL, "descriptors", "whether this wallet uses descriptors for output script management"},
58
1.26k
                        {RPCResult::Type::BOOL, "external_signer", "whether this wallet is configured to use an external signer such as a hardware wallet"},
59
1.26k
                        {RPCResult::Type::BOOL, "blank", "Whether this wallet intentionally does not contain any keys, scripts, or descriptors"},
60
1.26k
                        {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."},
61
1.26k
                        {RPCResult::Type::ARR, "flags", "The flags currently set on the wallet",
62
1.26k
                        {
63
1.26k
                            {RPCResult::Type::STR, "flag", "The name of the flag"},
64
1.26k
                        }},
65
1.26k
                        RESULT_LAST_PROCESSED_BLOCK,
66
1.26k
                    }},
67
1.26k
                },
68
1.26k
                RPCExamples{
69
1.26k
                    HelpExampleCli("getwalletinfo", "")
70
1.26k
            + HelpExampleRpc("getwalletinfo", "")
71
1.26k
                },
72
1.26k
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
73
1.26k
{
74
450
    const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
75
450
    if (!pwallet) return UniValue::VNULL;
76
77
    // Make sure the results are valid at least up to the most recent block
78
    // the user could have gotten from another RPC command prior to now
79
450
    pwallet->BlockUntilSyncedToCurrentChain();
80
81
450
    LOCK(pwallet->cs_wallet);
82
83
450
    UniValue obj(UniValue::VOBJ);
84
85
450
    const int latest_legacy_wallet_minversion{169900};
86
87
450
    size_t kpExternalSize = pwallet->KeypoolCountExternalKeys();
88
450
    obj.pushKV("walletname", pwallet->GetName());
89
450
    obj.pushKV("walletversion", latest_legacy_wallet_minversion);
90
450
    obj.pushKV("format", pwallet->GetDatabase().Format());
91
450
    obj.pushKV("txcount", pwallet->mapWallet.size());
92
450
    obj.pushKV("keypoolsize", kpExternalSize);
93
450
    obj.pushKV("keypoolsize_hd_internal", pwallet->GetKeyPoolSize() - kpExternalSize);
94
95
450
    if (pwallet->HasEncryptionKeys()) {
96
42
        obj.pushKV("unlocked_until", pwallet->nRelockTime);
97
42
    }
98
450
    obj.pushKV("private_keys_enabled", !pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS));
99
450
    obj.pushKV("avoid_reuse", pwallet->IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE));
100
450
    if (pwallet->IsScanning()) {
101
0
        UniValue scanning(UniValue::VOBJ);
102
0
        scanning.pushKV("duration", Ticks<std::chrono::seconds>(pwallet->ScanningDuration()));
103
0
        scanning.pushKV("progress", pwallet->ScanningProgress());
104
0
        obj.pushKV("scanning", std::move(scanning));
105
450
    } else {
106
450
        obj.pushKV("scanning", false);
107
450
    }
108
450
    obj.pushKV("descriptors", pwallet->IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS));
109
450
    obj.pushKV("external_signer", pwallet->IsWalletFlagSet(WALLET_FLAG_EXTERNAL_SIGNER));
110
450
    obj.pushKV("blank", pwallet->IsWalletFlagSet(WALLET_FLAG_BLANK_WALLET));
111
450
    if (int64_t birthtime = pwallet->GetBirthTime(); birthtime != UNKNOWN_TIME) {
112
394
        obj.pushKV("birthtime", birthtime);
113
394
    }
114
115
    // Push known flags
116
450
    UniValue flags(UniValue::VARR);
117
450
    uint64_t wallet_flags = pwallet->GetWalletFlags();
118
28.1k
    for (uint64_t i = 0; i < 64; ++i) {
119
27.7k
        uint64_t flag = uint64_t{1} << i;
120
27.7k
        if (flag & wallet_flags) {
121
1.09k
            if (flag & KNOWN_WALLET_FLAGS) {
122
1.09k
                flags.push_back(WALLET_FLAG_TO_STRING.at(WalletFlags{flag}));
123
1.09k
            } else {
124
0
                flags.push_back(strprintf("unknown_flag_%u", i));
125
0
            }
126
1.09k
        }
127
27.7k
    }
128
450
    obj.pushKV("flags", flags);
129
130
450
    AppendLastProcessedBlock(obj, *pwallet);
131
450
    return obj;
132
450
},
133
1.26k
    };
134
1.26k
}
135
136
static RPCMethod listwalletdir()
137
888
{
138
888
    return RPCMethod{"listwalletdir",
139
888
                "Returns a list of wallets in the wallet directory.\n",
140
888
                {},
141
888
                RPCResult{
142
888
                    RPCResult::Type::OBJ, "", "",
143
888
                    {
144
888
                        {RPCResult::Type::ARR, "wallets", "",
145
888
                        {
146
888
                            {RPCResult::Type::OBJ, "", "",
147
888
                            {
148
888
                                {RPCResult::Type::STR, "name", "The wallet name"},
149
888
                                {RPCResult::Type::ARR, "warnings", /*optional=*/true, "Warning messages, if any, related to loading the wallet.",
150
888
                                {
151
888
                                    {RPCResult::Type::STR, "", ""},
152
888
                                }},
153
888
                            }},
154
888
                        }},
155
888
                    }
156
888
                },
157
888
                RPCExamples{
158
888
                    HelpExampleCli("listwalletdir", "")
159
888
            + HelpExampleRpc("listwalletdir", "")
160
888
                },
161
888
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
162
888
{
163
77
    UniValue wallets(UniValue::VARR);
164
1.60k
    for (const auto& [path, db_type] : ListDatabases(GetWalletDir())) {
165
1.60k
        UniValue wallet(UniValue::VOBJ);
166
1.60k
        wallet.pushKV("name", path.utf8string());
167
1.60k
                UniValue warnings(UniValue::VARR);
168
1.60k
        if (db_type == "bdb") {
169
82
            warnings.push_back("This wallet is a legacy wallet and will need to be migrated with migratewallet before it can be loaded");
170
82
        }
171
1.60k
        wallet.pushKV("warnings", warnings);
172
1.60k
        wallets.push_back(std::move(wallet));
173
1.60k
    }
174
175
77
    UniValue result(UniValue::VOBJ);
176
77
    result.pushKV("wallets", std::move(wallets));
177
77
    return result;
178
77
},
179
888
    };
180
888
}
181
182
static RPCMethod listwallets()
183
889
{
184
889
    return RPCMethod{"listwallets",
185
889
                "Returns a list of currently loaded wallets.\n"
186
889
                "For full information on the wallet, use \"getwalletinfo\"\n",
187
889
                {},
188
889
                RPCResult{
189
889
                    RPCResult::Type::ARR, "", "",
190
889
                    {
191
889
                        {RPCResult::Type::STR, "walletname", "the wallet name"},
192
889
                    }
193
889
                },
194
889
                RPCExamples{
195
889
                    HelpExampleCli("listwallets", "")
196
889
            + HelpExampleRpc("listwallets", "")
197
889
                },
198
889
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
199
889
{
200
78
    UniValue obj(UniValue::VARR);
201
202
78
    WalletContext& context = EnsureWalletContext(request.context);
203
616
    for (const std::shared_ptr<CWallet>& wallet : GetWallets(context)) {
204
616
        LOCK(wallet->cs_wallet);
205
616
        obj.push_back(wallet->GetName());
206
616
    }
207
208
78
    return obj;
209
78
},
210
889
    };
211
889
}
212
213
static RPCMethod loadwallet()
214
966
{
215
966
    return RPCMethod{
216
966
        "loadwallet",
217
966
        "Loads a wallet from a wallet file or directory."
218
966
                "\nNote that all wallet command-line options used when starting bitcoind will be"
219
966
                "\napplied to the new wallet.\n",
220
966
                {
221
966
                    {"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."},
222
966
                    {"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."},
223
966
                },
224
966
                RPCResult{
225
966
                    RPCResult::Type::OBJ, "", "",
226
966
                    {
227
966
                        {RPCResult::Type::STR, "name", "The wallet name if loaded successfully."},
228
966
                        {RPCResult::Type::ARR, "warnings", /*optional=*/true, "Warning messages, if any, related to loading the wallet.",
229
966
                        {
230
966
                            {RPCResult::Type::STR, "", ""},
231
966
                        }},
232
966
                    }
233
966
                },
234
966
                RPCExamples{
235
966
                    "\nLoad wallet from the wallet dir:\n"
236
966
                    + HelpExampleCli("loadwallet", "\"walletname\"")
237
966
                    + HelpExampleRpc("loadwallet", "\"walletname\"")
238
966
                    + "\nLoad wallet using absolute path (Unix):\n"
239
966
                    + HelpExampleCli("loadwallet", "\"/path/to/walletname/\"")
240
966
                    + HelpExampleRpc("loadwallet", "\"/path/to/walletname/\"")
241
966
                    + "\nLoad wallet using absolute path (Windows):\n"
242
966
                    + HelpExampleCli("loadwallet", "\"DriveLetter:\\path\\to\\walletname\\\"")
243
966
                    + HelpExampleRpc("loadwallet", "\"DriveLetter:\\path\\to\\walletname\\\"")
244
966
                },
245
966
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
246
966
{
247
155
    WalletContext& context = EnsureWalletContext(request.context);
248
155
    const std::string name(request.params[0].get_str());
249
250
155
    DatabaseOptions options;
251
155
    DatabaseStatus status;
252
155
    ReadDatabaseArgs(*context.args, options);
253
155
    options.require_existing = true;
254
155
    bilingual_str error;
255
155
    std::vector<bilingual_str> warnings;
256
155
    std::optional<bool> load_on_start = request.params[1].isNull() ? std::nullopt : std::optional<bool>(request.params[1].get_bool());
257
258
155
    {
259
155
        LOCK(context.wallets_mutex);
260
637
        if (std::any_of(context.wallets.begin(), context.wallets.end(), [&name](const auto& wallet) { return wallet->GetName() == name; })) {
261
3
            throw JSONRPCError(RPC_WALLET_ALREADY_LOADED, "Wallet \"" + name + "\" is already loaded.");
262
3
        }
263
155
    }
264
265
152
    std::shared_ptr<CWallet> const wallet = LoadWallet(context, name, load_on_start, options, status, error, warnings);
266
267
152
    HandleWalletError(wallet, status, error);
268
269
152
    UniValue obj(UniValue::VOBJ);
270
152
    obj.pushKV("name", wallet->GetName());
271
152
    PushWarnings(warnings, obj);
272
273
152
    return obj;
274
155
},
275
966
    };
276
966
}
277
278
static RPCMethod setwalletflag()
279
819
{
280
819
            std::string flags;
281
819
            for (auto& it : STRING_TO_WALLET_FLAG)
282
5.73k
                if (it.second & MUTABLE_WALLET_FLAGS)
283
819
                    flags += (flags == "" ? "" : ", ") + it.first;
284
285
819
    return RPCMethod{
286
819
        "setwalletflag",
287
819
        "Change the state of the given wallet flag for a wallet.\n",
288
819
                {
289
819
                    {"flag", RPCArg::Type::STR, RPCArg::Optional::NO, "The name of the flag to change. Current available flags: " + flags},
290
819
                    {"value", RPCArg::Type::BOOL, RPCArg::Default{true}, "The new state."},
291
819
                },
292
819
                RPCResult{
293
819
                    RPCResult::Type::OBJ, "", "",
294
819
                    {
295
819
                        {RPCResult::Type::STR, "flag_name", "The name of the flag that was modified"},
296
819
                        {RPCResult::Type::BOOL, "flag_state", "The new state of the flag"},
297
819
                        {RPCResult::Type::STR, "warnings", /*optional=*/true, "Any warnings associated with the change"},
298
819
                    }
299
819
                },
300
819
                RPCExamples{
301
819
                    HelpExampleCli("setwalletflag", "avoid_reuse")
302
819
                  + HelpExampleRpc("setwalletflag", "\"avoid_reuse\"")
303
819
                },
304
819
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
305
819
{
306
8
    std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
307
8
    if (!pwallet) return UniValue::VNULL;
308
309
8
    std::string flag_str = request.params[0].get_str();
310
8
    bool value = request.params[1].isNull() || request.params[1].get_bool();
311
312
8
    if (!STRING_TO_WALLET_FLAG.contains(flag_str)) {
313
1
        throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Unknown wallet flag: %s", flag_str));
314
1
    }
315
316
7
    auto flag = STRING_TO_WALLET_FLAG.at(flag_str);
317
318
7
    if (!(flag & MUTABLE_WALLET_FLAGS)) {
319
3
        throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Wallet flag is immutable: %s", flag_str));
320
3
    }
321
322
4
    UniValue res(UniValue::VOBJ);
323
324
4
    if (pwallet->IsWalletFlagSet(flag) == value) {
325
2
        throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Wallet flag is already set to %s: %s", value ? "true" : "false", flag_str));
326
2
    }
327
328
2
    res.pushKV("flag_name", flag_str);
329
2
    res.pushKV("flag_state", value);
330
331
2
    if (value) {
332
1
        pwallet->SetWalletFlag(flag);
333
1
    } else {
334
1
        pwallet->UnsetWalletFlag(flag);
335
1
    }
336
337
2
    if (flag && value && WALLET_FLAG_CAVEATS.contains(flag)) {
338
1
        res.pushKV("warnings", WALLET_FLAG_CAVEATS.at(flag));
339
1
    }
340
341
2
    return res;
342
4
},
343
819
    };
344
819
}
345
346
static RPCMethod createwallet()
347
1.40k
{
348
1.40k
    return RPCMethod{
349
1.40k
        "createwallet",
350
1.40k
        "Creates and loads a new wallet.\n",
351
1.40k
        {
352
1.40k
            {"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."},
353
1.40k
            {"disable_private_keys", RPCArg::Type::BOOL, RPCArg::Default{false}, "Disable the possibility of private keys (only watchonlys are possible in this mode)."},
354
1.40k
            {"blank", RPCArg::Type::BOOL, RPCArg::Default{false}, "Create a blank wallet. A blank wallet has no keys."},
355
1.40k
            {"passphrase", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "Encrypt the wallet with this passphrase."},
356
1.40k
            {"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."},
357
1.40k
            {"descriptors", RPCArg::Type::BOOL, RPCArg::Default{true}, "If set, must be \"true\""},
358
1.40k
            {"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."},
359
1.40k
            {"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."},
360
1.40k
        },
361
1.40k
        RPCResult{
362
1.40k
            RPCResult::Type::OBJ, "", "",
363
1.40k
            {
364
1.40k
                {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."},
365
1.40k
                {RPCResult::Type::ARR, "warnings", /*optional=*/true, "Warning messages, if any, related to creating and loading the wallet.",
366
1.40k
                {
367
1.40k
                    {RPCResult::Type::STR, "", ""},
368
1.40k
                }},
369
1.40k
            }
370
1.40k
        },
371
1.40k
        RPCExamples{
372
1.40k
            HelpExampleCli("createwallet", "\"testwallet\"")
373
1.40k
            + HelpExampleRpc("createwallet", "\"testwallet\"")
374
1.40k
            + HelpExampleCliNamed("createwallet", {{"wallet_name", "descriptors"}, {"avoid_reuse", true}, {"load_on_startup", true}})
375
1.40k
            + HelpExampleRpcNamed("createwallet", {{"wallet_name", "descriptors"}, {"avoid_reuse", true}, {"load_on_startup", true}})
376
1.40k
        },
377
1.40k
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
378
1.40k
{
379
595
    WalletContext& context = EnsureWalletContext(request.context);
380
595
    uint64_t flags = 0;
381
595
    if (!request.params[1].isNull() && request.params[1].get_bool()) {
382
108
        flags |= WALLET_FLAG_DISABLE_PRIVATE_KEYS;
383
108
    }
384
385
595
    if (!request.params[2].isNull() && request.params[2].get_bool()) {
386
163
        flags |= WALLET_FLAG_BLANK_WALLET;
387
163
    }
388
595
    SecureString passphrase;
389
595
    passphrase.reserve(100);
390
595
    std::vector<bilingual_str> warnings;
391
595
    if (!request.params[3].isNull()) {
392
16
        passphrase = std::string_view{request.params[3].get_str()};
393
16
        if (passphrase.empty()) {
394
            // Empty string means unencrypted
395
4
            warnings.emplace_back(Untranslated("Empty string given as passphrase, wallet will not be encrypted."));
396
4
        }
397
16
    }
398
399
595
    if (!request.params[4].isNull() && request.params[4].get_bool()) {
400
3
        flags |= WALLET_FLAG_AVOID_REUSE;
401
3
    }
402
595
    flags |= WALLET_FLAG_DESCRIPTORS;
403
595
    if (!self.Arg<bool>("descriptors")) {
404
2
        throw JSONRPCError(RPC_WALLET_ERROR, "descriptors argument must be set to \"true\"; it is no longer possible to create a legacy wallet.");
405
2
    }
406
593
    if (!request.params[7].isNull() && request.params[7].get_bool()) {
407
6
#ifdef ENABLE_EXTERNAL_SIGNER
408
6
        flags |= WALLET_FLAG_EXTERNAL_SIGNER;
409
#else
410
        throw JSONRPCError(RPC_WALLET_ERROR, "Compiled without external signing support (required for external signing)");
411
#endif
412
6
    }
413
414
593
    DatabaseOptions options;
415
593
    DatabaseStatus status;
416
593
    ReadDatabaseArgs(*context.args, options);
417
593
    options.require_create = true;
418
593
    options.create_flags = flags;
419
593
    options.create_passphrase = passphrase;
420
593
    bilingual_str error;
421
593
    std::optional<bool> load_on_start = request.params[6].isNull() ? std::nullopt : std::optional<bool>(request.params[6].get_bool());
422
593
    const std::shared_ptr<CWallet> wallet = CreateWallet(context, request.params[0].get_str(), load_on_start, options, status, error, warnings);
423
593
    HandleWalletError(wallet, status, error);
424
425
593
    UniValue obj(UniValue::VOBJ);
426
593
    obj.pushKV("name", wallet->GetName());
427
593
    PushWarnings(warnings, obj);
428
429
593
    return obj;
430
595
},
431
1.40k
    };
432
1.40k
}
433
434
static RPCMethod unloadwallet()
435
1.10k
{
436
1.10k
    return RPCMethod{"unloadwallet",
437
1.10k
                "Unloads the wallet referenced by the request endpoint or the wallet_name argument.\n"
438
1.10k
                "If both are specified, they must be identical.",
439
1.10k
                {
440
1.10k
                    {"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."},
441
1.10k
                    {"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."},
442
1.10k
                },
443
1.10k
                RPCResult{RPCResult::Type::OBJ, "", "", {
444
1.10k
                    {RPCResult::Type::ARR, "warnings", /*optional=*/true, "Warning messages, if any, related to unloading the wallet.",
445
1.10k
                    {
446
1.10k
                        {RPCResult::Type::STR, "", ""},
447
1.10k
                    }},
448
1.10k
                }},
449
1.10k
                RPCExamples{
450
1.10k
                    HelpExampleCli("unloadwallet", "wallet_name")
451
1.10k
            + HelpExampleRpc("unloadwallet", "wallet_name")
452
1.10k
                },
453
1.10k
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
454
1.10k
{
455
294
    const std::string wallet_name{EnsureUniqueWalletName(request, self.MaybeArg<std::string_view>("wallet_name"))};
456
457
294
    WalletContext& context = EnsureWalletContext(request.context);
458
294
    std::shared_ptr<CWallet> wallet = GetWallet(context, wallet_name);
459
294
    if (!wallet) {
460
4
        throw JSONRPCError(RPC_WALLET_NOT_FOUND, "Requested wallet does not exist or is not loaded");
461
4
    }
462
463
290
    std::vector<bilingual_str> warnings;
464
290
    {
465
290
        WalletRescanReserver reserver(*wallet);
466
290
        if (!reserver.reserve()) {
467
0
            throw JSONRPCError(RPC_WALLET_ERROR, "Wallet is currently rescanning. Abort existing rescan or wait.");
468
0
        }
469
470
        // Release the "main" shared pointer and prevent further notifications.
471
        // Note that any attempt to load the same wallet would fail until the wallet
472
        // is destroyed (see CheckUniqueFileid).
473
290
        std::optional<bool> load_on_start{self.MaybeArg<bool>("load_on_startup")};
474
290
        if (!RemoveWallet(context, wallet, load_on_start, warnings)) {
475
0
            throw JSONRPCError(RPC_MISC_ERROR, "Requested wallet already unloaded");
476
0
        }
477
290
    }
478
479
290
    WaitForDeleteWallet(std::move(wallet));
480
481
290
    UniValue result(UniValue::VOBJ);
482
290
    PushWarnings(warnings, result);
483
484
290
    return result;
485
290
},
486
1.10k
    };
487
1.10k
}
488
489
RPCMethod simulaterawtransaction()
490
837
{
491
837
    return RPCMethod{
492
837
        "simulaterawtransaction",
493
837
        "Calculate the balance change resulting in the signing and broadcasting of the given transaction(s).\n",
494
837
        {
495
837
            {"rawtxs", RPCArg::Type::ARR, RPCArg::Optional::OMITTED, "An array of hex strings of raw transactions.\n",
496
837
                {
497
837
                    {"rawtx", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, ""},
498
837
                },
499
837
            },
500
837
            {"options", RPCArg::Type::OBJ_NAMED_PARAMS, RPCArg::Optional::OMITTED, "",
501
837
                {
502
837
                    {"include_watchonly", RPCArg::Type::BOOL, RPCArg::Default{false}, "(DEPRECATED) No longer used"},
503
837
                },
504
837
            },
505
837
        },
506
837
        RPCResult{
507
837
            RPCResult::Type::OBJ, "", "",
508
837
            {
509
837
                {RPCResult::Type::STR_AMOUNT, "balance_change", "The wallet balance change (negative means decrease)."},
510
837
            }
511
837
        },
512
837
        RPCExamples{
513
837
            HelpExampleCli("simulaterawtransaction", "[\"myhex\"]")
514
837
            + HelpExampleRpc("simulaterawtransaction", "[\"myhex\"]")
515
837
        },
516
837
    [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
517
837
{
518
26
    const std::shared_ptr<const CWallet> rpc_wallet = GetWalletForJSONRPCRequest(request);
519
26
    if (!rpc_wallet) return UniValue::VNULL;
520
26
    const CWallet& wallet = *rpc_wallet;
521
522
26
    LOCK(wallet.cs_wallet);
523
524
26
    const auto& txs = request.params[0].get_array();
525
26
    CAmount changes{0};
526
26
    std::map<COutPoint, CAmount> new_utxos; // UTXO:s that were made available in transaction array
527
26
    std::set<COutPoint> spent;
528
529
54
    for (size_t i = 0; i < txs.size(); ++i) {
530
38
        CMutableTransaction mtx;
531
38
        if (!DecodeHexTx(mtx, txs[i].get_str(), /*try_no_witness=*/ true, /*try_witness=*/ true)) {
532
0
            throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Transaction hex string decoding failure.");
533
0
        }
534
535
        // Fetch previous transactions (inputs)
536
38
        std::map<COutPoint, Coin> coins;
537
38
        for (const CTxIn& txin : mtx.vin) {
538
29
            coins[txin.prevout]; // Create empty map entry keyed by prevout.
539
29
        }
540
38
        wallet.chain().findCoins(coins);
541
542
        // Fetch debit; we are *spending* these; if the transaction is signed and
543
        // broadcast, we will lose everything in these
544
38
        for (const auto& txin : mtx.vin) {
545
29
            const auto& outpoint = txin.prevout;
546
29
            if (spent.contains(outpoint)) {
547
3
                throw JSONRPCError(RPC_INVALID_PARAMETER, "Transaction(s) are spending the same output more than once");
548
3
            }
549
26
            if (new_utxos.contains(outpoint)) {
550
6
                changes -= new_utxos.at(outpoint);
551
6
                new_utxos.erase(outpoint);
552
20
            } else {
553
20
                if (coins.at(outpoint).IsSpent()) {
554
7
                    throw JSONRPCError(RPC_INVALID_PARAMETER, "One or more transaction inputs are missing or have been spent already");
555
7
                }
556
13
                changes -= wallet.GetDebit(txin);
557
13
            }
558
19
            spent.insert(outpoint);
559
19
        }
560
561
        // Iterate over outputs; we are *receiving* these, if the wallet considers
562
        // them "mine"; if the transaction is signed and broadcast, we will receive
563
        // everything in these
564
        // Also populate new_utxos in case these are spent in later transactions
565
566
28
        const auto& hash = mtx.GetHash();
567
69
        for (size_t i = 0; i < mtx.vout.size(); ++i) {
568
41
            const auto& txout = mtx.vout[i];
569
41
            bool is_mine = wallet.IsMine(txout);
570
41
            changes += new_utxos[COutPoint(hash, i)] = is_mine ? txout.nValue : 0;
571
41
        }
572
28
    }
573
574
16
    UniValue result(UniValue::VOBJ);
575
16
    result.pushKV("balance_change", ValueFromAmount(changes));
576
577
16
    return result;
578
26
}
579
837
    };
580
837
}
581
582
static RPCMethod migratewallet()
583
862
{
584
862
    return RPCMethod{
585
862
        "migratewallet",
586
862
        "Migrate the wallet to a descriptor wallet.\n"
587
862
        "A new wallet backup will need to be made.\n"
588
862
        "\nThe migration process will create a backup of the wallet before migrating. This backup\n"
589
862
        "file will be named <wallet name>-<timestamp>.legacy.bak and can be found in the directory\n"
590
862
        "for this wallet. In the event of an incorrect migration, the backup can be restored using restorewallet."
591
862
        "\nEncrypted wallets must have the passphrase provided as an argument to this call.\n"
592
862
        "\nThis RPC may take a long time to complete. Increasing the RPC client timeout is recommended.",
593
862
        {
594
862
            {"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."},
595
862
            {"passphrase", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "The wallet passphrase"},
596
862
        },
597
862
        RPCResult{
598
862
            RPCResult::Type::OBJ, "", "",
599
862
            {
600
862
                {RPCResult::Type::STR, "wallet_name", "The name of the primary migrated wallet"},
601
862
                {RPCResult::Type::STR, "watchonly_name", /*optional=*/true, "The name of the migrated wallet containing the watchonly scripts"},
602
862
                {RPCResult::Type::STR, "solvables_name", /*optional=*/true, "The name of the migrated wallet containing solvable but not watched scripts"},
603
862
                {RPCResult::Type::STR, "backup_path", "The location of the backup of the original wallet"},
604
862
            }
605
862
        },
606
862
        RPCExamples{
607
862
            HelpExampleCli("migratewallet", "")
608
862
            + HelpExampleRpc("migratewallet", "")
609
862
        },
610
862
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
611
862
        {
612
51
            const std::string wallet_name{EnsureUniqueWalletName(request, self.MaybeArg<std::string_view>("wallet_name"))};
613
614
51
            SecureString wallet_pass;
615
51
            wallet_pass.reserve(100);
616
51
            if (!request.params[1].isNull()) {
617
3
                wallet_pass = std::string_view{request.params[1].get_str()};
618
3
            }
619
620
51
            WalletContext& context = EnsureWalletContext(request.context);
621
51
            util::Result<MigrationResult> res = MigrateLegacyToDescriptor(wallet_name, wallet_pass, context);
622
51
            if (!res) {
623
14
                throw JSONRPCError(RPC_WALLET_ERROR, util::ErrorString(res).original);
624
14
            }
625
626
37
            UniValue r{UniValue::VOBJ};
627
37
            r.pushKV("wallet_name", res->wallet_name);
628
37
            if (res->watchonly_wallet) {
629
8
                r.pushKV("watchonly_name", res->watchonly_wallet->GetName());
630
8
            }
631
37
            if (res->solvables_wallet) {
632
5
                r.pushKV("solvables_name", res->solvables_wallet->GetName());
633
5
            }
634
37
            r.pushKV("backup_path", res->backup_path.utf8string());
635
636
37
            return r;
637
51
        },
638
862
    };
639
862
}
640
641
RPCMethod gethdkeys()
642
844
{
643
844
    return RPCMethod{
644
844
        "gethdkeys",
645
844
        "List all BIP 32 HD keys in the wallet and which descriptors use them.\n",
646
844
        {
647
844
            {"options", RPCArg::Type::OBJ_NAMED_PARAMS, RPCArg::Optional::OMITTED, "", {
648
844
                {"active_only", RPCArg::Type::BOOL, RPCArg::Default{false}, "Show the keys for only active descriptors"},
649
844
                {"private", RPCArg::Type::BOOL, RPCArg::Default{false}, "Show private keys"}
650
844
            }},
651
844
        },
652
844
        RPCResult{RPCResult::Type::ARR, "", "", {
653
844
            {
654
844
                {RPCResult::Type::OBJ, "", "", {
655
844
                    {RPCResult::Type::STR, "xpub", "The extended public key"},
656
844
                    {RPCResult::Type::BOOL, "has_private", "Whether the wallet has the private key for this xpub"},
657
844
                    {RPCResult::Type::STR, "xprv", /*optional=*/true, "The extended private key if \"private\" is true"},
658
844
                    {RPCResult::Type::ARR, "descriptors", "Array of descriptor objects that use this HD key",
659
844
                    {
660
844
                        {RPCResult::Type::OBJ, "", "", {
661
844
                            {RPCResult::Type::STR, "desc", "Descriptor string public representation"},
662
844
                            {RPCResult::Type::BOOL, "active", "Whether this descriptor is currently used to generate new addresses"},
663
844
                        }},
664
844
                    }},
665
844
                }},
666
844
            }
667
844
        }},
668
844
        RPCExamples{
669
844
            HelpExampleCli("gethdkeys", "") + HelpExampleRpc("gethdkeys", "")
670
844
            + HelpExampleCliNamed("gethdkeys", {{"active_only", "true"}, {"private", "true"}}) + HelpExampleRpcNamed("gethdkeys", {{"active_only", "true"}, {"private", "true"}})
671
844
        },
672
844
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
673
844
        {
674
33
            const std::shared_ptr<const CWallet> wallet = GetWalletForJSONRPCRequest(request);
675
33
            if (!wallet) return UniValue::VNULL;
676
677
33
            LOCK(wallet->cs_wallet);
678
679
33
            UniValue options{request.params[0].isNull() ? UniValue::VOBJ : request.params[0]};
680
33
            const bool active_only{options.exists("active_only") ? options["active_only"].get_bool() : false};
681
33
            const bool priv{options.exists("private") ? options["private"].get_bool() : false};
682
33
            if (priv) {
683
10
                EnsureWalletIsUnlocked(*wallet);
684
10
            }
685
686
687
33
            std::set<ScriptPubKeyMan*> spkms;
688
33
            if (active_only) {
689
6
                spkms = wallet->GetActiveScriptPubKeyMans();
690
27
            } else {
691
27
                spkms = wallet->GetAllScriptPubKeyMans();
692
27
            }
693
694
33
            std::map<CExtPubKey, std::set<std::tuple<std::string, bool, bool>>> wallet_xpubs;
695
33
            std::map<CExtPubKey, CExtKey> wallet_xprvs;
696
233
            for (auto* spkm : spkms) {
697
233
                auto* desc_spkm{dynamic_cast<DescriptorScriptPubKeyMan*>(spkm)};
698
233
                CHECK_NONFATAL(desc_spkm);
699
233
                LOCK(desc_spkm->cs_desc_man);
700
233
                WalletDescriptor w_desc = desc_spkm->GetWalletDescriptor();
701
702
                // Retrieve the pubkeys from the descriptor
703
233
                std::set<CPubKey> desc_pubkeys;
704
233
                std::set<CExtPubKey> desc_xpubs;
705
233
                w_desc.descriptor->GetPubKeys(desc_pubkeys, desc_xpubs);
706
233
                for (const CExtPubKey& xpub : desc_xpubs) {
707
227
                    std::string desc_str;
708
227
                    bool ok = desc_spkm->GetDescriptorString(desc_str, /*priv=*/false);
709
227
                    CHECK_NONFATAL(ok);
710
227
                    wallet_xpubs[xpub].emplace(desc_str, wallet->IsActiveScriptPubKeyMan(*spkm), desc_spkm->HasPrivKey(xpub.pubkey.GetID()));
711
227
                    if (std::optional<CKey> key = priv ? desc_spkm->GetKey(xpub.pubkey.GetID()) : std::nullopt) {
712
73
                        wallet_xprvs[xpub] = CExtKey(xpub, *key);
713
73
                    }
714
227
                }
715
233
            }
716
717
33
            UniValue response(UniValue::VARR);
718
35
            for (const auto& [xpub, descs] : wallet_xpubs) {
719
35
                bool has_xprv = false;
720
35
                UniValue descriptors(UniValue::VARR);
721
227
                for (const auto& [desc, active, has_priv] : descs) {
722
227
                    UniValue d(UniValue::VOBJ);
723
227
                    d.pushKV("desc", desc);
724
227
                    d.pushKV("active", active);
725
227
                    has_xprv |= has_priv;
726
727
227
                    descriptors.push_back(std::move(d));
728
227
                }
729
35
                UniValue xpub_info(UniValue::VOBJ);
730
35
                xpub_info.pushKV("xpub", EncodeExtPubKey(xpub));
731
35
                xpub_info.pushKV("has_private", has_xprv);
732
35
                if (priv && has_xprv) {
733
9
                    xpub_info.pushKV("xprv", EncodeExtKey(wallet_xprvs.at(xpub)));
734
9
                }
735
35
                xpub_info.pushKV("descriptors", std::move(descriptors));
736
737
35
                response.push_back(std::move(xpub_info));
738
35
            }
739
740
33
            return response;
741
33
        },
742
844
    };
743
844
}
744
745
static RPCMethod createwalletdescriptor()
746
824
{
747
824
    return RPCMethod{"createwalletdescriptor",
748
824
        "Creates the wallet's descriptor for the given address type. "
749
824
        "The address type must be one that the wallet does not already have a descriptor for."
750
824
        + HELP_REQUIRING_PASSPHRASE,
751
824
        {
752
824
            {"type", RPCArg::Type::STR, RPCArg::Optional::NO, "The address type the descriptor will produce. Options are " + FormatAllOutputTypes() + "."},
753
824
            {"options", RPCArg::Type::OBJ_NAMED_PARAMS, RPCArg::Optional::OMITTED, "", {
754
824
                {"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)"},
755
824
                {"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"},
756
824
            }},
757
824
        },
758
824
        RPCResult{
759
824
            RPCResult::Type::OBJ, "", "",
760
824
            {
761
824
                {RPCResult::Type::ARR, "descs", "The public descriptors that were added to the wallet",
762
824
                    {{RPCResult::Type::STR, "", ""}}
763
824
                }
764
824
            },
765
824
        },
766
824
        RPCExamples{
767
824
            HelpExampleCli("createwalletdescriptor", "bech32m")
768
824
            + HelpExampleRpc("createwalletdescriptor", "bech32m")
769
824
        },
770
824
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
771
824
        {
772
13
            std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
773
13
            if (!pwallet) return UniValue::VNULL;
774
775
13
            std::optional<OutputType> output_type = ParseOutputType(request.params[0].get_str());
776
13
            if (!output_type) {
777
1
                throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Unknown address type '%s'", request.params[0].get_str()));
778
1
            }
779
780
12
            UniValue options{request.params[1].isNull() ? UniValue::VOBJ : request.params[1]};
781
12
            UniValue internal_only{options["internal"]};
782
12
            UniValue hdkey{options["hdkey"]};
783
784
12
            std::vector<bool> internals;
785
12
            if (internal_only.isNull()) {
786
10
                internals.push_back(false);
787
10
                internals.push_back(true);
788
10
            } else {
789
2
                internals.push_back(internal_only.get_bool());
790
2
            }
791
792
12
            LOCK(pwallet->cs_wallet);
793
12
            EnsureWalletIsUnlocked(*pwallet);
794
795
12
            CExtPubKey xpub;
796
12
            if (hdkey.isNull()) {
797
7
                std::set<CExtPubKey> active_xpubs = pwallet->GetActiveHDPubKeys();
798
7
                if (active_xpubs.size() != 1) {
799
2
                    throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Unable to determine which HD key to use from active descriptors. Please specify with 'hdkey'");
800
2
                }
801
5
                xpub = *active_xpubs.begin();
802
5
            } else {
803
5
                xpub = DecodeExtPubKey(hdkey.get_str());
804
5
                if (!xpub.pubkey.IsValid()) {
805
1
                    throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Unable to parse HD key. Please provide a valid xpub");
806
1
                }
807
5
            }
808
809
9
            std::optional<CKey> key = pwallet->GetKey(xpub.pubkey.GetID());
810
9
            if (!key) {
811
1
                throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Private key for %s is not known", EncodeExtPubKey(xpub)));
812
1
            }
813
8
            CExtKey active_hdkey(xpub, *key);
814
815
8
            std::vector<std::reference_wrapper<DescriptorScriptPubKeyMan>> spkms;
816
8
            WalletBatch batch{pwallet->GetDatabase()};
817
12
            for (bool internal : internals) {
818
12
                WalletDescriptor w_desc = GenerateWalletDescriptor(xpub, *output_type, internal);
819
12
                uint256 w_id = DescriptorID(*w_desc.descriptor);
820
12
                if (!pwallet->GetScriptPubKeyMan(w_id)) {
821
10
                    spkms.emplace_back(pwallet->SetupDescriptorScriptPubKeyMan(batch, active_hdkey, *output_type, internal));
822
10
                }
823
12
            }
824
8
            if (spkms.empty()) {
825
1
                throw JSONRPCError(RPC_WALLET_ERROR, "Descriptor already exists");
826
1
            }
827
828
            // Fetch each descspkm from the wallet in order to get the descriptor strings
829
7
            UniValue descs{UniValue::VARR};
830
10
            for (const auto& spkm : spkms) {
831
10
                std::string desc_str;
832
10
                bool ok = spkm.get().GetDescriptorString(desc_str, false);
833
10
                CHECK_NONFATAL(ok);
834
10
                descs.push_back(desc_str);
835
10
            }
836
7
            UniValue out{UniValue::VOBJ};
837
7
            out.pushKV("descs", std::move(descs));
838
7
            return out;
839
8
        }
840
824
    };
841
824
}
842
843
// addresses
844
RPCMethod getaddressinfo();
845
RPCMethod getnewaddress();
846
RPCMethod getrawchangeaddress();
847
RPCMethod setlabel();
848
RPCMethod listaddressgroupings();
849
RPCMethod keypoolrefill();
850
RPCMethod getaddressesbylabel();
851
RPCMethod listlabels();
852
#ifdef ENABLE_EXTERNAL_SIGNER
853
RPCMethod walletdisplayaddress();
854
#endif // ENABLE_EXTERNAL_SIGNER
855
856
// backup
857
RPCMethod importprunedfunds();
858
RPCMethod removeprunedfunds();
859
RPCMethod importdescriptors();
860
RPCMethod listdescriptors();
861
RPCMethod backupwallet();
862
RPCMethod restorewallet();
863
864
// coins
865
RPCMethod getreceivedbyaddress();
866
RPCMethod getreceivedbylabel();
867
RPCMethod getbalance();
868
RPCMethod lockunspent();
869
RPCMethod listlockunspent();
870
RPCMethod getbalances();
871
RPCMethod listunspent();
872
873
// encryption
874
RPCMethod walletpassphrase();
875
RPCMethod walletpassphrasechange();
876
RPCMethod walletlock();
877
RPCMethod encryptwallet();
878
879
// spend
880
RPCMethod sendtoaddress();
881
RPCMethod sendmany();
882
RPCMethod fundrawtransaction();
883
RPCMethod bumpfee();
884
RPCMethod psbtbumpfee();
885
RPCMethod send();
886
RPCMethod sendall();
887
RPCMethod walletprocesspsbt();
888
RPCMethod walletcreatefundedpsbt();
889
RPCMethod signrawtransactionwithwallet();
890
891
// signmessage
892
RPCMethod signmessage();
893
894
// transactions
895
RPCMethod listreceivedbyaddress();
896
RPCMethod listreceivedbylabel();
897
RPCMethod listtransactions();
898
RPCMethod listsinceblock();
899
RPCMethod gettransaction();
900
RPCMethod abandontransaction();
901
RPCMethod rescanblockchain();
902
RPCMethod abortrescan();
903
904
std::span<const CRPCCommand> GetWalletRPCCommands()
905
414
{
906
414
    static const CRPCCommand commands[]{
907
414
        {"rawtransactions", &fundrawtransaction},
908
414
        {"wallet", &abandontransaction},
909
414
        {"wallet", &abortrescan},
910
414
        {"wallet", &backupwallet},
911
414
        {"wallet", &bumpfee},
912
414
        {"wallet", &psbtbumpfee},
913
414
        {"wallet", &createwallet},
914
414
        {"wallet", &createwalletdescriptor},
915
414
        {"wallet", &restorewallet},
916
414
        {"wallet", &encryptwallet},
917
414
        {"wallet", &getaddressesbylabel},
918
414
        {"wallet", &getaddressinfo},
919
414
        {"wallet", &getbalance},
920
414
        {"wallet", &gethdkeys},
921
414
        {"wallet", &getnewaddress},
922
414
        {"wallet", &getrawchangeaddress},
923
414
        {"wallet", &getreceivedbyaddress},
924
414
        {"wallet", &getreceivedbylabel},
925
414
        {"wallet", &gettransaction},
926
414
        {"wallet", &getbalances},
927
414
        {"wallet", &getwalletinfo},
928
414
        {"wallet", &importdescriptors},
929
414
        {"wallet", &importprunedfunds},
930
414
        {"wallet", &keypoolrefill},
931
414
        {"wallet", &listaddressgroupings},
932
414
        {"wallet", &listdescriptors},
933
414
        {"wallet", &listlabels},
934
414
        {"wallet", &listlockunspent},
935
414
        {"wallet", &listreceivedbyaddress},
936
414
        {"wallet", &listreceivedbylabel},
937
414
        {"wallet", &listsinceblock},
938
414
        {"wallet", &listtransactions},
939
414
        {"wallet", &listunspent},
940
414
        {"wallet", &listwalletdir},
941
414
        {"wallet", &listwallets},
942
414
        {"wallet", &loadwallet},
943
414
        {"wallet", &lockunspent},
944
414
        {"wallet", &migratewallet},
945
414
        {"wallet", &removeprunedfunds},
946
414
        {"wallet", &rescanblockchain},
947
414
        {"wallet", &send},
948
414
        {"wallet", &sendmany},
949
414
        {"wallet", &sendtoaddress},
950
414
        {"wallet", &setlabel},
951
414
        {"wallet", &setwalletflag},
952
414
        {"wallet", &signmessage},
953
414
        {"wallet", &signrawtransactionwithwallet},
954
414
        {"wallet", &simulaterawtransaction},
955
414
        {"wallet", &sendall},
956
414
        {"wallet", &unloadwallet},
957
414
        {"wallet", &walletcreatefundedpsbt},
958
414
#ifdef ENABLE_EXTERNAL_SIGNER
959
414
        {"wallet", &walletdisplayaddress},
960
414
#endif // ENABLE_EXTERNAL_SIGNER
961
414
        {"wallet", &walletlock},
962
414
        {"wallet", &walletpassphrase},
963
414
        {"wallet", &walletpassphrasechange},
964
414
        {"wallet", &walletprocesspsbt},
965
414
    };
966
414
    return commands;
967
414
}
968
} // namespace wallet