Coverage Report

Created: 2026-06-12 16:48

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