Coverage Report

Created: 2026-07-23 20:35

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.1k
    std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
42
11.1k
    if (!pwallet) return UniValue::VNULL;
43
44
11.1k
    LOCK(pwallet->cs_wallet);
45
46
11.1k
    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.04k
        std::optional<OutputType> parsed = ParseOutputType(request.params[1].get_str());
56
5.04k
        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.04k
        output_type = parsed.value();
60
5.04k
    }
61
62
11.0k
    auto op_dest = pwallet->GetNewDestination(output_type, label);
63
11.0k
    if (!op_dest) {
64
6
        throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, util::ErrorString(op_dest).original);
65
6
    }
66
67
11.0k
    return EncodeDestination(*op_dest);
68
11.0k
},
69
11.9k
    };
70
11.9k
}
71
72
RPCMethod getrawchangeaddress()
73
1.18k
{
74
1.18k
    return RPCMethod{
75
1.18k
        "getrawchangeaddress",
76
1.18k
        "Returns a new Bitcoin address, for receiving change.\n"
77
1.18k
                "This is for use with raw transactions, NOT normal use.\n",
78
1.18k
                {
79
1.18k
                    {"address_type", RPCArg::Type::STR, RPCArg::DefaultHint{"set by -changetype"}, "The address type to use. Options are " + FormatAllOutputTypes() + "."},
80
1.18k
                },
81
1.18k
                RPCResult{
82
1.18k
                    RPCResult::Type::STR, "address", "The address"
83
1.18k
                },
84
1.18k
                RPCExamples{
85
1.18k
                    HelpExampleCli("getrawchangeaddress", "")
86
1.18k
            + HelpExampleRpc("getrawchangeaddress", "")
87
1.18k
                },
88
1.18k
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
89
1.18k
{
90
362
    std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
91
362
    if (!pwallet) return UniValue::VNULL;
92
93
362
    LOCK(pwallet->cs_wallet);
94
95
362
    if (!pwallet->CanGetAddresses(true)) {
96
25
        throw JSONRPCError(RPC_WALLET_ERROR, "Error: This wallet has no available keys");
97
25
    }
98
99
337
    OutputType output_type = pwallet->m_default_change_type.value_or(pwallet->m_default_address_type);
100
337
    if (!request.params[0].isNull()) {
101
218
        std::optional<OutputType> parsed = ParseOutputType(request.params[0].get_str());
102
218
        if (!parsed) {
103
2
            throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Unknown address type '%s'", request.params[0].get_str()));
104
2
        }
105
216
        output_type = parsed.value();
106
216
    }
107
108
335
    auto op_dest = pwallet->GetNewChangeDestination(output_type);
109
335
    if (!op_dest) {
110
1
        throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, util::ErrorString(op_dest).original);
111
1
    }
112
334
    return EncodeDestination(*op_dest);
113
335
},
114
1.18k
    };
115
1.18k
}
116
117
118
RPCMethod setlabel()
119
838
{
120
838
    return RPCMethod{
121
838
        "setlabel",
122
838
        "Sets the label associated with the given address.\n",
123
838
                {
124
838
                    {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The bitcoin address to be associated with a label."},
125
838
                    {"label", RPCArg::Type::STR, RPCArg::Optional::NO, "The label to assign to the address."},
126
838
                },
127
838
                RPCResult{RPCResult::Type::NONE, "", ""},
128
838
                RPCExamples{
129
838
                    HelpExampleCli("setlabel", "\"" + EXAMPLE_ADDRESS[0] + "\" \"tabby\"")
130
838
            + HelpExampleRpc("setlabel", "\"" + EXAMPLE_ADDRESS[0] + "\", \"tabby\"")
131
838
                },
132
838
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
133
838
{
134
17
    std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
135
17
    if (!pwallet) return UniValue::VNULL;
136
137
17
    LOCK(pwallet->cs_wallet);
138
139
17
    CTxDestination dest = DecodeDestination(request.params[0].get_str());
140
17
    if (!IsValidDestination(dest)) {
141
0
        throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address");
142
0
    }
143
144
17
    const std::string label{LabelFromValue(request.params[1])};
145
146
17
    if (pwallet->IsMine(dest)) {
147
14
        pwallet->SetAddressBook(dest, label, AddressPurpose::RECEIVE);
148
14
    } else {
149
3
        pwallet->SetAddressBook(dest, label, AddressPurpose::SEND);
150
3
    }
151
152
17
    return UniValue::VNULL;
153
17
},
154
838
    };
155
838
}
156
157
RPCMethod listaddressgroupings()
158
823
{
159
823
    return RPCMethod{
160
823
        "listaddressgroupings",
161
823
        "Lists groups of addresses which have had their common ownership\n"
162
823
                "made public by common use as inputs or as the resulting change\n"
163
823
                "in past transactions\n",
164
823
                {},
165
823
                RPCResult{
166
823
                    RPCResult::Type::ARR, "", "",
167
823
                    {
168
823
                        {RPCResult::Type::ARR, "", "",
169
823
                        {
170
823
                            {RPCResult::Type::ARR_FIXED, "", "",
171
823
                            {
172
823
                                {RPCResult::Type::STR, "address", "The bitcoin address"},
173
823
                                {RPCResult::Type::STR_AMOUNT, "amount", "The amount in " + CURRENCY_UNIT},
174
823
                                {RPCResult::Type::STR, "label", /*optional=*/true, "The label"},
175
823
                            }},
176
823
                        }},
177
823
                    }
178
823
                },
179
823
                RPCExamples{
180
823
                    HelpExampleCli("listaddressgroupings", "")
181
823
            + HelpExampleRpc("listaddressgroupings", "")
182
823
                },
183
823
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
184
823
{
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
823
    };
216
823
}
217
218
RPCMethod keypoolrefill()
219
831
{
220
831
    return RPCMethod{"keypoolrefill",
221
831
                "Refills each descriptor keypool in the wallet up to the specified number of new keys.\n"
222
831
                "By default, descriptor wallets have 4 active ranged descriptors (" + FormatAllOutputTypes() + "), each with " + util::ToString(DEFAULT_KEYPOOL_SIZE) + " entries.\n" +
223
831
        HELP_REQUIRING_PASSPHRASE,
224
831
                {
225
831
                    {"newsize", RPCArg::Type::NUM, RPCArg::DefaultHint{strprintf("%u, or as set by -keypool", DEFAULT_KEYPOOL_SIZE)}, "The new keypool size"},
226
831
                },
227
831
                RPCResult{RPCResult::Type::NONE, "", ""},
228
831
                RPCExamples{
229
831
                    HelpExampleCli("keypoolrefill", "")
230
831
            + HelpExampleRpc("keypoolrefill", "")
231
831
                },
232
831
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
233
831
{
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
831
    };
258
831
}
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
112
    {
268
        // Always present: script type and redeemscript
269
112
        std::vector<std::vector<unsigned char>> solutions_data;
270
112
        TxoutType which_type = Solver(subscript, solutions_data);
271
112
        obj.pushKV("script", GetTxnOutputType(which_type));
272
112
        obj.pushKV("hex", HexStr(subscript));
273
274
112
        CTxDestination embedded;
275
112
        if (ExtractDestination(subscript, embedded)) {
276
            // Only when the script corresponds to an address.
277
92
            UniValue subobj(UniValue::VOBJ);
278
92
            UniValue detail = DescribeAddress(embedded);
279
92
            subobj.pushKVs(std::move(detail));
280
92
            UniValue wallet_detail = std::visit(*this, embedded);
281
92
            subobj.pushKVs(std::move(wallet_detail));
282
92
            subobj.pushKV("address", EncodeDestination(embedded));
283
92
            subobj.pushKV("scriptPubKey", HexStr(subscript));
284
            // Always report the pubkey at the top level, so that `getnewaddress()['pubkey']` always works.
285
92
            if (subobj.exists("pubkey")) obj.pushKV("pubkey", subobj["pubkey"]);
286
92
            obj.pushKV("embedded", std::move(subobj));
287
92
        } else if (which_type == TxoutType::MULTISIG) {
288
            // Also report some information on multisig scripts (which do not have a corresponding address).
289
18
            obj.pushKV("sigsrequired", solutions_data[0][0]);
290
18
            UniValue pubkeys(UniValue::VARR);
291
66
            for (size_t i = 1; i < solutions_data.size() - 1; ++i) {
292
48
                CPubKey key(solutions_data[i].begin(), solutions_data[i].end());
293
48
                pubkeys.push_back(HexStr(key));
294
48
            }
295
18
            obj.pushKV("pubkeys", std::move(pubkeys));
296
18
        }
297
112
    }
298
299
706
    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
81
    {
306
81
        CKeyID keyID{ToKeyID(pkhash)};
307
81
        UniValue obj(UniValue::VOBJ);
308
81
        CPubKey vchPubKey;
309
81
        if (provider && provider->GetPubKey(keyID, vchPubKey)) {
310
77
            obj.pushKV("pubkey", HexStr(vchPubKey));
311
77
            obj.pushKV("iscompressed", vchPubKey.IsCompressed());
312
77
        }
313
81
        return obj;
314
81
    }
315
316
    // NOLINTNEXTLINE(misc-no-recursion)
317
    UniValue operator()(const ScriptHash& scripthash) const
318
90
    {
319
90
        UniValue obj(UniValue::VOBJ);
320
90
        CScript subscript;
321
90
        if (provider && provider->GetCScript(ToScriptID(scripthash), subscript)) {
322
88
            ProcessSubScript(subscript, obj);
323
88
        }
324
90
        return obj;
325
90
    }
326
327
    UniValue operator()(const WitnessV0KeyHash& id) const
328
471
    {
329
471
        UniValue obj(UniValue::VOBJ);
330
471
        CPubKey pubkey;
331
471
        if (provider && provider->GetPubKey(ToKeyID(id), pubkey)) {
332
402
            obj.pushKV("pubkey", HexStr(pubkey));
333
402
        }
334
471
        return obj;
335
471
    }
336
337
    // NOLINTNEXTLINE(misc-no-recursion)
338
    UniValue operator()(const WitnessV0ScriptHash& id) const
339
40
    {
340
40
        UniValue obj(UniValue::VOBJ);
341
40
        CScript subscript;
342
40
        CRIPEMD160 hasher;
343
40
        uint160 hash;
344
40
        hasher.Write(id.begin(), 32).Finalize(hash.begin());
345
40
        if (provider && provider->GetCScript(CScriptID(hash), subscript)) {
346
24
            ProcessSubScript(subscript, obj);
347
24
        }
348
40
        return obj;
349
40
    }
350
351
115
    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
706
{
358
706
    UniValue ret(UniValue::VOBJ);
359
706
    UniValue detail = DescribeAddress(dest);
360
706
    CScript script = GetScriptForDestination(dest);
361
706
    std::unique_ptr<SigningProvider> provider = nullptr;
362
706
    provider = wallet.GetSolvingProvider(script);
363
706
    ret.pushKVs(std::move(detail));
364
706
    ret.pushKVs(std::visit(DescribeWalletAddressVisitor(provider.get()), dest));
365
706
    return ret;
366
706
}
367
368
// NOLINTNEXTLINE(misc-no-recursion)
369
static std::vector<RPCResult> GetAddressInfoBaseFields()
370
3.06k
{
371
3.06k
    return {
372
3.06k
        {RPCResult::Type::STR, "address", /*optional=*/true, "The bitcoin address of the embedded script."},
373
3.06k
        {RPCResult::Type::STR_HEX, "scriptPubKey", /*optional=*/true, "The hex-encoded output script generated by the address."},
374
3.06k
        {RPCResult::Type::BOOL, "isscript", /*optional=*/true, "If the key is a script."},
375
3.06k
        {RPCResult::Type::BOOL, "iswitness", /*optional=*/true, "If the address is a witness address."},
376
3.06k
        {RPCResult::Type::NUM, "witness_version", /*optional=*/true, "The version number of the witness program."},
377
3.06k
        {RPCResult::Type::STR_HEX, "witness_program", /*optional=*/true, "The hex value of the witness program."},
378
3.06k
        {RPCResult::Type::STR, "script", /*optional=*/true,
379
3.06k
            "The output script type. Only if isscript is true and the redeemscript is known. Possible\n"
380
3.06k
            "types: nonstandard, pubkey, pubkeyhash, scripthash, multisig, nulldata, witness_v0_keyhash,\n"
381
3.06k
            "witness_v0_scripthash, witness_unknown."},
382
3.06k
        {RPCResult::Type::STR_HEX, "hex", /*optional=*/true, "The redeemscript for the p2sh address."},
383
3.06k
        {RPCResult::Type::ARR, "pubkeys", /*optional=*/true,
384
3.06k
            "Array of pubkeys associated with the known redeemscript (only if script is multisig).",
385
3.06k
            {
386
3.06k
                {RPCResult::Type::STR, "pubkey", ""},
387
3.06k
            }},
388
3.06k
        {RPCResult::Type::NUM, "sigsrequired", /*optional=*/true,
389
3.06k
            "The number of signatures required to spend multisig output (only if script is multisig)."},
390
3.06k
        {RPCResult::Type::STR_HEX, "pubkey", /*optional=*/true,
391
3.06k
            "The hex value of the raw public key for single-key addresses (possibly embedded in P2SH or P2WSH)."},
392
3.06k
        {RPCResult::Type::BOOL, "iscompressed", /*optional=*/true, "If the pubkey is compressed."},
393
3.06k
    };
394
3.06k
}
395
396
static std::vector<RPCResult> GetAddressInfoEmbeddedFields(bool include_nested)
397
1.53k
{
398
1.53k
    auto fields = GetAddressInfoBaseFields();
399
400
1.53k
    if (include_nested) {
401
1.53k
        auto nested = GetAddressInfoBaseFields();
402
1.53k
        fields.emplace_back(
403
1.53k
            RPCResult::Type::OBJ,
404
1.53k
            "embedded",
405
1.53k
            /*optional=*/true,
406
1.53k
            "Information about the address embedded in P2SH or P2WSH, if relevant and known.",
407
1.53k
            std::move(nested)
408
1.53k
        );
409
1.53k
    }
410
411
1.53k
    return fields;
412
1.53k
}
413
414
RPCMethod getaddressinfo()
415
1.53k
{
416
1.53k
    return RPCMethod{
417
1.53k
        "getaddressinfo",
418
1.53k
        "Return information about the given bitcoin address.\n"
419
1.53k
                "Some of the information will only be present if the address is in the active wallet.\n",
420
1.53k
                {
421
1.53k
                    {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The bitcoin address for which to get information."},
422
1.53k
                },
423
1.53k
                RPCResult{
424
1.53k
                    RPCResult::Type::OBJ, "", "",
425
1.53k
                    {
426
1.53k
                        {RPCResult::Type::STR, "address", "The bitcoin address validated."},
427
1.53k
                        {RPCResult::Type::STR_HEX, "scriptPubKey", "The hex-encoded output script generated by the address."},
428
1.53k
                        {RPCResult::Type::BOOL, "ismine", "If the address is yours."},
429
1.53k
                        {RPCResult::Type::BOOL, "iswatchonly", "(DEPRECATED) Always false."},
430
1.53k
                        {RPCResult::Type::BOOL, "solvable", "If we know how to spend coins sent to this address, ignoring the possible lack of private keys."},
431
1.53k
                        {RPCResult::Type::STR, "desc", /*optional=*/true, "A descriptor for spending coins sent to this address (only when solvable)."},
432
1.53k
                        {RPCResult::Type::STR, "parent_desc", /*optional=*/true, "The descriptor used to derive this address if this is a descriptor wallet"},
433
1.53k
                        {RPCResult::Type::BOOL, "isscript", /*optional=*/true, "If the key is a script."},
434
1.53k
                        {RPCResult::Type::BOOL, "ischange", "If the address was used for change output."},
435
1.53k
                        {RPCResult::Type::BOOL, "iswitness", "If the address is a witness address."},
436
1.53k
                        {RPCResult::Type::NUM, "witness_version", /*optional=*/true, "The version number of the witness program."},
437
1.53k
                        {RPCResult::Type::STR_HEX, "witness_program", /*optional=*/true, "The hex value of the witness program."},
438
1.53k
                        {RPCResult::Type::STR, "script", /*optional=*/true, "The output script type. Only if isscript is true and the redeemscript is known. Possible\n"
439
1.53k
                                                                     "types: nonstandard, pubkey, pubkeyhash, scripthash, multisig, nulldata, witness_v0_keyhash,\n"
440
1.53k
                            "witness_v0_scripthash, witness_unknown."},
441
1.53k
                        {RPCResult::Type::STR_HEX, "hex", /*optional=*/true, "The redeemscript for the p2sh address."},
442
1.53k
                        {RPCResult::Type::ARR, "pubkeys", /*optional=*/true, "Array of pubkeys associated with the known redeemscript (only if script is multisig).",
443
1.53k
                        {
444
1.53k
                            {RPCResult::Type::STR, "pubkey", ""},
445
1.53k
                        }},
446
1.53k
                        {RPCResult::Type::NUM, "sigsrequired", /*optional=*/true, "The number of signatures required to spend multisig output (only if script is multisig)."},
447
1.53k
                        {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)."},
448
1.53k
                        {RPCResult::Type::OBJ, "embedded", /*optional=*/true,
449
1.53k
                        "Information about the address embedded in P2SH or P2WSH, if relevant and known.",
450
1.53k
                        ElideGroup(
451
1.53k
                            GetAddressInfoEmbeddedFields(/*include_nested=*/true),
452
1.53k
                            "Includes all getaddressinfo output fields for the embedded address, excluding metadata (timestamp, hdkeypath, hdseedid)\n"
453
1.53k
                            "and relation to the wallet (ismine)."
454
1.53k
                        )},
455
1.53k
                        {RPCResult::Type::BOOL, "iscompressed", /*optional=*/true, "If the pubkey is compressed."},
456
1.53k
                        {RPCResult::Type::NUM_TIME, "timestamp", /*optional=*/true, "The creation time of the key, if available, expressed in " + UNIX_EPOCH_TIME + "."},
457
1.53k
                        {RPCResult::Type::STR, "hdkeypath", /*optional=*/true, "The HD keypath, if the key is HD and available."},
458
1.53k
                        {RPCResult::Type::STR_HEX, "hdseedid", /*optional=*/true, "The Hash160 of the HD seed."},
459
1.53k
                        {RPCResult::Type::STR_HEX, "hdmasterfingerprint", /*optional=*/true, "The fingerprint of the master key."},
460
1.53k
                        {RPCResult::Type::ARR, "labels", "Array of labels associated with the address. Currently limited to one label but returned\n"
461
1.53k
                            "as an array to keep the API stable if multiple labels are enabled in the future.",
462
1.53k
                        {
463
1.53k
                            {RPCResult::Type::STR, "label name", "Label name (defaults to \"\")."},
464
1.53k
                        }},
465
1.53k
                    }
466
1.53k
                },
467
1.53k
                RPCExamples{
468
1.53k
                    HelpExampleCli("getaddressinfo", "\"" + EXAMPLE_ADDRESS[0] + "\"") +
469
1.53k
                    HelpExampleRpc("getaddressinfo", "\"" + EXAMPLE_ADDRESS[0] + "\"")
470
1.53k
                },
471
1.53k
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
472
1.53k
{
473
711
    const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
474
711
    if (!pwallet) return UniValue::VNULL;
475
476
711
    LOCK(pwallet->cs_wallet);
477
478
711
    std::string error_msg;
479
711
    CTxDestination dest = DecodeDestination(request.params[0].get_str(), error_msg);
480
481
    // Make sure the destination is valid
482
711
    if (!IsValidDestination(dest)) {
483
        // Set generic error message in case 'DecodeDestination' didn't set it
484
5
        if (error_msg.empty()) error_msg = "Invalid address";
485
486
5
        throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, error_msg);
487
5
    }
488
489
706
    UniValue ret(UniValue::VOBJ);
490
491
706
    std::string currentAddress = EncodeDestination(dest);
492
706
    ret.pushKV("address", currentAddress);
493
494
706
    CScript scriptPubKey = GetScriptForDestination(dest);
495
706
    ret.pushKV("scriptPubKey", HexStr(scriptPubKey));
496
497
706
    std::unique_ptr<SigningProvider> provider = pwallet->GetSolvingProvider(scriptPubKey);
498
499
706
    bool mine = pwallet->IsMine(dest);
500
706
    ret.pushKV("ismine", mine);
501
502
706
    if (provider) {
503
615
        auto inferred = InferDescriptor(scriptPubKey, *provider);
504
615
        bool solvable = inferred->IsSolvable();
505
615
        ret.pushKV("solvable", solvable);
506
615
        if (solvable) {
507
611
            ret.pushKV("desc", inferred->ToString());
508
611
        }
509
615
    } else {
510
91
        ret.pushKV("solvable", false);
511
91
    }
512
513
706
    const auto& spk_mans = pwallet->GetScriptPubKeyMans(scriptPubKey);
514
    // In most cases there is only one matching ScriptPubKey manager and we can't resolve ambiguity in a better way
515
706
    ScriptPubKeyMan* spk_man{nullptr};
516
706
    if (spk_mans.size()) spk_man = *spk_mans.begin();
517
518
706
    DescriptorScriptPubKeyMan* desc_spk_man = dynamic_cast<DescriptorScriptPubKeyMan*>(spk_man);
519
706
    if (desc_spk_man) {
520
615
        std::string desc_str;
521
615
        if (desc_spk_man->GetDescriptorString(desc_str, /*priv=*/false)) {
522
615
            ret.pushKV("parent_desc", desc_str);
523
615
        }
524
615
    }
525
526
706
    ret.pushKV("iswatchonly", false);
527
528
706
    UniValue detail = DescribeWalletAddress(*pwallet, dest);
529
706
    ret.pushKVs(std::move(detail));
530
531
706
    ret.pushKV("ischange", ScriptIsChange(*pwallet, scriptPubKey));
532
533
706
    if (spk_man) {
534
615
        if (const std::unique_ptr<CKeyMetadata> meta = spk_man->GetMetadata(dest)) {
535
527
            ret.pushKV("timestamp", meta->nCreateTime);
536
527
            if (meta->has_key_origin) {
537
                // In legacy wallets hdkeypath has always used an apostrophe for
538
                // hardened derivation. Perhaps some external tool depends on that.
539
527
                ret.pushKV("hdkeypath", WriteHDKeypath(meta->key_origin.path, /*apostrophe=*/!desc_spk_man));
540
527
                ret.pushKV("hdseedid", meta->hd_seed_id.GetHex());
541
527
                ret.pushKV("hdmasterfingerprint", HexStr(meta->key_origin.fingerprint));
542
527
            }
543
527
        }
544
615
    }
545
546
    // Return a `labels` array containing the label associated with the address,
547
    // equivalent to the `label` field above. Currently only one label can be
548
    // associated with an address, but we return an array so the API remains
549
    // stable if we allow multiple labels to be associated with an address in
550
    // the future.
551
706
    UniValue labels(UniValue::VARR);
552
706
    const auto* address_book_entry = pwallet->FindAddressBookEntry(dest);
553
706
    if (address_book_entry) {
554
469
        labels.push_back(address_book_entry->GetLabel());
555
469
    }
556
706
    ret.pushKV("labels", std::move(labels));
557
558
706
    return ret;
559
711
},
560
1.53k
    };
561
1.53k
}
562
563
RPCMethod getaddressesbylabel()
564
864
{
565
864
    return RPCMethod{
566
864
        "getaddressesbylabel",
567
864
        "Returns the list of addresses assigned the specified label.\n",
568
864
                {
569
864
                    {"label", RPCArg::Type::STR, RPCArg::Optional::NO, "The label."},
570
864
                },
571
864
                RPCResult{
572
864
                    RPCResult::Type::OBJ_DYN, "", "json object with addresses as keys",
573
864
                    {
574
864
                        {RPCResult::Type::OBJ, "address", "json object with information about address",
575
864
                        {
576
864
                            {RPCResult::Type::STR, "purpose", "Purpose of address (\"send\" for sending address, \"receive\" for receiving address)"},
577
864
                        }},
578
864
                    }
579
864
                },
580
864
                RPCExamples{
581
864
                    HelpExampleCli("getaddressesbylabel", "\"tabby\"")
582
864
            + HelpExampleRpc("getaddressesbylabel", "\"tabby\"")
583
864
                },
584
864
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
585
864
{
586
43
    const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
587
43
    if (!pwallet) return UniValue::VNULL;
588
589
43
    LOCK(pwallet->cs_wallet);
590
591
43
    const std::string label{LabelFromValue(request.params[0])};
592
593
    // Find all addresses that have the given label
594
43
    UniValue ret(UniValue::VOBJ);
595
43
    std::set<std::string> addresses;
596
528
    pwallet->ForEachAddrBookEntry([&](const CTxDestination& _dest, const std::string& _label, bool _is_change, const std::optional<AddressPurpose>& _purpose) {
597
528
        if (_is_change) return;
598
528
        if (_label == label) {
599
65
            std::string address = EncodeDestination(_dest);
600
            // CWallet::m_address_book is not expected to contain duplicate
601
            // address strings, but build a separate set as a precaution just in
602
            // case it does.
603
65
            bool unique = addresses.emplace(address).second;
604
65
            CHECK_NONFATAL(unique);
605
            // UniValue::pushKV checks if the key exists in O(N)
606
            // and since duplicate addresses are unexpected (checked with
607
            // std::set in O(log(N))), UniValue::pushKVEnd is used instead,
608
            // which currently is O(1).
609
65
            UniValue value(UniValue::VOBJ);
610
65
            value.pushKV("purpose", _purpose ? PurposeToString(*_purpose) : "unknown");
611
65
            ret.pushKVEnd(address, std::move(value));
612
65
        }
613
528
    });
614
615
43
    if (ret.empty()) {
616
5
        throw JSONRPCError(RPC_WALLET_INVALID_LABEL_NAME, std::string("No addresses with label " + label));
617
5
    }
618
619
38
    return ret;
620
43
},
621
864
    };
622
864
}
623
624
RPCMethod listlabels()
625
865
{
626
865
    return RPCMethod{
627
865
        "listlabels",
628
865
        "Returns the list of all labels, or labels that are assigned to addresses with a specific purpose.\n",
629
865
                {
630
865
                    {"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."},
631
865
                },
632
865
                RPCResult{
633
865
                    RPCResult::Type::ARR, "", "",
634
865
                    {
635
865
                        {RPCResult::Type::STR, "label", "Label name"},
636
865
                    }
637
865
                },
638
865
                RPCExamples{
639
865
            "\nList all labels\n"
640
865
            + HelpExampleCli("listlabels", "") +
641
865
            "\nList labels that have receiving addresses\n"
642
865
            + HelpExampleCli("listlabels", "receive") +
643
865
            "\nList labels that have sending addresses\n"
644
865
            + HelpExampleCli("listlabels", "send") +
645
865
            "\nAs a JSON-RPC call\n"
646
865
            + HelpExampleRpc("listlabels", "receive")
647
865
                },
648
865
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
649
865
{
650
44
    const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
651
44
    if (!pwallet) return UniValue::VNULL;
652
653
44
    LOCK(pwallet->cs_wallet);
654
655
44
    std::optional<AddressPurpose> purpose;
656
44
    if (!request.params[0].isNull()) {
657
8
        std::string purpose_str = request.params[0].get_str();
658
8
        if (!purpose_str.empty()) {
659
8
            purpose = PurposeFromString(purpose_str);
660
8
            if (!purpose) {
661
2
                throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid 'purpose' argument, must be a known purpose string, typically 'send', or 'receive'.");
662
2
            }
663
8
        }
664
8
    }
665
666
    // Add to a set to sort by label name, then insert into Univalue array
667
42
    std::set<std::string> label_set = pwallet->ListAddrBookLabels(purpose);
668
669
42
    UniValue ret(UniValue::VARR);
670
229
    for (const std::string& name : label_set) {
671
229
        ret.push_back(name);
672
229
    }
673
674
42
    return ret;
675
44
},
676
865
    };
677
865
}
678
679
680
#ifdef ENABLE_EXTERNAL_SIGNER
681
RPCMethod walletdisplayaddress()
682
826
{
683
826
    return RPCMethod{
684
826
        "walletdisplayaddress",
685
826
        "Display address on an external signer for verification.",
686
826
        {
687
826
            {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "bitcoin address to display"},
688
826
        },
689
826
        RPCResult{
690
826
            RPCResult::Type::OBJ,"","",
691
826
            {
692
826
                {RPCResult::Type::STR, "address", "The address as confirmed by the signer"},
693
826
            }
694
826
        },
695
826
        RPCExamples{""},
696
826
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
697
826
        {
698
5
            std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
699
5
            if (!wallet) return UniValue::VNULL;
700
5
            CWallet* const pwallet = wallet.get();
701
702
5
            LOCK(pwallet->cs_wallet);
703
704
5
            CTxDestination dest = DecodeDestination(request.params[0].get_str());
705
706
            // Make sure the destination is valid
707
5
            if (!IsValidDestination(dest)) {
708
0
                throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address");
709
0
            }
710
711
5
            util::Result<void> res = pwallet->DisplayAddress(dest);
712
5
            if (!res) throw JSONRPCError(RPC_MISC_ERROR, util::ErrorString(res).original);
713
714
4
            UniValue result(UniValue::VOBJ);
715
4
            result.pushKV("address", request.params[0].get_str());
716
4
            return result;
717
5
        }
718
826
    };
719
826
}
720
#endif // ENABLE_EXTERNAL_SIGNER
721
} // namespace wallet