Coverage Report

Created: 2026-06-16 16:41

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/tmp/bitcoin/src/wallet/rpc/addresses.cpp
Line
Count
Source
1
// Copyright (c) 2011-present The Bitcoin Core developers
2
// Distributed under the MIT software license, see the accompanying
3
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4
5
#include <bitcoin-build-config.h> // IWYU pragma: keep
6
7
#include <core_io.h>
8
#include <key_io.h>
9
#include <rpc/util.h>
10
#include <script/script.h>
11
#include <script/solver.h>
12
#include <util/bip32.h>
13
#include <util/translation.h>
14
#include <wallet/receive.h>
15
#include <wallet/rpc/util.h>
16
#include <wallet/wallet.h>
17
18
#include <univalue.h>
19
20
namespace wallet {
21
RPCMethod getnewaddress()
22
11.9k
{
23
11.9k
    return RPCMethod{
24
11.9k
        "getnewaddress",
25
11.9k
        "Returns a new Bitcoin address for receiving payments.\n"
26
11.9k
                "If 'label' is specified, it is added to the address book \n"
27
11.9k
                "so payments received with the address will be associated with 'label'.\n",
28
11.9k
                {
29
11.9k
                    {"label", RPCArg::Type::STR, RPCArg::Default{""}, "The label name for the address to be linked to. It can also be set to the empty string \"\" to represent the default label. The label does not need to exist, it will be created if there is no label by the given name."},
30
11.9k
                    {"address_type", RPCArg::Type::STR, RPCArg::DefaultHint{"set by -addresstype"}, "The address type to use. Options are " + FormatAllOutputTypes() + "."},
31
11.9k
                },
32
11.9k
                RPCResult{
33
11.9k
                    RPCResult::Type::STR, "address", "The new bitcoin address"
34
11.9k
                },
35
11.9k
                RPCExamples{
36
11.9k
                    HelpExampleCli("getnewaddress", "")
37
11.9k
            + HelpExampleRpc("getnewaddress", "")
38
11.9k
                },
39
11.9k
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
40
11.9k
{
41
11.0k
    std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
42
11.0k
    if (!pwallet) return UniValue::VNULL;
43
44
11.0k
    LOCK(pwallet->cs_wallet);
45
46
11.0k
    if (!pwallet->CanGetAddresses()) {
47
22
        throw JSONRPCError(RPC_WALLET_ERROR, "Error: This wallet has no available keys");
48
22
    }
49
50
    // Parse the label first so we don't generate a key if there's an error
51
11.0k
    const std::string label{LabelFromValue(request.params[0])};
52
53
11.0k
    OutputType output_type = pwallet->m_default_address_type;
54
11.0k
    if (!request.params[1].isNull()) {
55
5.07k
        std::optional<OutputType> parsed = ParseOutputType(request.params[1].get_str());
56
5.07k
        if (!parsed) {
57
1
            throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Unknown address type '%s'", request.params[1].get_str()));
58
1
        }
59
5.07k
        output_type = parsed.value();
60
5.07k
    }
61
62
11.0k
    auto op_dest = pwallet->GetNewDestination(output_type, label);
63
11.0k
    if (!op_dest) {
64
5
        throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, util::ErrorString(op_dest).original);
65
5
    }
66
67
11.0k
    return EncodeDestination(*op_dest);
68
11.0k
},
69
11.9k
    };
70
11.9k
}
71
72
RPCMethod getrawchangeaddress()
73
1.12k
{
74
1.12k
    return RPCMethod{
75
1.12k
        "getrawchangeaddress",
76
1.12k
        "Returns a new Bitcoin address, for receiving change.\n"
77
1.12k
                "This is for use with raw transactions, NOT normal use.\n",
78
1.12k
                {
79
1.12k
                    {"address_type", RPCArg::Type::STR, RPCArg::DefaultHint{"set by -changetype"}, "The address type to use. Options are " + FormatAllOutputTypes() + "."},
80
1.12k
                },
81
1.12k
                RPCResult{
82
1.12k
                    RPCResult::Type::STR, "address", "The address"
83
1.12k
                },
84
1.12k
                RPCExamples{
85
1.12k
                    HelpExampleCli("getrawchangeaddress", "")
86
1.12k
            + HelpExampleRpc("getrawchangeaddress", "")
87
1.12k
                },
88
1.12k
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
89
1.12k
{
90
313
    std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
91
313
    if (!pwallet) return UniValue::VNULL;
92
93
313
    LOCK(pwallet->cs_wallet);
94
95
313
    if (!pwallet->CanGetAddresses(true)) {
96
25
        throw JSONRPCError(RPC_WALLET_ERROR, "Error: This wallet has no available keys");
97
25
    }
98
99
288
    OutputType output_type = pwallet->m_default_change_type.value_or(pwallet->m_default_address_type);
100
288
    if (!request.params[0].isNull()) {
101
210
        std::optional<OutputType> parsed = ParseOutputType(request.params[0].get_str());
102
210
        if (!parsed) {
103
2
            throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Unknown address type '%s'", request.params[0].get_str()));
104
2
        }
105
208
        output_type = parsed.value();
106
208
    }
107
108
286
    auto op_dest = pwallet->GetNewChangeDestination(output_type);
109
286
    if (!op_dest) {
110
1
        throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, util::ErrorString(op_dest).original);
111
1
    }
112
285
    return EncodeDestination(*op_dest);
113
286
},
114
1.12k
    };
115
1.12k
}
116
117
118
RPCMethod setlabel()
119
829
{
120
829
    return RPCMethod{
121
829
        "setlabel",
122
829
        "Sets the label associated with the given address.\n",
123
829
                {
124
829
                    {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The bitcoin address to be associated with a label."},
125
829
                    {"label", RPCArg::Type::STR, RPCArg::Optional::NO, "The label to assign to the address."},
126
829
                },
127
829
                RPCResult{RPCResult::Type::NONE, "", ""},
128
829
                RPCExamples{
129
829
                    HelpExampleCli("setlabel", "\"" + EXAMPLE_ADDRESS[0] + "\" \"tabby\"")
130
829
            + HelpExampleRpc("setlabel", "\"" + EXAMPLE_ADDRESS[0] + "\", \"tabby\"")
131
829
                },
132
829
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
133
829
{
134
16
    std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
135
16
    if (!pwallet) return UniValue::VNULL;
136
137
16
    LOCK(pwallet->cs_wallet);
138
139
16
    CTxDestination dest = DecodeDestination(request.params[0].get_str());
140
16
    if (!IsValidDestination(dest)) {
141
0
        throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address");
142
0
    }
143
144
16
    const std::string label{LabelFromValue(request.params[1])};
145
146
16
    if (pwallet->IsMine(dest)) {
147
14
        pwallet->SetAddressBook(dest, label, AddressPurpose::RECEIVE);
148
14
    } else {
149
2
        pwallet->SetAddressBook(dest, label, AddressPurpose::SEND);
150
2
    }
151
152
16
    return UniValue::VNULL;
153
16
},
154
829
    };
155
829
}
156
157
RPCMethod listaddressgroupings()
158
815
{
159
815
    return RPCMethod{
160
815
        "listaddressgroupings",
161
815
        "Lists groups of addresses which have had their common ownership\n"
162
815
                "made public by common use as inputs or as the resulting change\n"
163
815
                "in past transactions\n",
164
815
                {},
165
815
                RPCResult{
166
815
                    RPCResult::Type::ARR, "", "",
167
815
                    {
168
815
                        {RPCResult::Type::ARR, "", "",
169
815
                        {
170
815
                            {RPCResult::Type::ARR_FIXED, "", "",
171
815
                            {
172
815
                                {RPCResult::Type::STR, "address", "The bitcoin address"},
173
815
                                {RPCResult::Type::STR_AMOUNT, "amount", "The amount in " + CURRENCY_UNIT},
174
815
                                {RPCResult::Type::STR, "label", /*optional=*/true, "The label"},
175
815
                            }},
176
815
                        }},
177
815
                    }
178
815
                },
179
815
                RPCExamples{
180
815
                    HelpExampleCli("listaddressgroupings", "")
181
815
            + HelpExampleRpc("listaddressgroupings", "")
182
815
                },
183
815
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
184
815
{
185
2
    const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
186
2
    if (!pwallet) return UniValue::VNULL;
187
188
    // Make sure the results are valid at least up to the most recent block
189
    // the user could have gotten from another RPC command prior to now
190
2
    pwallet->BlockUntilSyncedToCurrentChain();
191
192
2
    LOCK(pwallet->cs_wallet);
193
194
2
    UniValue jsonGroupings(UniValue::VARR);
195
2
    std::map<CTxDestination, CAmount> balances = GetAddressBalances(*pwallet);
196
3
    for (const std::set<CTxDestination>& grouping : GetAddressGroupings(*pwallet)) {
197
3
        UniValue jsonGrouping(UniValue::VARR);
198
3
        for (const CTxDestination& address : grouping)
199
4
        {
200
4
            UniValue addressInfo(UniValue::VARR);
201
4
            addressInfo.push_back(EncodeDestination(address));
202
4
            addressInfo.push_back(ValueFromAmount(balances[address]));
203
4
            {
204
4
                const auto* address_book_entry = pwallet->FindAddressBookEntry(address);
205
4
                if (address_book_entry) {
206
4
                    addressInfo.push_back(address_book_entry->GetLabel());
207
4
                }
208
4
            }
209
4
            jsonGrouping.push_back(std::move(addressInfo));
210
4
        }
211
3
        jsonGroupings.push_back(std::move(jsonGrouping));
212
3
    }
213
2
    return jsonGroupings;
214
2
},
215
815
    };
216
815
}
217
218
RPCMethod keypoolrefill()
219
823
{
220
823
    return RPCMethod{"keypoolrefill",
221
823
                "Refills each descriptor keypool in the wallet up to the specified number of new keys.\n"
222
823
                "By default, descriptor wallets have 4 active ranged descriptors (" + FormatAllOutputTypes() + "), each with " + util::ToString(DEFAULT_KEYPOOL_SIZE) + " entries.\n" +
223
823
        HELP_REQUIRING_PASSPHRASE,
224
823
                {
225
823
                    {"newsize", RPCArg::Type::NUM, RPCArg::DefaultHint{strprintf("%u, or as set by -keypool", DEFAULT_KEYPOOL_SIZE)}, "The new keypool size"},
226
823
                },
227
823
                RPCResult{RPCResult::Type::NONE, "", ""},
228
823
                RPCExamples{
229
823
                    HelpExampleCli("keypoolrefill", "")
230
823
            + HelpExampleRpc("keypoolrefill", "")
231
823
                },
232
823
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
233
823
{
234
10
    std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
235
10
    if (!pwallet) return UniValue::VNULL;
236
237
10
    LOCK(pwallet->cs_wallet);
238
239
    // 0 is interpreted by TopUpKeyPool() as the default keypool size given by -keypool
240
10
    unsigned int kpSize = 0;
241
10
    if (!request.params[0].isNull()) {
242
9
        if (request.params[0].getInt<int>() < 0)
243
0
            throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected valid size.");
244
9
        kpSize = (unsigned int)request.params[0].getInt<int>();
245
9
    }
246
247
10
    EnsureWalletIsUnlocked(*pwallet);
248
10
    pwallet->TopUpKeyPool(kpSize);
249
250
10
    if (pwallet->GetKeyPoolSize() < kpSize) {
251
0
        throw JSONRPCError(RPC_WALLET_ERROR, "Error refreshing keypool.");
252
0
    }
253
10
    pwallet->RefreshAllTXOs();
254
255
10
    return UniValue::VNULL;
256
10
},
257
823
    };
258
823
}
259
260
class DescribeWalletAddressVisitor
261
{
262
public:
263
    const SigningProvider * const provider;
264
265
    // NOLINTNEXTLINE(misc-no-recursion)
266
    void ProcessSubScript(const CScript& subscript, UniValue& obj) const
267
128
    {
268
        // Always present: script type and redeemscript
269
128
        std::vector<std::vector<unsigned char>> solutions_data;
270
128
        TxoutType which_type = Solver(subscript, solutions_data);
271
128
        obj.pushKV("script", GetTxnOutputType(which_type));
272
128
        obj.pushKV("hex", HexStr(subscript));
273
274
128
        CTxDestination embedded;
275
128
        if (ExtractDestination(subscript, embedded)) {
276
            // Only when the script corresponds to an address.
277
110
            UniValue subobj(UniValue::VOBJ);
278
110
            UniValue detail = DescribeAddress(embedded);
279
110
            subobj.pushKVs(std::move(detail));
280
110
            UniValue wallet_detail = std::visit(*this, embedded);
281
110
            subobj.pushKVs(std::move(wallet_detail));
282
110
            subobj.pushKV("address", EncodeDestination(embedded));
283
110
            subobj.pushKV("scriptPubKey", HexStr(subscript));
284
            // Always report the pubkey at the top level, so that `getnewaddress()['pubkey']` always works.
285
110
            if (subobj.exists("pubkey")) obj.pushKV("pubkey", subobj["pubkey"]);
286
110
            obj.pushKV("embedded", std::move(subobj));
287
110
        } else if (which_type == TxoutType::MULTISIG) {
288
            // Also report some information on multisig scripts (which do not have a corresponding address).
289
16
            obj.pushKV("sigsrequired", solutions_data[0][0]);
290
16
            UniValue pubkeys(UniValue::VARR);
291
58
            for (size_t i = 1; i < solutions_data.size() - 1; ++i) {
292
42
                CPubKey key(solutions_data[i].begin(), solutions_data[i].end());
293
42
                pubkeys.push_back(HexStr(key));
294
42
            }
295
16
            obj.pushKV("pubkeys", std::move(pubkeys));
296
16
        }
297
128
    }
298
299
746
    explicit DescribeWalletAddressVisitor(const SigningProvider* _provider) : provider(_provider) {}
300
301
0
    UniValue operator()(const CNoDestination& dest) const { return UniValue(UniValue::VOBJ); }
302
0
    UniValue operator()(const PubKeyDestination& dest) const { return UniValue(UniValue::VOBJ); }
303
304
    UniValue operator()(const PKHash& pkhash) const
305
110
    {
306
110
        CKeyID keyID{ToKeyID(pkhash)};
307
110
        UniValue obj(UniValue::VOBJ);
308
110
        CPubKey vchPubKey;
309
110
        if (provider && provider->GetPubKey(keyID, vchPubKey)) {
310
106
            obj.pushKV("pubkey", HexStr(vchPubKey));
311
106
            obj.pushKV("iscompressed", vchPubKey.IsCompressed());
312
106
        }
313
110
        return obj;
314
110
    }
315
316
    // NOLINTNEXTLINE(misc-no-recursion)
317
    UniValue operator()(const ScriptHash& scripthash) const
318
108
    {
319
108
        UniValue obj(UniValue::VOBJ);
320
108
        CScript subscript;
321
108
        if (provider && provider->GetCScript(ToScriptID(scripthash), subscript)) {
322
106
            ProcessSubScript(subscript, obj);
323
106
        }
324
108
        return obj;
325
108
    }
326
327
    UniValue operator()(const WitnessV0KeyHash& id) const
328
492
    {
329
492
        UniValue obj(UniValue::VOBJ);
330
492
        CPubKey pubkey;
331
492
        if (provider && provider->GetPubKey(ToKeyID(id), pubkey)) {
332
427
            obj.pushKV("pubkey", HexStr(pubkey));
333
427
        }
334
492
        return obj;
335
492
    }
336
337
    // NOLINTNEXTLINE(misc-no-recursion)
338
    UniValue operator()(const WitnessV0ScriptHash& id) const
339
38
    {
340
38
        UniValue obj(UniValue::VOBJ);
341
38
        CScript subscript;
342
38
        CRIPEMD160 hasher;
343
38
        uint160 hash;
344
38
        hasher.Write(id.begin(), 32).Finalize(hash.begin());
345
38
        if (provider && provider->GetCScript(CScriptID(hash), subscript)) {
346
22
            ProcessSubScript(subscript, obj);
347
22
        }
348
38
        return obj;
349
38
    }
350
351
107
    UniValue operator()(const WitnessV1Taproot& id) const { return UniValue(UniValue::VOBJ); }
352
0
    UniValue operator()(const PayToAnchor& id) const { return UniValue(UniValue::VOBJ); }
353
1
    UniValue operator()(const WitnessUnknown& id) const { return UniValue(UniValue::VOBJ); }
354
};
355
356
static UniValue DescribeWalletAddress(const CWallet& wallet, const CTxDestination& dest)
357
746
{
358
746
    UniValue ret(UniValue::VOBJ);
359
746
    UniValue detail = DescribeAddress(dest);
360
746
    CScript script = GetScriptForDestination(dest);
361
746
    std::unique_ptr<SigningProvider> provider = nullptr;
362
746
    provider = wallet.GetSolvingProvider(script);
363
746
    ret.pushKVs(std::move(detail));
364
746
    ret.pushKVs(std::visit(DescribeWalletAddressVisitor(provider.get()), dest));
365
746
    return ret;
366
746
}
367
368
RPCMethod getaddressinfo()
369
1.56k
{
370
1.56k
    return RPCMethod{
371
1.56k
        "getaddressinfo",
372
1.56k
        "Return information about the given bitcoin address.\n"
373
1.56k
                "Some of the information will only be present if the address is in the active wallet.\n",
374
1.56k
                {
375
1.56k
                    {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The bitcoin address for which to get information."},
376
1.56k
                },
377
1.56k
                RPCResult{
378
1.56k
                    RPCResult::Type::OBJ, "", "",
379
1.56k
                    {
380
1.56k
                        {RPCResult::Type::STR, "address", "The bitcoin address validated."},
381
1.56k
                        {RPCResult::Type::STR_HEX, "scriptPubKey", "The hex-encoded output script generated by the address."},
382
1.56k
                        {RPCResult::Type::BOOL, "ismine", "If the address is yours."},
383
1.56k
                        {RPCResult::Type::BOOL, "iswatchonly", "(DEPRECATED) Always false."},
384
1.56k
                        {RPCResult::Type::BOOL, "solvable", "If we know how to spend coins sent to this address, ignoring the possible lack of private keys."},
385
1.56k
                        {RPCResult::Type::STR, "desc", /*optional=*/true, "A descriptor for spending coins sent to this address (only when solvable)."},
386
1.56k
                        {RPCResult::Type::STR, "parent_desc", /*optional=*/true, "The descriptor used to derive this address if this is a descriptor wallet"},
387
1.56k
                        {RPCResult::Type::BOOL, "isscript", /*optional=*/true, "If the key is a script."},
388
1.56k
                        {RPCResult::Type::BOOL, "ischange", "If the address was used for change output."},
389
1.56k
                        {RPCResult::Type::BOOL, "iswitness", "If the address is a witness address."},
390
1.56k
                        {RPCResult::Type::NUM, "witness_version", /*optional=*/true, "The version number of the witness program."},
391
1.56k
                        {RPCResult::Type::STR_HEX, "witness_program", /*optional=*/true, "The hex value of the witness program."},
392
1.56k
                        {RPCResult::Type::STR, "script", /*optional=*/true, "The output script type. Only if isscript is true and the redeemscript is known. Possible\n"
393
1.56k
                                                                     "types: nonstandard, pubkey, pubkeyhash, scripthash, multisig, nulldata, witness_v0_keyhash,\n"
394
1.56k
                            "witness_v0_scripthash, witness_unknown."},
395
1.56k
                        {RPCResult::Type::STR_HEX, "hex", /*optional=*/true, "The redeemscript for the p2sh address."},
396
1.56k
                        {RPCResult::Type::ARR, "pubkeys", /*optional=*/true, "Array of pubkeys associated with the known redeemscript (only if script is multisig).",
397
1.56k
                        {
398
1.56k
                            {RPCResult::Type::STR, "pubkey", ""},
399
1.56k
                        }},
400
1.56k
                        {RPCResult::Type::NUM, "sigsrequired", /*optional=*/true, "The number of signatures required to spend multisig output (only if script is multisig)."},
401
1.56k
                        {RPCResult::Type::STR_HEX, "pubkey", /*optional=*/true, "The hex value of the raw public key for single-key addresses (possibly embedded in P2SH or P2WSH)."},
402
1.56k
                        {RPCResult::Type::OBJ, "embedded", /*optional=*/true, "Information about the address embedded in P2SH or P2WSH, if relevant and known.",
403
1.56k
                        {
404
1.56k
                            {RPCResult::Type::ELISION, "", "Includes all getaddressinfo output fields for the embedded address, excluding metadata (timestamp, hdkeypath, hdseedid)\n"
405
1.56k
                            "and relation to the wallet (ismine)."},
406
1.56k
                        }},
407
1.56k
                        {RPCResult::Type::BOOL, "iscompressed", /*optional=*/true, "If the pubkey is compressed."},
408
1.56k
                        {RPCResult::Type::NUM_TIME, "timestamp", /*optional=*/true, "The creation time of the key, if available, expressed in " + UNIX_EPOCH_TIME + "."},
409
1.56k
                        {RPCResult::Type::STR, "hdkeypath", /*optional=*/true, "The HD keypath, if the key is HD and available."},
410
1.56k
                        {RPCResult::Type::STR_HEX, "hdseedid", /*optional=*/true, "The Hash160 of the HD seed."},
411
1.56k
                        {RPCResult::Type::STR_HEX, "hdmasterfingerprint", /*optional=*/true, "The fingerprint of the master key."},
412
1.56k
                        {RPCResult::Type::ARR, "labels", "Array of labels associated with the address. Currently limited to one label but returned\n"
413
1.56k
                            "as an array to keep the API stable if multiple labels are enabled in the future.",
414
1.56k
                        {
415
1.56k
                            {RPCResult::Type::STR, "label name", "Label name (defaults to \"\")."},
416
1.56k
                        }},
417
1.56k
                    }
418
1.56k
                },
419
1.56k
                RPCExamples{
420
1.56k
                    HelpExampleCli("getaddressinfo", "\"" + EXAMPLE_ADDRESS[0] + "\"") +
421
1.56k
                    HelpExampleRpc("getaddressinfo", "\"" + EXAMPLE_ADDRESS[0] + "\"")
422
1.56k
                },
423
1.56k
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
424
1.56k
{
425
751
    const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
426
751
    if (!pwallet) return UniValue::VNULL;
427
428
751
    LOCK(pwallet->cs_wallet);
429
430
751
    std::string error_msg;
431
751
    CTxDestination dest = DecodeDestination(request.params[0].get_str(), error_msg);
432
433
    // Make sure the destination is valid
434
751
    if (!IsValidDestination(dest)) {
435
        // Set generic error message in case 'DecodeDestination' didn't set it
436
5
        if (error_msg.empty()) error_msg = "Invalid address";
437
438
5
        throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, error_msg);
439
5
    }
440
441
746
    UniValue ret(UniValue::VOBJ);
442
443
746
    std::string currentAddress = EncodeDestination(dest);
444
746
    ret.pushKV("address", currentAddress);
445
446
746
    CScript scriptPubKey = GetScriptForDestination(dest);
447
746
    ret.pushKV("scriptPubKey", HexStr(scriptPubKey));
448
449
746
    std::unique_ptr<SigningProvider> provider = pwallet->GetSolvingProvider(scriptPubKey);
450
451
746
    bool mine = pwallet->IsMine(dest);
452
746
    ret.pushKV("ismine", mine);
453
454
746
    if (provider) {
455
659
        auto inferred = InferDescriptor(scriptPubKey, *provider);
456
659
        bool solvable = inferred->IsSolvable();
457
659
        ret.pushKV("solvable", solvable);
458
659
        if (solvable) {
459
655
            ret.pushKV("desc", inferred->ToString());
460
655
        }
461
659
    } else {
462
87
        ret.pushKV("solvable", false);
463
87
    }
464
465
746
    const auto& spk_mans = pwallet->GetScriptPubKeyMans(scriptPubKey);
466
    // In most cases there is only one matching ScriptPubKey manager and we can't resolve ambiguity in a better way
467
746
    ScriptPubKeyMan* spk_man{nullptr};
468
746
    if (spk_mans.size()) spk_man = *spk_mans.begin();
469
470
746
    DescriptorScriptPubKeyMan* desc_spk_man = dynamic_cast<DescriptorScriptPubKeyMan*>(spk_man);
471
746
    if (desc_spk_man) {
472
659
        std::string desc_str;
473
659
        if (desc_spk_man->GetDescriptorString(desc_str, /*priv=*/false)) {
474
659
            ret.pushKV("parent_desc", desc_str);
475
659
        }
476
659
    }
477
478
746
    ret.pushKV("iswatchonly", false);
479
480
746
    UniValue detail = DescribeWalletAddress(*pwallet, dest);
481
746
    ret.pushKVs(std::move(detail));
482
483
746
    ret.pushKV("ischange", ScriptIsChange(*pwallet, scriptPubKey));
484
485
746
    if (spk_man) {
486
659
        if (const std::unique_ptr<CKeyMetadata> meta = spk_man->GetMetadata(dest)) {
487
573
            ret.pushKV("timestamp", meta->nCreateTime);
488
573
            if (meta->has_key_origin) {
489
                // In legacy wallets hdkeypath has always used an apostrophe for
490
                // hardened derivation. Perhaps some external tool depends on that.
491
573
                ret.pushKV("hdkeypath", WriteHDKeypath(meta->key_origin.path, /*apostrophe=*/!desc_spk_man));
492
573
                ret.pushKV("hdseedid", meta->hd_seed_id.GetHex());
493
573
                ret.pushKV("hdmasterfingerprint", HexStr(meta->key_origin.fingerprint));
494
573
            }
495
573
        }
496
659
    }
497
498
    // Return a `labels` array containing the label associated with the address,
499
    // equivalent to the `label` field above. Currently only one label can be
500
    // associated with an address, but we return an array so the API remains
501
    // stable if we allow multiple labels to be associated with an address in
502
    // the future.
503
746
    UniValue labels(UniValue::VARR);
504
746
    const auto* address_book_entry = pwallet->FindAddressBookEntry(dest);
505
746
    if (address_book_entry) {
506
516
        labels.push_back(address_book_entry->GetLabel());
507
516
    }
508
746
    ret.pushKV("labels", std::move(labels));
509
510
746
    return ret;
511
751
},
512
1.56k
    };
513
1.56k
}
514
515
RPCMethod getaddressesbylabel()
516
852
{
517
852
    return RPCMethod{
518
852
        "getaddressesbylabel",
519
852
        "Returns the list of addresses assigned the specified label.\n",
520
852
                {
521
852
                    {"label", RPCArg::Type::STR, RPCArg::Optional::NO, "The label."},
522
852
                },
523
852
                RPCResult{
524
852
                    RPCResult::Type::OBJ_DYN, "", "json object with addresses as keys",
525
852
                    {
526
852
                        {RPCResult::Type::OBJ, "address", "json object with information about address",
527
852
                        {
528
852
                            {RPCResult::Type::STR, "purpose", "Purpose of address (\"send\" for sending address, \"receive\" for receiving address)"},
529
852
                        }},
530
852
                    }
531
852
                },
532
852
                RPCExamples{
533
852
                    HelpExampleCli("getaddressesbylabel", "\"tabby\"")
534
852
            + HelpExampleRpc("getaddressesbylabel", "\"tabby\"")
535
852
                },
536
852
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
537
852
{
538
39
    const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
539
39
    if (!pwallet) return UniValue::VNULL;
540
541
39
    LOCK(pwallet->cs_wallet);
542
543
39
    const std::string label{LabelFromValue(request.params[0])};
544
545
    // Find all addresses that have the given label
546
39
    UniValue ret(UniValue::VOBJ);
547
39
    std::set<std::string> addresses;
548
520
    pwallet->ForEachAddrBookEntry([&](const CTxDestination& _dest, const std::string& _label, bool _is_change, const std::optional<AddressPurpose>& _purpose) {
549
520
        if (_is_change) return;
550
520
        if (_label == label) {
551
61
            std::string address = EncodeDestination(_dest);
552
            // CWallet::m_address_book is not expected to contain duplicate
553
            // address strings, but build a separate set as a precaution just in
554
            // case it does.
555
61
            bool unique = addresses.emplace(address).second;
556
61
            CHECK_NONFATAL(unique);
557
            // UniValue::pushKV checks if the key exists in O(N)
558
            // and since duplicate addresses are unexpected (checked with
559
            // std::set in O(log(N))), UniValue::pushKVEnd is used instead,
560
            // which currently is O(1).
561
61
            UniValue value(UniValue::VOBJ);
562
61
            value.pushKV("purpose", _purpose ? PurposeToString(*_purpose) : "unknown");
563
61
            ret.pushKVEnd(address, std::move(value));
564
61
        }
565
520
    });
566
567
39
    if (ret.empty()) {
568
5
        throw JSONRPCError(RPC_WALLET_INVALID_LABEL_NAME, std::string("No addresses with label " + label));
569
5
    }
570
571
34
    return ret;
572
39
},
573
852
    };
574
852
}
575
576
RPCMethod listlabels()
577
853
{
578
853
    return RPCMethod{
579
853
        "listlabels",
580
853
        "Returns the list of all labels, or labels that are assigned to addresses with a specific purpose.\n",
581
853
                {
582
853
                    {"purpose", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "Address purpose to list labels for ('send','receive'). An empty string is the same as not providing this argument."},
583
853
                },
584
853
                RPCResult{
585
853
                    RPCResult::Type::ARR, "", "",
586
853
                    {
587
853
                        {RPCResult::Type::STR, "label", "Label name"},
588
853
                    }
589
853
                },
590
853
                RPCExamples{
591
853
            "\nList all labels\n"
592
853
            + HelpExampleCli("listlabels", "") +
593
853
            "\nList labels that have receiving addresses\n"
594
853
            + HelpExampleCli("listlabels", "receive") +
595
853
            "\nList labels that have sending addresses\n"
596
853
            + HelpExampleCli("listlabels", "send") +
597
853
            "\nAs a JSON-RPC call\n"
598
853
            + HelpExampleRpc("listlabels", "receive")
599
853
                },
600
853
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
601
853
{
602
40
    const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
603
40
    if (!pwallet) return UniValue::VNULL;
604
605
40
    LOCK(pwallet->cs_wallet);
606
607
40
    std::optional<AddressPurpose> purpose;
608
40
    if (!request.params[0].isNull()) {
609
4
        std::string purpose_str = request.params[0].get_str();
610
4
        if (!purpose_str.empty()) {
611
4
            purpose = PurposeFromString(purpose_str);
612
4
            if (!purpose) {
613
2
                throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid 'purpose' argument, must be a known purpose string, typically 'send', or 'receive'.");
614
2
            }
615
4
        }
616
4
    }
617
618
    // Add to a set to sort by label name, then insert into Univalue array
619
38
    std::set<std::string> label_set = pwallet->ListAddrBookLabels(purpose);
620
621
38
    UniValue ret(UniValue::VARR);
622
225
    for (const std::string& name : label_set) {
623
225
        ret.push_back(name);
624
225
    }
625
626
38
    return ret;
627
40
},
628
853
    };
629
853
}
630
631
632
#ifdef ENABLE_EXTERNAL_SIGNER
633
RPCMethod walletdisplayaddress()
634
818
{
635
818
    return RPCMethod{
636
818
        "walletdisplayaddress",
637
818
        "Display address on an external signer for verification.",
638
818
        {
639
818
            {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "bitcoin address to display"},
640
818
        },
641
818
        RPCResult{
642
818
            RPCResult::Type::OBJ,"","",
643
818
            {
644
818
                {RPCResult::Type::STR, "address", "The address as confirmed by the signer"},
645
818
            }
646
818
        },
647
818
        RPCExamples{""},
648
818
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
649
818
        {
650
5
            std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
651
5
            if (!wallet) return UniValue::VNULL;
652
5
            CWallet* const pwallet = wallet.get();
653
654
5
            LOCK(pwallet->cs_wallet);
655
656
5
            CTxDestination dest = DecodeDestination(request.params[0].get_str());
657
658
            // Make sure the destination is valid
659
5
            if (!IsValidDestination(dest)) {
660
0
                throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address");
661
0
            }
662
663
5
            util::Result<void> res = pwallet->DisplayAddress(dest);
664
5
            if (!res) throw JSONRPCError(RPC_MISC_ERROR, util::ErrorString(res).original);
665
666
4
            UniValue result(UniValue::VOBJ);
667
4
            result.pushKV("address", request.params[0].get_str());
668
4
            return result;
669
5
        }
670
818
    };
671
818
}
672
#endif // ENABLE_EXTERNAL_SIGNER
673
} // namespace wallet