Coverage Report

Created: 2026-10-03 10:33

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.1k
    return EncodeDestination(*op_dest);
68
11.2k
},
69
12.1k
    };
70
12.1k
}
71
72
RPCMethod getrawchangeaddress()
73
1.23k
{
74
1.23k
    return RPCMethod{
75
1.23k
        "getrawchangeaddress",
76
1.23k
        "Returns a new Bitcoin address, for receiving change.\n"
77
1.23k
                "This is for use with raw transactions, NOT normal use.\n",
78
1.23k
                {
79
1.23k
                    {"address_type", RPCArg::Type::STR, RPCArg::DefaultHint{"set by -changetype"}, "The address type to use. Options are " + FormatAllOutputTypes() + "."},
80
1.23k
                },
81
1.23k
                RPCResult{
82
1.23k
                    RPCResult::Type::STR, "address", "The address"
83
1.23k
                },
84
1.23k
                RPCExamples{
85
1.23k
                    HelpExampleCli("getrawchangeaddress", "")
86
1.23k
            + HelpExampleRpc("getrawchangeaddress", "")
87
1.23k
                },
88
1.23k
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
89
1.23k
{
90
366
    std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
91
366
    if (!pwallet) return UniValue::VNULL;
92
93
366
    LOCK(pwallet->cs_wallet);
94
95
366
    if (!pwallet->CanGetAddresses(true)) {
96
25
        throw JSONRPCError(RPC_WALLET_ERROR, "Error: This wallet has no available keys");
97
25
    }
98
99
341
    OutputType output_type = pwallet->m_default_change_type.value_or(pwallet->m_default_address_type);
100
341
    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
339
    auto op_dest = pwallet->GetNewChangeDestination(output_type);
109
339
    if (!op_dest) {
110
1
        throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, util::ErrorString(op_dest).original);
111
1
    }
112
338
    return EncodeDestination(*op_dest);
113
339
},
114
1.23k
    };
115
1.23k
}
116
117
118
RPCMethod setlabel()
119
889
{
120
889
    return RPCMethod{
121
889
        "setlabel",
122
889
        "Sets the label associated with the given address.\n",
123
889
                {
124
889
                    {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The bitcoin address to be associated with a label."},
125
889
                    {"label", RPCArg::Type::STR, RPCArg::Optional::NO, "The label to assign to the address."},
126
889
                },
127
889
                RPCResult{RPCResult::Type::NONE, "", ""},
128
889
                RPCExamples{
129
889
                    HelpExampleCli("setlabel", "\"" + EXAMPLE_ADDRESS[0] + "\" \"tabby\"")
130
889
            + HelpExampleRpc("setlabel", "\"" + EXAMPLE_ADDRESS[0] + "\", \"tabby\"")
131
889
                },
132
889
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
133
889
{
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
889
    };
155
889
}
156
157
RPCMethod listaddressgroupings()
158
873
{
159
873
    return RPCMethod{
160
873
        "listaddressgroupings",
161
873
        "Lists groups of addresses which have had their common ownership\n"
162
873
                "made public by common use as inputs or as the resulting change\n"
163
873
                "in past transactions\n",
164
873
                {},
165
873
                RPCResult{
166
873
                    RPCResult::Type::ARR, "", "",
167
873
                    {
168
873
                        {RPCResult::Type::ARR, "", "",
169
873
                        {
170
873
                            {RPCResult::Type::ARR_FIXED, "", "",
171
873
                            {
172
873
                                {RPCResult::Type::STR, "address", "The bitcoin address"},
173
873
                                {RPCResult::Type::STR_AMOUNT, "amount", "The amount in " + CURRENCY_UNIT},
174
873
                                {RPCResult::Type::STR, "label", /*optional=*/true, "The label"},
175
873
                            }},
176
873
                        }},
177
873
                    }
178
873
                },
179
873
                RPCExamples{
180
873
                    HelpExampleCli("listaddressgroupings", "")
181
873
            + HelpExampleRpc("listaddressgroupings", "")
182
873
                },
183
873
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
184
873
{
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
873
    };
216
873
}
217
218
RPCMethod keypoolrefill()
219
881
{
220
881
    return RPCMethod{"keypoolrefill",
221
881
                "Refills each descriptor keypool in the wallet up to the specified number of new keys.\n"
222
881
                "By default, descriptor wallets have 4 active ranged descriptors (" + FormatAllOutputTypes() + "), each with " + util::ToString(DEFAULT_KEYPOOL_SIZE) + " entries.\n" +
223
881
        HELP_REQUIRING_PASSPHRASE,
224
881
                {
225
881
                    {"newsize", RPCArg::Type::NUM, RPCArg::DefaultHint{strprintf("%u, or as set by -keypool", DEFAULT_KEYPOOL_SIZE)}, "The new keypool size"},
226
881
                },
227
881
                RPCResult{RPCResult::Type::NONE, "", ""},
228
881
                RPCExamples{
229
881
                    HelpExampleCli("keypoolrefill", "")
230
881
            + HelpExampleRpc("keypoolrefill", "")
231
881
                },
232
881
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
233
881
{
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
881
    };
258
881
}
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
742
    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
117
    {
306
117
        CKeyID keyID{ToKeyID(pkhash)};
307
117
        UniValue obj(UniValue::VOBJ);
308
117
        CPubKey vchPubKey;
309
117
        if (provider && provider->GetPubKey(keyID, vchPubKey)) {
310
113
            obj.pushKV("pubkey", HexStr(vchPubKey));
311
113
            obj.pushKV("iscompressed", vchPubKey.IsCompressed());
312
113
        }
313
117
        return obj;
314
117
    }
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
475
    {
329
475
        UniValue obj(UniValue::VOBJ);
330
475
        CPubKey pubkey;
331
475
        if (provider && provider->GetPubKey(ToKeyID(id), pubkey)) {
332
406
            obj.pushKV("pubkey", HexStr(pubkey));
333
406
        }
334
475
        return obj;
335
475
    }
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
742
{
358
742
    UniValue ret(UniValue::VOBJ);
359
742
    UniValue detail = DescribeAddress(dest);
360
742
    CScript script = GetScriptForDestination(dest);
361
742
    std::unique_ptr<SigningProvider> provider = nullptr;
362
742
    provider = wallet.GetSolvingProvider(script);
363
742
    ret.pushKVs(std::move(detail));
364
742
    ret.pushKVs(std::visit(DescribeWalletAddressVisitor(provider.get()), dest));
365
742
    return ret;
366
742
}
367
368
static std::vector<RPCResult> GetAddressInfoBaseFields()
369
3.23k
{
370
3.23k
    return {
371
3.23k
        {RPCResult::Type::STR, "address", /*optional=*/true, "The bitcoin address of the embedded script."},
372
3.23k
        {RPCResult::Type::STR_HEX, "scriptPubKey", /*optional=*/true, "The hex-encoded output script generated by the address."},
373
3.23k
        {RPCResult::Type::BOOL, "isscript", /*optional=*/true, "If the key is a script."},
374
3.23k
        {RPCResult::Type::BOOL, "iswitness", /*optional=*/true, "If the address is a witness address."},
375
3.23k
        {RPCResult::Type::NUM, "witness_version", /*optional=*/true, "The version number of the witness program."},
376
3.23k
        {RPCResult::Type::STR_HEX, "witness_program", /*optional=*/true, "The hex value of the witness program."},
377
3.23k
        {RPCResult::Type::STR, "script", /*optional=*/true,
378
3.23k
            "The output script type. Only if isscript is true and the redeemscript is known. Possible\n"
379
3.23k
            "types: nonstandard, pubkey, pubkeyhash, scripthash, multisig, nulldata, witness_v0_keyhash,\n"
380
3.23k
            "witness_v0_scripthash, witness_unknown."},
381
3.23k
        {RPCResult::Type::STR_HEX, "hex", /*optional=*/true, "The redeemscript for the p2sh address."},
382
3.23k
        {RPCResult::Type::ARR, "pubkeys", /*optional=*/true,
383
3.23k
            "Array of pubkeys associated with the known redeemscript (only if script is multisig).",
384
3.23k
            {
385
3.23k
                {RPCResult::Type::STR, "pubkey", ""},
386
3.23k
            }},
387
3.23k
        {RPCResult::Type::NUM, "sigsrequired", /*optional=*/true,
388
3.23k
            "The number of signatures required to spend multisig output (only if script is multisig)."},
389
3.23k
        {RPCResult::Type::STR_HEX, "pubkey", /*optional=*/true,
390
3.23k
            "The hex value of the raw public key for single-key addresses (possibly embedded in P2SH or P2WSH)."},
391
3.23k
        {RPCResult::Type::BOOL, "iscompressed", /*optional=*/true, "If the pubkey is compressed."},
392
3.23k
    };
393
3.23k
}
394
395
static std::vector<RPCResult> GetAddressInfoEmbeddedFields(bool include_nested)
396
1.61k
{
397
1.61k
    auto fields = GetAddressInfoBaseFields();
398
399
1.61k
    if (include_nested) {
400
1.61k
        auto nested = GetAddressInfoBaseFields();
401
1.61k
        fields.emplace_back(
402
1.61k
            RPCResult::Type::OBJ,
403
1.61k
            "embedded",
404
1.61k
            /*optional=*/true,
405
1.61k
            "Information about the address embedded in P2SH or P2WSH, if relevant and known.",
406
1.61k
            std::move(nested)
407
1.61k
        );
408
1.61k
    }
409
410
1.61k
    return fields;
411
1.61k
}
412
413
RPCMethod getaddressinfo()
414
1.61k
{
415
1.61k
    return RPCMethod{
416
1.61k
        "getaddressinfo",
417
1.61k
        "Return information about the given bitcoin address.\n"
418
1.61k
                "Some of the information will only be present if the address is in the active wallet.\n",
419
1.61k
                {
420
1.61k
                    {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The bitcoin address for which to get information."},
421
1.61k
                },
422
1.61k
                RPCResult{
423
1.61k
                    RPCResult::Type::OBJ, "", "",
424
1.61k
                    {
425
1.61k
                        {RPCResult::Type::STR, "address", "The bitcoin address validated."},
426
1.61k
                        {RPCResult::Type::STR_HEX, "scriptPubKey", "The hex-encoded output script generated by the address."},
427
1.61k
                        {RPCResult::Type::BOOL, "ismine", "If the address is yours."},
428
1.61k
                        {RPCResult::Type::BOOL, "iswatchonly", "(DEPRECATED) Always false."},
429
1.61k
                        {RPCResult::Type::BOOL, "solvable", "If we know how to spend coins sent to this address, ignoring the possible lack of private keys."},
430
1.61k
                        {RPCResult::Type::STR, "desc", /*optional=*/true, "A descriptor for spending coins sent to this address (only when solvable)."},
431
1.61k
                        {RPCResult::Type::STR, "parent_desc", /*optional=*/true, "The descriptor used to derive this address if this is a descriptor wallet"},
432
1.61k
                        {RPCResult::Type::BOOL, "isscript", /*optional=*/true, "If the key is a script."},
433
1.61k
                        {RPCResult::Type::BOOL, "ischange", "If the address was used for change output."},
434
1.61k
                        {RPCResult::Type::BOOL, "iswitness", "If the address is a witness address."},
435
1.61k
                        {RPCResult::Type::NUM, "witness_version", /*optional=*/true, "The version number of the witness program."},
436
1.61k
                        {RPCResult::Type::STR_HEX, "witness_program", /*optional=*/true, "The hex value of the witness program."},
437
1.61k
                        {RPCResult::Type::STR, "script", /*optional=*/true, "The output script type. Only if isscript is true and the redeemscript is known. Possible\n"
438
1.61k
                                                                     "types: nonstandard, pubkey, pubkeyhash, scripthash, multisig, nulldata, witness_v0_keyhash,\n"
439
1.61k
                            "witness_v0_scripthash, witness_unknown."},
440
1.61k
                        {RPCResult::Type::STR_HEX, "hex", /*optional=*/true, "The redeemscript for the p2sh address."},
441
1.61k
                        {RPCResult::Type::ARR, "pubkeys", /*optional=*/true, "Array of pubkeys associated with the known redeemscript (only if script is multisig).",
442
1.61k
                        {
443
1.61k
                            {RPCResult::Type::STR, "pubkey", ""},
444
1.61k
                        }},
445
1.61k
                        {RPCResult::Type::NUM, "sigsrequired", /*optional=*/true, "The number of signatures required to spend multisig output (only if script is multisig)."},
446
1.61k
                        {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.61k
                        {RPCResult::Type::OBJ, "embedded", /*optional=*/true,
448
1.61k
                        "Information about the address embedded in P2SH or P2WSH, if relevant and known.",
449
1.61k
                        ElideGroup(
450
1.61k
                            GetAddressInfoEmbeddedFields(/*include_nested=*/true),
451
1.61k
                            "Includes all getaddressinfo output fields for the embedded address, excluding metadata (timestamp, hdkeypath, hdseedid)\n"
452
1.61k
                            "and relation to the wallet (ismine)."
453
1.61k
                        )},
454
1.61k
                        {RPCResult::Type::BOOL, "iscompressed", /*optional=*/true, "If the pubkey is compressed."},
455
1.61k
                        {RPCResult::Type::NUM_TIME, "timestamp", /*optional=*/true, "The creation time of the key, if available, expressed in " + UNIX_EPOCH_TIME + "."},
456
1.61k
                        {RPCResult::Type::STR, "hdkeypath", /*optional=*/true, "The HD keypath, if the key is HD and available."},
457
1.61k
                        {RPCResult::Type::STR_HEX, "hdseedid", /*optional=*/true, "The Hash160 of the HD seed."},
458
1.61k
                        {RPCResult::Type::STR_HEX, "hdmasterfingerprint", /*optional=*/true, "The fingerprint of the master key."},
459
1.61k
                        {RPCResult::Type::ARR, "labels", "Array of labels associated with the address. Currently limited to one label but returned\n"
460
1.61k
                            "as an array to keep the API stable if multiple labels are enabled in the future.",
461
1.61k
                        {
462
1.61k
                            {RPCResult::Type::STR, "label name", "Label name (defaults to \"\")."},
463
1.61k
                        }},
464
1.61k
                    }
465
1.61k
                },
466
1.61k
                RPCExamples{
467
1.61k
                    HelpExampleCli("getaddressinfo", "\"" + EXAMPLE_ADDRESS[0] + "\"") +
468
1.61k
                    HelpExampleRpc("getaddressinfo", "\"" + EXAMPLE_ADDRESS[0] + "\"")
469
1.61k
                },
470
1.61k
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
471
1.61k
{
472
747
    const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
473
747
    if (!pwallet) return UniValue::VNULL;
474
475
747
    LOCK(pwallet->cs_wallet);
476
477
747
    std::string error_msg;
478
747
    CTxDestination dest = DecodeDestination(request.params[0].get_str(), error_msg);
479
480
    // Make sure the destination is valid
481
747
    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
742
    UniValue ret(UniValue::VOBJ);
489
490
742
    std::string currentAddress = EncodeDestination(dest);
491
742
    ret.pushKV("address", currentAddress);
492
493
742
    CScript scriptPubKey = GetScriptForDestination(dest);
494
742
    ret.pushKV("scriptPubKey", HexStr(scriptPubKey));
495
496
742
    std::unique_ptr<SigningProvider> provider = pwallet->GetSolvingProvider(scriptPubKey);
497
498
742
    bool mine = pwallet->IsMine(dest);
499
742
    ret.pushKV("ismine", mine);
500
501
742
    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
91
        ret.pushKV("solvable", false);
510
91
    }
511
512
742
    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
742
    ScriptPubKeyMan* spk_man{nullptr};
515
742
    if (spk_mans.size()) spk_man = *spk_mans.begin();
516
517
742
    DescriptorScriptPubKeyMan* desc_spk_man = dynamic_cast<DescriptorScriptPubKeyMan*>(spk_man);
518
742
    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
742
    ret.pushKV("iswatchonly", false);
526
527
742
    UniValue detail = DescribeWalletAddress(*pwallet, dest);
528
742
    ret.pushKVs(std::move(detail));
529
530
742
    ret.pushKV("ischange", ScriptIsChange(*pwallet, scriptPubKey));
531
532
742
    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
742
    UniValue labels(UniValue::VARR);
551
742
    const auto* address_book_entry = pwallet->FindAddressBookEntry(dest);
552
742
    if (address_book_entry) {
553
494
        labels.push_back(address_book_entry->GetLabel());
554
494
    }
555
742
    ret.pushKV("labels", std::move(labels));
556
557
742
    return ret;
558
747
},
559
1.61k
    };
560
1.61k
}
561
562
RPCMethod getaddressesbylabel()
563
914
{
564
914
    return RPCMethod{
565
914
        "getaddressesbylabel",
566
914
        "Returns the list of addresses assigned the specified label.\n",
567
914
                {
568
914
                    {"label", RPCArg::Type::STR, RPCArg::Optional::NO, "The label."},
569
914
                },
570
914
                RPCResult{
571
914
                    RPCResult::Type::OBJ_DYN, "", "json object with addresses as keys",
572
914
                    {
573
914
                        {RPCResult::Type::OBJ, "address", "json object with information about address",
574
914
                        {
575
914
                            {RPCResult::Type::STR, "purpose", "Purpose of address (\"send\" for sending address, \"receive\" for receiving address)"},
576
914
                        }},
577
914
                    }
578
914
                },
579
914
                RPCExamples{
580
914
                    HelpExampleCli("getaddressesbylabel", "\"tabby\"")
581
914
            + HelpExampleRpc("getaddressesbylabel", "\"tabby\"")
582
914
                },
583
914
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
584
914
{
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
914
    };
621
914
}
622
623
RPCMethod listlabels()
624
915
{
625
915
    return RPCMethod{
626
915
        "listlabels",
627
915
        "Returns the list of all labels, or labels that are assigned to addresses with a specific purpose.\n",
628
915
                {
629
915
                    {"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
915
                },
631
915
                RPCResult{
632
915
                    RPCResult::Type::ARR, "", "",
633
915
                    {
634
915
                        {RPCResult::Type::STR, "label", "Label name"},
635
915
                    }
636
915
                },
637
915
                RPCExamples{
638
915
            "\nList all labels\n"
639
915
            + HelpExampleCli("listlabels", "") +
640
915
            "\nList labels that have receiving addresses\n"
641
915
            + HelpExampleCli("listlabels", "receive") +
642
915
            "\nList labels that have sending addresses\n"
643
915
            + HelpExampleCli("listlabels", "send") +
644
915
            "\nAs a JSON-RPC call\n"
645
915
            + HelpExampleRpc("listlabels", R"("receive")")
646
915
                },
647
915
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
648
915
{
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
915
    };
676
915
}
677
678
679
#ifdef ENABLE_EXTERNAL_SIGNER
680
RPCMethod walletdisplayaddress()
681
876
{
682
876
    return RPCMethod{
683
876
        "walletdisplayaddress",
684
876
        "Display address on an external signer for verification.",
685
876
        {
686
876
            {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "bitcoin address to display"},
687
876
        },
688
876
        RPCResult{
689
876
            RPCResult::Type::OBJ,"","",
690
876
            {
691
876
                {RPCResult::Type::STR, "address", "The address as confirmed by the signer"},
692
876
            }
693
876
        },
694
876
        RPCExamples{""},
695
876
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
696
876
        {
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
876
    };
718
876
}
719
#endif // ENABLE_EXTERNAL_SIGNER
720
} // namespace wallet