Coverage Report

Created: 2026-09-02 14:16

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