Coverage Report

Created: 2026-08-14 20:23

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.19k
{
74
1.19k
    return RPCMethod{
75
1.19k
        "getrawchangeaddress",
76
1.19k
        "Returns a new Bitcoin address, for receiving change.\n"
77
1.19k
                "This is for use with raw transactions, NOT normal use.\n",
78
1.19k
                {
79
1.19k
                    {"address_type", RPCArg::Type::STR, RPCArg::DefaultHint{"set by -changetype"}, "The address type to use. Options are " + FormatAllOutputTypes() + "."},
80
1.19k
                },
81
1.19k
                RPCResult{
82
1.19k
                    RPCResult::Type::STR, "address", "The address"
83
1.19k
                },
84
1.19k
                RPCExamples{
85
1.19k
                    HelpExampleCli("getrawchangeaddress", "")
86
1.19k
            + HelpExampleRpc("getrawchangeaddress", "")
87
1.19k
                },
88
1.19k
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
89
1.19k
{
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.19k
    };
115
1.19k
}
116
117
118
RPCMethod setlabel()
119
849
{
120
849
    return RPCMethod{
121
849
        "setlabel",
122
849
        "Sets the label associated with the given address.\n",
123
849
                {
124
849
                    {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The bitcoin address to be associated with a label."},
125
849
                    {"label", RPCArg::Type::STR, RPCArg::Optional::NO, "The label to assign to the address."},
126
849
                },
127
849
                RPCResult{RPCResult::Type::NONE, "", ""},
128
849
                RPCExamples{
129
849
                    HelpExampleCli("setlabel", "\"" + EXAMPLE_ADDRESS[0] + "\" \"tabby\"")
130
849
            + HelpExampleRpc("setlabel", "\"" + EXAMPLE_ADDRESS[0] + "\", \"tabby\"")
131
849
                },
132
849
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
133
849
{
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
849
    };
155
849
}
156
157
RPCMethod listaddressgroupings()
158
833
{
159
833
    return RPCMethod{
160
833
        "listaddressgroupings",
161
833
        "Lists groups of addresses which have had their common ownership\n"
162
833
                "made public by common use as inputs or as the resulting change\n"
163
833
                "in past transactions\n",
164
833
                {},
165
833
                RPCResult{
166
833
                    RPCResult::Type::ARR, "", "",
167
833
                    {
168
833
                        {RPCResult::Type::ARR, "", "",
169
833
                        {
170
833
                            {RPCResult::Type::ARR_FIXED, "", "",
171
833
                            {
172
833
                                {RPCResult::Type::STR, "address", "The bitcoin address"},
173
833
                                {RPCResult::Type::STR_AMOUNT, "amount", "The amount in " + CURRENCY_UNIT},
174
833
                                {RPCResult::Type::STR, "label", /*optional=*/true, "The label"},
175
833
                            }},
176
833
                        }},
177
833
                    }
178
833
                },
179
833
                RPCExamples{
180
833
                    HelpExampleCli("listaddressgroupings", "")
181
833
            + HelpExampleRpc("listaddressgroupings", "")
182
833
                },
183
833
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
184
833
{
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
833
    };
216
833
}
217
218
RPCMethod keypoolrefill()
219
841
{
220
841
    return RPCMethod{"keypoolrefill",
221
841
                "Refills each descriptor keypool in the wallet up to the specified number of new keys.\n"
222
841
                "By default, descriptor wallets have 4 active ranged descriptors (" + FormatAllOutputTypes() + "), each with " + util::ToString(DEFAULT_KEYPOOL_SIZE) + " entries.\n" +
223
841
        HELP_REQUIRING_PASSPHRASE,
224
841
                {
225
841
                    {"newsize", RPCArg::Type::NUM, RPCArg::DefaultHint{strprintf("%u, or as set by -keypool", DEFAULT_KEYPOOL_SIZE)}, "The new keypool size"},
226
841
                },
227
841
                RPCResult{RPCResult::Type::NONE, "", ""},
228
841
                RPCExamples{
229
841
                    HelpExampleCli("keypoolrefill", "")
230
841
            + HelpExampleRpc("keypoolrefill", "")
231
841
                },
232
841
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
233
841
{
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
841
    };
258
841
}
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
740
    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
109
    {
306
109
        CKeyID keyID{ToKeyID(pkhash)};
307
109
        UniValue obj(UniValue::VOBJ);
308
109
        CPubKey vchPubKey;
309
109
        if (provider && provider->GetPubKey(keyID, vchPubKey)) {
310
105
            obj.pushKV("pubkey", HexStr(vchPubKey));
311
105
            obj.pushKV("iscompressed", vchPubKey.IsCompressed());
312
105
        }
313
109
        return obj;
314
109
    }
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
481
    {
329
481
        UniValue obj(UniValue::VOBJ);
330
481
        CPubKey pubkey;
331
481
        if (provider && provider->GetPubKey(ToKeyID(id), pubkey)) {
332
410
            obj.pushKV("pubkey", HexStr(pubkey));
333
410
        }
334
481
        return obj;
335
481
    }
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
740
{
358
740
    UniValue ret(UniValue::VOBJ);
359
740
    UniValue detail = DescribeAddress(dest);
360
740
    CScript script = GetScriptForDestination(dest);
361
740
    std::unique_ptr<SigningProvider> provider = nullptr;
362
740
    provider = wallet.GetSolvingProvider(script);
363
740
    ret.pushKVs(std::move(detail));
364
740
    ret.pushKVs(std::visit(DescribeWalletAddressVisitor(provider.get()), dest));
365
740
    return ret;
366
740
}
367
368
static std::vector<RPCResult> GetAddressInfoBaseFields()
369
3.15k
{
370
3.15k
    return {
371
3.15k
        {RPCResult::Type::STR, "address", /*optional=*/true, "The bitcoin address of the embedded script."},
372
3.15k
        {RPCResult::Type::STR_HEX, "scriptPubKey", /*optional=*/true, "The hex-encoded output script generated by the address."},
373
3.15k
        {RPCResult::Type::BOOL, "isscript", /*optional=*/true, "If the key is a script."},
374
3.15k
        {RPCResult::Type::BOOL, "iswitness", /*optional=*/true, "If the address is a witness address."},
375
3.15k
        {RPCResult::Type::NUM, "witness_version", /*optional=*/true, "The version number of the witness program."},
376
3.15k
        {RPCResult::Type::STR_HEX, "witness_program", /*optional=*/true, "The hex value of the witness program."},
377
3.15k
        {RPCResult::Type::STR, "script", /*optional=*/true,
378
3.15k
            "The output script type. Only if isscript is true and the redeemscript is known. Possible\n"
379
3.15k
            "types: nonstandard, pubkey, pubkeyhash, scripthash, multisig, nulldata, witness_v0_keyhash,\n"
380
3.15k
            "witness_v0_scripthash, witness_unknown."},
381
3.15k
        {RPCResult::Type::STR_HEX, "hex", /*optional=*/true, "The redeemscript for the p2sh address."},
382
3.15k
        {RPCResult::Type::ARR, "pubkeys", /*optional=*/true,
383
3.15k
            "Array of pubkeys associated with the known redeemscript (only if script is multisig).",
384
3.15k
            {
385
3.15k
                {RPCResult::Type::STR, "pubkey", ""},
386
3.15k
            }},
387
3.15k
        {RPCResult::Type::NUM, "sigsrequired", /*optional=*/true,
388
3.15k
            "The number of signatures required to spend multisig output (only if script is multisig)."},
389
3.15k
        {RPCResult::Type::STR_HEX, "pubkey", /*optional=*/true,
390
3.15k
            "The hex value of the raw public key for single-key addresses (possibly embedded in P2SH or P2WSH)."},
391
3.15k
        {RPCResult::Type::BOOL, "iscompressed", /*optional=*/true, "If the pubkey is compressed."},
392
3.15k
    };
393
3.15k
}
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
745
    const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
473
745
    if (!pwallet) return UniValue::VNULL;
474
475
745
    LOCK(pwallet->cs_wallet);
476
477
745
    std::string error_msg;
478
745
    CTxDestination dest = DecodeDestination(request.params[0].get_str(), error_msg);
479
480
    // Make sure the destination is valid
481
745
    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
740
    UniValue ret(UniValue::VOBJ);
489
490
740
    std::string currentAddress = EncodeDestination(dest);
491
740
    ret.pushKV("address", currentAddress);
492
493
740
    CScript scriptPubKey = GetScriptForDestination(dest);
494
740
    ret.pushKV("scriptPubKey", HexStr(scriptPubKey));
495
496
740
    std::unique_ptr<SigningProvider> provider = pwallet->GetSolvingProvider(scriptPubKey);
497
498
740
    bool mine = pwallet->IsMine(dest);
499
740
    ret.pushKV("ismine", mine);
500
501
740
    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
93
        ret.pushKV("solvable", false);
510
93
    }
511
512
740
    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
740
    ScriptPubKeyMan* spk_man{nullptr};
515
740
    if (spk_mans.size()) spk_man = *spk_mans.begin();
516
517
740
    DescriptorScriptPubKeyMan* desc_spk_man = dynamic_cast<DescriptorScriptPubKeyMan*>(spk_man);
518
740
    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
740
    ret.pushKV("iswatchonly", false);
526
527
740
    UniValue detail = DescribeWalletAddress(*pwallet, dest);
528
740
    ret.pushKVs(std::move(detail));
529
530
740
    ret.pushKV("ischange", ScriptIsChange(*pwallet, scriptPubKey));
531
532
740
    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
740
    UniValue labels(UniValue::VARR);
551
740
    const auto* address_book_entry = pwallet->FindAddressBookEntry(dest);
552
740
    if (address_book_entry) {
553
490
        labels.push_back(address_book_entry->GetLabel());
554
490
    }
555
740
    ret.pushKV("labels", std::move(labels));
556
557
740
    return ret;
558
745
},
559
1.57k
    };
560
1.57k
}
561
562
RPCMethod getaddressesbylabel()
563
874
{
564
874
    return RPCMethod{
565
874
        "getaddressesbylabel",
566
874
        "Returns the list of addresses assigned the specified label.\n",
567
874
                {
568
874
                    {"label", RPCArg::Type::STR, RPCArg::Optional::NO, "The label."},
569
874
                },
570
874
                RPCResult{
571
874
                    RPCResult::Type::OBJ_DYN, "", "json object with addresses as keys",
572
874
                    {
573
874
                        {RPCResult::Type::OBJ, "address", "json object with information about address",
574
874
                        {
575
874
                            {RPCResult::Type::STR, "purpose", "Purpose of address (\"send\" for sending address, \"receive\" for receiving address)"},
576
874
                        }},
577
874
                    }
578
874
                },
579
874
                RPCExamples{
580
874
                    HelpExampleCli("getaddressesbylabel", "\"tabby\"")
581
874
            + HelpExampleRpc("getaddressesbylabel", "\"tabby\"")
582
874
                },
583
874
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
584
874
{
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
874
    };
621
874
}
622
623
RPCMethod listlabels()
624
875
{
625
875
    return RPCMethod{
626
875
        "listlabels",
627
875
        "Returns the list of all labels, or labels that are assigned to addresses with a specific purpose.\n",
628
875
                {
629
875
                    {"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
875
                },
631
875
                RPCResult{
632
875
                    RPCResult::Type::ARR, "", "",
633
875
                    {
634
875
                        {RPCResult::Type::STR, "label", "Label name"},
635
875
                    }
636
875
                },
637
875
                RPCExamples{
638
875
            "\nList all labels\n"
639
875
            + HelpExampleCli("listlabels", "") +
640
875
            "\nList labels that have receiving addresses\n"
641
875
            + HelpExampleCli("listlabels", "receive") +
642
875
            "\nList labels that have sending addresses\n"
643
875
            + HelpExampleCli("listlabels", "send") +
644
875
            "\nAs a JSON-RPC call\n"
645
875
            + HelpExampleRpc("listlabels", "receive")
646
875
                },
647
875
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
648
875
{
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
875
    };
676
875
}
677
678
679
#ifdef ENABLE_EXTERNAL_SIGNER
680
RPCMethod walletdisplayaddress()
681
836
{
682
836
    return RPCMethod{
683
836
        "walletdisplayaddress",
684
836
        "Display address on an external signer for verification.",
685
836
        {
686
836
            {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "bitcoin address to display"},
687
836
        },
688
836
        RPCResult{
689
836
            RPCResult::Type::OBJ,"","",
690
836
            {
691
836
                {RPCResult::Type::STR, "address", "The address as confirmed by the signer"},
692
836
            }
693
836
        },
694
836
        RPCExamples{""},
695
836
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
696
836
        {
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
836
    };
718
836
}
719
#endif // ENABLE_EXTERNAL_SIGNER
720
} // namespace wallet