Coverage Report

Created: 2026-09-14 20:36

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
12.0k
{
23
12.0k
    return RPCMethod{
24
12.0k
        "getnewaddress",
25
12.0k
        "Returns a new Bitcoin address for receiving payments.\n"
26
12.0k
                "If 'label' is specified, it is added to the address book \n"
27
12.0k
                "so payments received with the address will be associated with 'label'.\n",
28
12.0k
                {
29
12.0k
                    {"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
12.0k
                    {"address_type", RPCArg::Type::STR, RPCArg::DefaultHint{"set by -addresstype"}, "The address type to use. Options are " + FormatAllOutputTypes() + "."},
31
12.0k
                },
32
12.0k
                RPCResult{
33
12.0k
                    RPCResult::Type::STR, "address", "The new bitcoin address"
34
12.0k
                },
35
12.0k
                RPCExamples{
36
12.0k
                    HelpExampleCli("getnewaddress", "")
37
12.0k
            + HelpExampleRpc("getnewaddress", "")
38
12.0k
                },
39
12.0k
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
40
12.0k
{
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.06k
        std::optional<OutputType> parsed = ParseOutputType(request.params[1].get_str());
56
5.06k
        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.06k
        output_type = parsed.value();
60
5.06k
    }
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
12.0k
    };
70
12.0k
}
71
72
RPCMethod getrawchangeaddress()
73
1.21k
{
74
1.21k
    return RPCMethod{
75
1.21k
        "getrawchangeaddress",
76
1.21k
        "Returns a new Bitcoin address, for receiving change.\n"
77
1.21k
                "This is for use with raw transactions, NOT normal use.\n",
78
1.21k
                {
79
1.21k
                    {"address_type", RPCArg::Type::STR, RPCArg::DefaultHint{"set by -changetype"}, "The address type to use. Options are " + FormatAllOutputTypes() + "."},
80
1.21k
                },
81
1.21k
                RPCResult{
82
1.21k
                    RPCResult::Type::STR, "address", "The address"
83
1.21k
                },
84
1.21k
                RPCExamples{
85
1.21k
                    HelpExampleCli("getrawchangeaddress", "")
86
1.21k
            + HelpExampleRpc("getrawchangeaddress", "")
87
1.21k
                },
88
1.21k
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
89
1.21k
{
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.21k
    };
115
1.21k
}
116
117
118
RPCMethod setlabel()
119
863
{
120
863
    return RPCMethod{
121
863
        "setlabel",
122
863
        "Sets the label associated with the given address.\n",
123
863
                {
124
863
                    {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The bitcoin address to be associated with a label."},
125
863
                    {"label", RPCArg::Type::STR, RPCArg::Optional::NO, "The label to assign to the address."},
126
863
                },
127
863
                RPCResult{RPCResult::Type::NONE, "", ""},
128
863
                RPCExamples{
129
863
                    HelpExampleCli("setlabel", "\"" + EXAMPLE_ADDRESS[0] + "\" \"tabby\"")
130
863
            + HelpExampleRpc("setlabel", "\"" + EXAMPLE_ADDRESS[0] + "\", \"tabby\"")
131
863
                },
132
863
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
133
863
{
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
863
    };
155
863
}
156
157
RPCMethod listaddressgroupings()
158
847
{
159
847
    return RPCMethod{
160
847
        "listaddressgroupings",
161
847
        "Lists groups of addresses which have had their common ownership\n"
162
847
                "made public by common use as inputs or as the resulting change\n"
163
847
                "in past transactions\n",
164
847
                {},
165
847
                RPCResult{
166
847
                    RPCResult::Type::ARR, "", "",
167
847
                    {
168
847
                        {RPCResult::Type::ARR, "", "",
169
847
                        {
170
847
                            {RPCResult::Type::ARR_FIXED, "", "",
171
847
                            {
172
847
                                {RPCResult::Type::STR, "address", "The bitcoin address"},
173
847
                                {RPCResult::Type::STR_AMOUNT, "amount", "The amount in " + CURRENCY_UNIT},
174
847
                                {RPCResult::Type::STR, "label", /*optional=*/true, "The label"},
175
847
                            }},
176
847
                        }},
177
847
                    }
178
847
                },
179
847
                RPCExamples{
180
847
                    HelpExampleCli("listaddressgroupings", "")
181
847
            + HelpExampleRpc("listaddressgroupings", "")
182
847
                },
183
847
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
184
847
{
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
847
    };
216
847
}
217
218
RPCMethod keypoolrefill()
219
855
{
220
855
    return RPCMethod{"keypoolrefill",
221
855
                "Refills each descriptor keypool in the wallet up to the specified number of new keys.\n"
222
855
                "By default, descriptor wallets have 4 active ranged descriptors (" + FormatAllOutputTypes() + "), each with " + util::ToString(DEFAULT_KEYPOOL_SIZE) + " entries.\n" +
223
855
        HELP_REQUIRING_PASSPHRASE,
224
855
                {
225
855
                    {"newsize", RPCArg::Type::NUM, RPCArg::DefaultHint{strprintf("%u, or as set by -keypool", DEFAULT_KEYPOOL_SIZE)}, "The new keypool size"},
226
855
                },
227
855
                RPCResult{RPCResult::Type::NONE, "", ""},
228
855
                RPCExamples{
229
855
                    HelpExampleCli("keypoolrefill", "")
230
855
            + HelpExampleRpc("keypoolrefill", "")
231
855
                },
232
855
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
233
855
{
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
855
    };
258
855
}
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
116
    {
268
        // Always present: script type and redeemscript
269
116
        std::vector<std::vector<unsigned char>> solutions_data;
270
116
        TxoutType which_type = Solver(subscript, solutions_data);
271
116
        obj.pushKV("script", GetTxnOutputType(which_type));
272
116
        obj.pushKV("hex", HexStr(subscript));
273
274
116
        CTxDestination embedded;
275
116
        if (ExtractDestination(subscript, embedded)) {
276
            // Only when the script corresponds to an address.
277
96
            UniValue subobj(UniValue::VOBJ);
278
96
            UniValue detail = DescribeAddress(embedded);
279
96
            subobj.pushKVs(std::move(detail));
280
96
            UniValue wallet_detail = std::visit(*this, embedded);
281
96
            subobj.pushKVs(std::move(wallet_detail));
282
96
            subobj.pushKV("address", EncodeDestination(embedded));
283
96
            subobj.pushKV("scriptPubKey", HexStr(subscript));
284
            // Always report the pubkey at the top level, so that `getnewaddress()['pubkey']` always works.
285
96
            if (subobj.exists("pubkey")) obj.pushKV("pubkey", subobj["pubkey"]);
286
96
            obj.pushKV("embedded", std::move(subobj));
287
96
        } 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
116
    }
298
299
737
    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
94
    {
319
94
        UniValue obj(UniValue::VOBJ);
320
94
        CScript subscript;
321
94
        if (provider && provider->GetCScript(ToScriptID(scripthash), subscript)) {
322
92
            ProcessSubScript(subscript, obj);
323
92
        }
324
94
        return obj;
325
94
    }
326
327
    UniValue operator()(const WitnessV0KeyHash& id) const
328
470
    {
329
470
        UniValue obj(UniValue::VOBJ);
330
470
        CPubKey pubkey;
331
470
        if (provider && provider->GetPubKey(ToKeyID(id), pubkey)) {
332
405
            obj.pushKV("pubkey", HexStr(pubkey));
333
405
        }
334
470
        return obj;
335
470
    }
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
737
{
358
737
    UniValue ret(UniValue::VOBJ);
359
737
    UniValue detail = DescribeAddress(dest);
360
737
    CScript script = GetScriptForDestination(dest);
361
737
    std::unique_ptr<SigningProvider> provider = nullptr;
362
737
    provider = wallet.GetSolvingProvider(script);
363
737
    ret.pushKVs(std::move(detail));
364
737
    ret.pushKVs(std::visit(DescribeWalletAddressVisitor(provider.get()), dest));
365
737
    return ret;
366
737
}
367
368
static std::vector<RPCResult> GetAddressInfoBaseFields()
369
3.17k
{
370
3.17k
    return {
371
3.17k
        {RPCResult::Type::STR, "address", /*optional=*/true, "The bitcoin address of the embedded script."},
372
3.17k
        {RPCResult::Type::STR_HEX, "scriptPubKey", /*optional=*/true, "The hex-encoded output script generated by the address."},
373
3.17k
        {RPCResult::Type::BOOL, "isscript", /*optional=*/true, "If the key is a script."},
374
3.17k
        {RPCResult::Type::BOOL, "iswitness", /*optional=*/true, "If the address is a witness address."},
375
3.17k
        {RPCResult::Type::NUM, "witness_version", /*optional=*/true, "The version number of the witness program."},
376
3.17k
        {RPCResult::Type::STR_HEX, "witness_program", /*optional=*/true, "The hex value of the witness program."},
377
3.17k
        {RPCResult::Type::STR, "script", /*optional=*/true,
378
3.17k
            "The output script type. Only if isscript is true and the redeemscript is known. Possible\n"
379
3.17k
            "types: nonstandard, pubkey, pubkeyhash, scripthash, multisig, nulldata, witness_v0_keyhash,\n"
380
3.17k
            "witness_v0_scripthash, witness_unknown."},
381
3.17k
        {RPCResult::Type::STR_HEX, "hex", /*optional=*/true, "The redeemscript for the p2sh address."},
382
3.17k
        {RPCResult::Type::ARR, "pubkeys", /*optional=*/true,
383
3.17k
            "Array of pubkeys associated with the known redeemscript (only if script is multisig).",
384
3.17k
            {
385
3.17k
                {RPCResult::Type::STR, "pubkey", ""},
386
3.17k
            }},
387
3.17k
        {RPCResult::Type::NUM, "sigsrequired", /*optional=*/true,
388
3.17k
            "The number of signatures required to spend multisig output (only if script is multisig)."},
389
3.17k
        {RPCResult::Type::STR_HEX, "pubkey", /*optional=*/true,
390
3.17k
            "The hex value of the raw public key for single-key addresses (possibly embedded in P2SH or P2WSH)."},
391
3.17k
        {RPCResult::Type::BOOL, "iscompressed", /*optional=*/true, "If the pubkey is compressed."},
392
3.17k
    };
393
3.17k
}
394
395
static std::vector<RPCResult> GetAddressInfoEmbeddedFields(bool include_nested)
396
1.58k
{
397
1.58k
    auto fields = GetAddressInfoBaseFields();
398
399
1.58k
    if (include_nested) {
400
1.58k
        auto nested = GetAddressInfoBaseFields();
401
1.58k
        fields.emplace_back(
402
1.58k
            RPCResult::Type::OBJ,
403
1.58k
            "embedded",
404
1.58k
            /*optional=*/true,
405
1.58k
            "Information about the address embedded in P2SH or P2WSH, if relevant and known.",
406
1.58k
            std::move(nested)
407
1.58k
        );
408
1.58k
    }
409
410
1.58k
    return fields;
411
1.58k
}
412
413
RPCMethod getaddressinfo()
414
1.58k
{
415
1.58k
    return RPCMethod{
416
1.58k
        "getaddressinfo",
417
1.58k
        "Return information about the given bitcoin address.\n"
418
1.58k
                "Some of the information will only be present if the address is in the active wallet.\n",
419
1.58k
                {
420
1.58k
                    {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The bitcoin address for which to get information."},
421
1.58k
                },
422
1.58k
                RPCResult{
423
1.58k
                    RPCResult::Type::OBJ, "", "",
424
1.58k
                    {
425
1.58k
                        {RPCResult::Type::STR, "address", "The bitcoin address validated."},
426
1.58k
                        {RPCResult::Type::STR_HEX, "scriptPubKey", "The hex-encoded output script generated by the address."},
427
1.58k
                        {RPCResult::Type::BOOL, "ismine", "If the address is yours."},
428
1.58k
                        {RPCResult::Type::BOOL, "iswatchonly", "(DEPRECATED) Always false."},
429
1.58k
                        {RPCResult::Type::BOOL, "solvable", "If we know how to spend coins sent to this address, ignoring the possible lack of private keys."},
430
1.58k
                        {RPCResult::Type::STR, "desc", /*optional=*/true, "A descriptor for spending coins sent to this address (only when solvable)."},
431
1.58k
                        {RPCResult::Type::STR, "parent_desc", /*optional=*/true, "The descriptor used to derive this address if this is a descriptor wallet"},
432
1.58k
                        {RPCResult::Type::BOOL, "isscript", /*optional=*/true, "If the key is a script."},
433
1.58k
                        {RPCResult::Type::BOOL, "ischange", "If the address was used for change output."},
434
1.58k
                        {RPCResult::Type::BOOL, "iswitness", "If the address is a witness address."},
435
1.58k
                        {RPCResult::Type::NUM, "witness_version", /*optional=*/true, "The version number of the witness program."},
436
1.58k
                        {RPCResult::Type::STR_HEX, "witness_program", /*optional=*/true, "The hex value of the witness program."},
437
1.58k
                        {RPCResult::Type::STR, "script", /*optional=*/true, "The output script type. Only if isscript is true and the redeemscript is known. Possible\n"
438
1.58k
                                                                     "types: nonstandard, pubkey, pubkeyhash, scripthash, multisig, nulldata, witness_v0_keyhash,\n"
439
1.58k
                            "witness_v0_scripthash, witness_unknown."},
440
1.58k
                        {RPCResult::Type::STR_HEX, "hex", /*optional=*/true, "The redeemscript for the p2sh address."},
441
1.58k
                        {RPCResult::Type::ARR, "pubkeys", /*optional=*/true, "Array of pubkeys associated with the known redeemscript (only if script is multisig).",
442
1.58k
                        {
443
1.58k
                            {RPCResult::Type::STR, "pubkey", ""},
444
1.58k
                        }},
445
1.58k
                        {RPCResult::Type::NUM, "sigsrequired", /*optional=*/true, "The number of signatures required to spend multisig output (only if script is multisig)."},
446
1.58k
                        {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.58k
                        {RPCResult::Type::OBJ, "embedded", /*optional=*/true,
448
1.58k
                        "Information about the address embedded in P2SH or P2WSH, if relevant and known.",
449
1.58k
                        ElideGroup(
450
1.58k
                            GetAddressInfoEmbeddedFields(/*include_nested=*/true),
451
1.58k
                            "Includes all getaddressinfo output fields for the embedded address, excluding metadata (timestamp, hdkeypath, hdseedid)\n"
452
1.58k
                            "and relation to the wallet (ismine)."
453
1.58k
                        )},
454
1.58k
                        {RPCResult::Type::BOOL, "iscompressed", /*optional=*/true, "If the pubkey is compressed."},
455
1.58k
                        {RPCResult::Type::NUM_TIME, "timestamp", /*optional=*/true, "The creation time of the key, if available, expressed in " + UNIX_EPOCH_TIME + "."},
456
1.58k
                        {RPCResult::Type::STR, "hdkeypath", /*optional=*/true, "The HD keypath, if the key is HD and available."},
457
1.58k
                        {RPCResult::Type::STR_HEX, "hdseedid", /*optional=*/true, "The Hash160 of the HD seed."},
458
1.58k
                        {RPCResult::Type::STR_HEX, "hdmasterfingerprint", /*optional=*/true, "The fingerprint of the master key."},
459
1.58k
                        {RPCResult::Type::ARR, "labels", "Array of labels associated with the address. Currently limited to one label but returned\n"
460
1.58k
                            "as an array to keep the API stable if multiple labels are enabled in the future.",
461
1.58k
                        {
462
1.58k
                            {RPCResult::Type::STR, "label name", "Label name (defaults to \"\")."},
463
1.58k
                        }},
464
1.58k
                    }
465
1.58k
                },
466
1.58k
                RPCExamples{
467
1.58k
                    HelpExampleCli("getaddressinfo", "\"" + EXAMPLE_ADDRESS[0] + "\"") +
468
1.58k
                    HelpExampleRpc("getaddressinfo", "\"" + EXAMPLE_ADDRESS[0] + "\"")
469
1.58k
                },
470
1.58k
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
471
1.58k
{
472
742
    const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
473
742
    if (!pwallet) return UniValue::VNULL;
474
475
742
    LOCK(pwallet->cs_wallet);
476
477
742
    std::string error_msg;
478
742
    CTxDestination dest = DecodeDestination(request.params[0].get_str(), error_msg);
479
480
    // Make sure the destination is valid
481
742
    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
737
    UniValue ret(UniValue::VOBJ);
489
490
737
    std::string currentAddress = EncodeDestination(dest);
491
737
    ret.pushKV("address", currentAddress);
492
493
737
    CScript scriptPubKey = GetScriptForDestination(dest);
494
737
    ret.pushKV("scriptPubKey", HexStr(scriptPubKey));
495
496
737
    std::unique_ptr<SigningProvider> provider = pwallet->GetSolvingProvider(scriptPubKey);
497
498
737
    bool mine = pwallet->IsMine(dest);
499
737
    ret.pushKV("ismine", mine);
500
501
737
    if (provider) {
502
650
        auto inferred = InferDescriptor(scriptPubKey, *provider);
503
650
        bool solvable = inferred->IsSolvable();
504
650
        ret.pushKV("solvable", solvable);
505
650
        if (solvable) {
506
646
            ret.pushKV("desc", inferred->ToString());
507
646
        }
508
650
    } else {
509
87
        ret.pushKV("solvable", false);
510
87
    }
511
512
737
    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
737
    ScriptPubKeyMan* spk_man{nullptr};
515
737
    if (spk_mans.size()) spk_man = *spk_mans.begin();
516
517
737
    DescriptorScriptPubKeyMan* desc_spk_man = dynamic_cast<DescriptorScriptPubKeyMan*>(spk_man);
518
737
    if (desc_spk_man) {
519
650
        std::string desc_str;
520
650
        if (desc_spk_man->GetDescriptorString(desc_str, /*priv=*/false)) {
521
650
            ret.pushKV("parent_desc", desc_str);
522
650
        }
523
650
    }
524
525
737
    ret.pushKV("iswatchonly", false);
526
527
737
    UniValue detail = DescribeWalletAddress(*pwallet, dest);
528
737
    ret.pushKVs(std::move(detail));
529
530
737
    ret.pushKV("ischange", ScriptIsChange(*pwallet, scriptPubKey));
531
532
737
    if (spk_man) {
533
650
        if (const std::unique_ptr<CKeyMetadata> meta = spk_man->GetMetadata(dest)) {
534
562
            ret.pushKV("timestamp", meta->nCreateTime);
535
562
            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
562
                ret.pushKV("hdkeypath", WriteHDKeypath(meta->key_origin.path, /*apostrophe=*/!desc_spk_man));
539
562
                ret.pushKV("hdseedid", meta->hd_seed_id.GetHex());
540
562
                ret.pushKV("hdmasterfingerprint", HexStr(meta->key_origin.fingerprint));
541
562
            }
542
562
        }
543
650
    }
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
737
    UniValue labels(UniValue::VARR);
551
737
    const auto* address_book_entry = pwallet->FindAddressBookEntry(dest);
552
737
    if (address_book_entry) {
553
493
        labels.push_back(address_book_entry->GetLabel());
554
493
    }
555
737
    ret.pushKV("labels", std::move(labels));
556
557
737
    return ret;
558
742
},
559
1.58k
    };
560
1.58k
}
561
562
RPCMethod getaddressesbylabel()
563
888
{
564
888
    return RPCMethod{
565
888
        "getaddressesbylabel",
566
888
        "Returns the list of addresses assigned the specified label.\n",
567
888
                {
568
888
                    {"label", RPCArg::Type::STR, RPCArg::Optional::NO, "The label."},
569
888
                },
570
888
                RPCResult{
571
888
                    RPCResult::Type::OBJ_DYN, "", "json object with addresses as keys",
572
888
                    {
573
888
                        {RPCResult::Type::OBJ, "address", "json object with information about address",
574
888
                        {
575
888
                            {RPCResult::Type::STR, "purpose", "Purpose of address (\"send\" for sending address, \"receive\" for receiving address)"},
576
888
                        }},
577
888
                    }
578
888
                },
579
888
                RPCExamples{
580
888
                    HelpExampleCli("getaddressesbylabel", "\"tabby\"")
581
888
            + HelpExampleRpc("getaddressesbylabel", "\"tabby\"")
582
888
                },
583
888
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
584
888
{
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
888
    };
621
888
}
622
623
RPCMethod listlabels()
624
889
{
625
889
    return RPCMethod{
626
889
        "listlabels",
627
889
        "Returns the list of all labels, or labels that are assigned to addresses with a specific purpose.\n",
628
889
                {
629
889
                    {"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
889
                },
631
889
                RPCResult{
632
889
                    RPCResult::Type::ARR, "", "",
633
889
                    {
634
889
                        {RPCResult::Type::STR, "label", "Label name"},
635
889
                    }
636
889
                },
637
889
                RPCExamples{
638
889
            "\nList all labels\n"
639
889
            + HelpExampleCli("listlabels", "") +
640
889
            "\nList labels that have receiving addresses\n"
641
889
            + HelpExampleCli("listlabels", "receive") +
642
889
            "\nList labels that have sending addresses\n"
643
889
            + HelpExampleCli("listlabels", "send") +
644
889
            "\nAs a JSON-RPC call\n"
645
889
            + HelpExampleRpc("listlabels", R"("receive")")
646
889
                },
647
889
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
648
889
{
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
889
    };
676
889
}
677
678
679
#ifdef ENABLE_EXTERNAL_SIGNER
680
RPCMethod walletdisplayaddress()
681
850
{
682
850
    return RPCMethod{
683
850
        "walletdisplayaddress",
684
850
        "Display address on an external signer for verification.",
685
850
        {
686
850
            {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "bitcoin address to display"},
687
850
        },
688
850
        RPCResult{
689
850
            RPCResult::Type::OBJ,"","",
690
850
            {
691
850
                {RPCResult::Type::STR, "address", "The address as confirmed by the signer"},
692
850
            }
693
850
        },
694
850
        RPCExamples{""},
695
850
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
696
850
        {
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
850
    };
718
850
}
719
#endif // ENABLE_EXTERNAL_SIGNER
720
} // namespace wallet