Coverage Report

Created: 2026-10-19 15:22

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