Coverage Report

Created: 2025-06-10 13:21

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
RPCHelpMan getnewaddress()
22
22.1k
{
23
22.1k
    return RPCHelpMan{
24
22.1k
        "getnewaddress",
25
22.1k
        "Returns a new Bitcoin address for receiving payments.\n"
26
22.1k
                "If 'label' is specified, it is added to the address book \n"
27
22.1k
                "so payments received with the address will be associated with 'label'.\n",
28
22.1k
                {
29
22.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
22.1k
                    {"address_type", RPCArg::Type::STR, RPCArg::DefaultHint{"set by -addresstype"}, "The address type to use. Options are \"legacy\", \"p2sh-segwit\", \"bech32\", and \"bech32m\"."},
31
22.1k
                },
32
22.1k
                RPCResult{
33
22.1k
                    RPCResult::Type::STR, "address", "The new bitcoin address"
34
22.1k
                },
35
22.1k
                RPCExamples{
36
22.1k
                    HelpExampleCli("getnewaddress", "")
37
22.1k
            + HelpExampleRpc("getnewaddress", "")
38
22.1k
                },
39
22.1k
        [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
40
22.1k
{
41
0
    std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
42
0
    if (!pwallet) return UniValue::VNULL;
  Branch (42:9): [True: 0, False: 0]
43
44
0
    LOCK(pwallet->cs_wallet);
45
46
0
    if (!pwallet->CanGetAddresses()) {
  Branch (46:9): [True: 0, False: 0]
47
0
        throw JSONRPCError(RPC_WALLET_ERROR, "Error: This wallet has no available keys");
48
0
    }
49
50
    // Parse the label first so we don't generate a key if there's an error
51
0
    const std::string label{LabelFromValue(request.params[0])};
52
53
0
    OutputType output_type = pwallet->m_default_address_type;
54
0
    if (!request.params[1].isNull()) {
  Branch (54:9): [True: 0, False: 0]
55
0
        std::optional<OutputType> parsed = ParseOutputType(request.params[1].get_str());
56
0
        if (!parsed) {
  Branch (56:13): [True: 0, False: 0]
57
0
            throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Unknown address type '%s'", request.params[1].get_str()));
58
0
        }
59
0
        output_type = parsed.value();
60
0
    }
61
62
0
    auto op_dest = pwallet->GetNewDestination(output_type, label);
63
0
    if (!op_dest) {
  Branch (63:9): [True: 0, False: 0]
64
0
        throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, util::ErrorString(op_dest).original);
65
0
    }
66
67
0
    return EncodeDestination(*op_dest);
68
0
},
69
22.1k
    };
70
22.1k
}
71
72
RPCHelpMan getrawchangeaddress()
73
22.1k
{
74
22.1k
    return RPCHelpMan{
75
22.1k
        "getrawchangeaddress",
76
22.1k
        "Returns a new Bitcoin address, for receiving change.\n"
77
22.1k
                "This is for use with raw transactions, NOT normal use.\n",
78
22.1k
                {
79
22.1k
                    {"address_type", RPCArg::Type::STR, RPCArg::DefaultHint{"set by -changetype"}, "The address type to use. Options are \"legacy\", \"p2sh-segwit\", \"bech32\", and \"bech32m\"."},
80
22.1k
                },
81
22.1k
                RPCResult{
82
22.1k
                    RPCResult::Type::STR, "address", "The address"
83
22.1k
                },
84
22.1k
                RPCExamples{
85
22.1k
                    HelpExampleCli("getrawchangeaddress", "")
86
22.1k
            + HelpExampleRpc("getrawchangeaddress", "")
87
22.1k
                },
88
22.1k
        [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
89
22.1k
{
90
0
    std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
91
0
    if (!pwallet) return UniValue::VNULL;
  Branch (91:9): [True: 0, False: 0]
92
93
0
    LOCK(pwallet->cs_wallet);
94
95
0
    if (!pwallet->CanGetAddresses(true)) {
  Branch (95:9): [True: 0, False: 0]
96
0
        throw JSONRPCError(RPC_WALLET_ERROR, "Error: This wallet has no available keys");
97
0
    }
98
99
0
    OutputType output_type = pwallet->m_default_change_type.value_or(pwallet->m_default_address_type);
100
0
    if (!request.params[0].isNull()) {
  Branch (100:9): [True: 0, False: 0]
101
0
        std::optional<OutputType> parsed = ParseOutputType(request.params[0].get_str());
102
0
        if (!parsed) {
  Branch (102:13): [True: 0, False: 0]
103
0
            throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Unknown address type '%s'", request.params[0].get_str()));
104
0
        }
105
0
        output_type = parsed.value();
106
0
    }
107
108
0
    auto op_dest = pwallet->GetNewChangeDestination(output_type);
109
0
    if (!op_dest) {
  Branch (109:9): [True: 0, False: 0]
110
0
        throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, util::ErrorString(op_dest).original);
111
0
    }
112
0
    return EncodeDestination(*op_dest);
113
0
},
114
22.1k
    };
115
22.1k
}
116
117
118
RPCHelpMan setlabel()
119
22.1k
{
120
22.1k
    return RPCHelpMan{
121
22.1k
        "setlabel",
122
22.1k
        "Sets the label associated with the given address.\n",
123
22.1k
                {
124
22.1k
                    {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The bitcoin address to be associated with a label."},
125
22.1k
                    {"label", RPCArg::Type::STR, RPCArg::Optional::NO, "The label to assign to the address."},
126
22.1k
                },
127
22.1k
                RPCResult{RPCResult::Type::NONE, "", ""},
128
22.1k
                RPCExamples{
129
22.1k
                    HelpExampleCli("setlabel", "\"" + EXAMPLE_ADDRESS[0] + "\" \"tabby\"")
130
22.1k
            + HelpExampleRpc("setlabel", "\"" + EXAMPLE_ADDRESS[0] + "\", \"tabby\"")
131
22.1k
                },
132
22.1k
        [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
133
22.1k
{
134
0
    std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
135
0
    if (!pwallet) return UniValue::VNULL;
  Branch (135:9): [True: 0, False: 0]
136
137
0
    LOCK(pwallet->cs_wallet);
138
139
0
    CTxDestination dest = DecodeDestination(request.params[0].get_str());
140
0
    if (!IsValidDestination(dest)) {
  Branch (140:9): [True: 0, False: 0]
141
0
        throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address");
142
0
    }
143
144
0
    const std::string label{LabelFromValue(request.params[1])};
145
146
0
    if (pwallet->IsMine(dest)) {
  Branch (146:9): [True: 0, False: 0]
147
0
        pwallet->SetAddressBook(dest, label, AddressPurpose::RECEIVE);
148
0
    } else {
149
0
        pwallet->SetAddressBook(dest, label, AddressPurpose::SEND);
150
0
    }
151
152
0
    return UniValue::VNULL;
153
0
},
154
22.1k
    };
155
22.1k
}
156
157
RPCHelpMan listaddressgroupings()
158
22.1k
{
159
22.1k
    return RPCHelpMan{
160
22.1k
        "listaddressgroupings",
161
22.1k
        "Lists groups of addresses which have had their common ownership\n"
162
22.1k
                "made public by common use as inputs or as the resulting change\n"
163
22.1k
                "in past transactions\n",
164
22.1k
                {},
165
22.1k
                RPCResult{
166
22.1k
                    RPCResult::Type::ARR, "", "",
167
22.1k
                    {
168
22.1k
                        {RPCResult::Type::ARR, "", "",
169
22.1k
                        {
170
22.1k
                            {RPCResult::Type::ARR_FIXED, "", "",
171
22.1k
                            {
172
22.1k
                                {RPCResult::Type::STR, "address", "The bitcoin address"},
173
22.1k
                                {RPCResult::Type::STR_AMOUNT, "amount", "The amount in " + CURRENCY_UNIT},
174
22.1k
                                {RPCResult::Type::STR, "label", /*optional=*/true, "The label"},
175
22.1k
                            }},
176
22.1k
                        }},
177
22.1k
                    }
178
22.1k
                },
179
22.1k
                RPCExamples{
180
22.1k
                    HelpExampleCli("listaddressgroupings", "")
181
22.1k
            + HelpExampleRpc("listaddressgroupings", "")
182
22.1k
                },
183
22.1k
        [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
184
22.1k
{
185
0
    const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
186
0
    if (!pwallet) return UniValue::VNULL;
  Branch (186:9): [True: 0, False: 0]
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
0
    pwallet->BlockUntilSyncedToCurrentChain();
191
192
0
    LOCK(pwallet->cs_wallet);
193
194
0
    UniValue jsonGroupings(UniValue::VARR);
195
0
    std::map<CTxDestination, CAmount> balances = GetAddressBalances(*pwallet);
196
0
    for (const std::set<CTxDestination>& grouping : GetAddressGroupings(*pwallet)) {
  Branch (196:51): [True: 0, False: 0]
197
0
        UniValue jsonGrouping(UniValue::VARR);
198
0
        for (const CTxDestination& address : grouping)
  Branch (198:44): [True: 0, False: 0]
199
0
        {
200
0
            UniValue addressInfo(UniValue::VARR);
201
0
            addressInfo.push_back(EncodeDestination(address));
202
0
            addressInfo.push_back(ValueFromAmount(balances[address]));
203
0
            {
204
0
                const auto* address_book_entry = pwallet->FindAddressBookEntry(address);
205
0
                if (address_book_entry) {
  Branch (205:21): [True: 0, False: 0]
206
0
                    addressInfo.push_back(address_book_entry->GetLabel());
207
0
                }
208
0
            }
209
0
            jsonGrouping.push_back(std::move(addressInfo));
210
0
        }
211
0
        jsonGroupings.push_back(std::move(jsonGrouping));
212
0
    }
213
0
    return jsonGroupings;
214
0
},
215
22.1k
    };
216
22.1k
}
217
218
RPCHelpMan keypoolrefill()
219
22.1k
{
220
22.1k
    return RPCHelpMan{"keypoolrefill",
221
22.1k
                "Refills each descriptor keypool in the wallet up to the specified number of new keys.\n"
222
22.1k
                "By default, descriptor wallets have 4 active ranged descriptors (\"legacy\", \"p2sh-segwit\", \"bech32\", and \"bech32m\"), each with " + util::ToString(DEFAULT_KEYPOOL_SIZE) + " entries.\n" +
223
22.1k
        HELP_REQUIRING_PASSPHRASE,
224
22.1k
                {
225
22.1k
                    {"newsize", RPCArg::Type::NUM, RPCArg::DefaultHint{strprintf("%u, or as set by -keypool", DEFAULT_KEYPOOL_SIZE)}, "The new keypool size"},
226
22.1k
                },
227
22.1k
                RPCResult{RPCResult::Type::NONE, "", ""},
228
22.1k
                RPCExamples{
229
22.1k
                    HelpExampleCli("keypoolrefill", "")
230
22.1k
            + HelpExampleRpc("keypoolrefill", "")
231
22.1k
                },
232
22.1k
        [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
233
22.1k
{
234
0
    std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
235
0
    if (!pwallet) return UniValue::VNULL;
  Branch (235:9): [True: 0, False: 0]
236
237
0
    LOCK(pwallet->cs_wallet);
238
239
    // 0 is interpreted by TopUpKeyPool() as the default keypool size given by -keypool
240
0
    unsigned int kpSize = 0;
241
0
    if (!request.params[0].isNull()) {
  Branch (241:9): [True: 0, False: 0]
242
0
        if (request.params[0].getInt<int>() < 0)
  Branch (242:13): [True: 0, False: 0]
243
0
            throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected valid size.");
244
0
        kpSize = (unsigned int)request.params[0].getInt<int>();
245
0
    }
246
247
0
    EnsureWalletIsUnlocked(*pwallet);
248
0
    pwallet->TopUpKeyPool(kpSize);
249
250
0
    if (pwallet->GetKeyPoolSize() < kpSize) {
  Branch (250:9): [True: 0, False: 0]
251
0
        throw JSONRPCError(RPC_WALLET_ERROR, "Error refreshing keypool.");
252
0
    }
253
254
0
    return UniValue::VNULL;
255
0
},
256
22.1k
    };
257
22.1k
}
258
259
class DescribeWalletAddressVisitor
260
{
261
public:
262
    const SigningProvider * const provider;
263
264
    // NOLINTNEXTLINE(misc-no-recursion)
265
    void ProcessSubScript(const CScript& subscript, UniValue& obj) const
266
0
    {
267
        // Always present: script type and redeemscript
268
0
        std::vector<std::vector<unsigned char>> solutions_data;
269
0
        TxoutType which_type = Solver(subscript, solutions_data);
270
0
        obj.pushKV("script", GetTxnOutputType(which_type));
271
0
        obj.pushKV("hex", HexStr(subscript));
272
273
0
        CTxDestination embedded;
274
0
        if (ExtractDestination(subscript, embedded)) {
  Branch (274:13): [True: 0, False: 0]
275
            // Only when the script corresponds to an address.
276
0
            UniValue subobj(UniValue::VOBJ);
277
0
            UniValue detail = DescribeAddress(embedded);
278
0
            subobj.pushKVs(std::move(detail));
279
0
            UniValue wallet_detail = std::visit(*this, embedded);
280
0
            subobj.pushKVs(std::move(wallet_detail));
281
0
            subobj.pushKV("address", EncodeDestination(embedded));
282
0
            subobj.pushKV("scriptPubKey", HexStr(subscript));
283
            // Always report the pubkey at the top level, so that `getnewaddress()['pubkey']` always works.
284
0
            if (subobj.exists("pubkey")) obj.pushKV("pubkey", subobj["pubkey"]);
  Branch (284:17): [True: 0, False: 0]
285
0
            obj.pushKV("embedded", std::move(subobj));
286
0
        } else if (which_type == TxoutType::MULTISIG) {
  Branch (286:20): [True: 0, False: 0]
287
            // Also report some information on multisig scripts (which do not have a corresponding address).
288
0
            obj.pushKV("sigsrequired", solutions_data[0][0]);
289
0
            UniValue pubkeys(UniValue::VARR);
290
0
            for (size_t i = 1; i < solutions_data.size() - 1; ++i) {
  Branch (290:32): [True: 0, False: 0]
291
0
                CPubKey key(solutions_data[i].begin(), solutions_data[i].end());
292
0
                pubkeys.push_back(HexStr(key));
293
0
            }
294
0
            obj.pushKV("pubkeys", std::move(pubkeys));
295
0
        }
296
0
    }
297
298
0
    explicit DescribeWalletAddressVisitor(const SigningProvider* _provider) : provider(_provider) {}
299
300
0
    UniValue operator()(const CNoDestination& dest) const { return UniValue(UniValue::VOBJ); }
301
0
    UniValue operator()(const PubKeyDestination& dest) const { return UniValue(UniValue::VOBJ); }
302
303
    UniValue operator()(const PKHash& pkhash) const
304
0
    {
305
0
        CKeyID keyID{ToKeyID(pkhash)};
306
0
        UniValue obj(UniValue::VOBJ);
307
0
        CPubKey vchPubKey;
308
0
        if (provider && provider->GetPubKey(keyID, vchPubKey)) {
  Branch (308:13): [True: 0, False: 0]
  Branch (308:25): [True: 0, False: 0]
309
0
            obj.pushKV("pubkey", HexStr(vchPubKey));
310
0
            obj.pushKV("iscompressed", vchPubKey.IsCompressed());
311
0
        }
312
0
        return obj;
313
0
    }
314
315
    // NOLINTNEXTLINE(misc-no-recursion)
316
    UniValue operator()(const ScriptHash& scripthash) const
317
0
    {
318
0
        UniValue obj(UniValue::VOBJ);
319
0
        CScript subscript;
320
0
        if (provider && provider->GetCScript(ToScriptID(scripthash), subscript)) {
  Branch (320:13): [True: 0, False: 0]
  Branch (320:13): [True: 0, False: 0]
  Branch (320:25): [True: 0, False: 0]
321
0
            ProcessSubScript(subscript, obj);
322
0
        }
323
0
        return obj;
324
0
    }
325
326
    UniValue operator()(const WitnessV0KeyHash& id) const
327
0
    {
328
0
        UniValue obj(UniValue::VOBJ);
329
0
        CPubKey pubkey;
330
0
        if (provider && provider->GetPubKey(ToKeyID(id), pubkey)) {
  Branch (330:13): [True: 0, False: 0]
  Branch (330:13): [True: 0, False: 0]
  Branch (330:25): [True: 0, False: 0]
331
0
            obj.pushKV("pubkey", HexStr(pubkey));
332
0
        }
333
0
        return obj;
334
0
    }
335
336
    // NOLINTNEXTLINE(misc-no-recursion)
337
    UniValue operator()(const WitnessV0ScriptHash& id) const
338
0
    {
339
0
        UniValue obj(UniValue::VOBJ);
340
0
        CScript subscript;
341
0
        CRIPEMD160 hasher;
342
0
        uint160 hash;
343
0
        hasher.Write(id.begin(), 32).Finalize(hash.begin());
344
0
        if (provider && provider->GetCScript(CScriptID(hash), subscript)) {
  Branch (344:13): [True: 0, False: 0]
  Branch (344:13): [True: 0, False: 0]
  Branch (344:25): [True: 0, False: 0]
345
0
            ProcessSubScript(subscript, obj);
346
0
        }
347
0
        return obj;
348
0
    }
349
350
0
    UniValue operator()(const WitnessV1Taproot& id) const { return UniValue(UniValue::VOBJ); }
351
0
    UniValue operator()(const PayToAnchor& id) const { return UniValue(UniValue::VOBJ); }
352
0
    UniValue operator()(const WitnessUnknown& id) const { return UniValue(UniValue::VOBJ); }
353
};
354
355
static UniValue DescribeWalletAddress(const CWallet& wallet, const CTxDestination& dest)
356
0
{
357
0
    UniValue ret(UniValue::VOBJ);
358
0
    UniValue detail = DescribeAddress(dest);
359
0
    CScript script = GetScriptForDestination(dest);
360
0
    std::unique_ptr<SigningProvider> provider = nullptr;
361
0
    provider = wallet.GetSolvingProvider(script);
362
0
    ret.pushKVs(std::move(detail));
363
0
    ret.pushKVs(std::visit(DescribeWalletAddressVisitor(provider.get()), dest));
364
0
    return ret;
365
0
}
366
367
RPCHelpMan getaddressinfo()
368
22.1k
{
369
22.1k
    return RPCHelpMan{
370
22.1k
        "getaddressinfo",
371
22.1k
        "Return information about the given bitcoin address.\n"
372
22.1k
                "Some of the information will only be present if the address is in the active wallet.\n",
373
22.1k
                {
374
22.1k
                    {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The bitcoin address for which to get information."},
375
22.1k
                },
376
22.1k
                RPCResult{
377
22.1k
                    RPCResult::Type::OBJ, "", "",
378
22.1k
                    {
379
22.1k
                        {RPCResult::Type::STR, "address", "The bitcoin address validated."},
380
22.1k
                        {RPCResult::Type::STR_HEX, "scriptPubKey", "The hex-encoded output script generated by the address."},
381
22.1k
                        {RPCResult::Type::BOOL, "ismine", "If the address is yours."},
382
22.1k
                        {RPCResult::Type::BOOL, "iswatchonly", "If the address is watchonly."},
383
22.1k
                        {RPCResult::Type::BOOL, "solvable", "If we know how to spend coins sent to this address, ignoring the possible lack of private keys."},
384
22.1k
                        {RPCResult::Type::STR, "desc", /*optional=*/true, "A descriptor for spending coins sent to this address (only when solvable)."},
385
22.1k
                        {RPCResult::Type::STR, "parent_desc", /*optional=*/true, "The descriptor used to derive this address if this is a descriptor wallet"},
386
22.1k
                        {RPCResult::Type::BOOL, "isscript", /*optional=*/true, "If the key is a script."},
387
22.1k
                        {RPCResult::Type::BOOL, "ischange", "If the address was used for change output."},
388
22.1k
                        {RPCResult::Type::BOOL, "iswitness", "If the address is a witness address."},
389
22.1k
                        {RPCResult::Type::NUM, "witness_version", /*optional=*/true, "The version number of the witness program."},
390
22.1k
                        {RPCResult::Type::STR_HEX, "witness_program", /*optional=*/true, "The hex value of the witness program."},
391
22.1k
                        {RPCResult::Type::STR, "script", /*optional=*/true, "The output script type. Only if isscript is true and the redeemscript is known. Possible\n"
392
22.1k
                                                                     "types: nonstandard, pubkey, pubkeyhash, scripthash, multisig, nulldata, witness_v0_keyhash,\n"
393
22.1k
                            "witness_v0_scripthash, witness_unknown."},
394
22.1k
                        {RPCResult::Type::STR_HEX, "hex", /*optional=*/true, "The redeemscript for the p2sh address."},
395
22.1k
                        {RPCResult::Type::ARR, "pubkeys", /*optional=*/true, "Array of pubkeys associated with the known redeemscript (only if script is multisig).",
396
22.1k
                        {
397
22.1k
                            {RPCResult::Type::STR, "pubkey", ""},
398
22.1k
                        }},
399
22.1k
                        {RPCResult::Type::NUM, "sigsrequired", /*optional=*/true, "The number of signatures required to spend multisig output (only if script is multisig)."},
400
22.1k
                        {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)."},
401
22.1k
                        {RPCResult::Type::OBJ, "embedded", /*optional=*/true, "Information about the address embedded in P2SH or P2WSH, if relevant and known.",
402
22.1k
                        {
403
22.1k
                            {RPCResult::Type::ELISION, "", "Includes all getaddressinfo output fields for the embedded address, excluding metadata (timestamp, hdkeypath, hdseedid)\n"
404
22.1k
                            "and relation to the wallet (ismine, iswatchonly)."},
405
22.1k
                        }},
406
22.1k
                        {RPCResult::Type::BOOL, "iscompressed", /*optional=*/true, "If the pubkey is compressed."},
407
22.1k
                        {RPCResult::Type::NUM_TIME, "timestamp", /*optional=*/true, "The creation time of the key, if available, expressed in " + UNIX_EPOCH_TIME + "."},
408
22.1k
                        {RPCResult::Type::STR, "hdkeypath", /*optional=*/true, "The HD keypath, if the key is HD and available."},
409
22.1k
                        {RPCResult::Type::STR_HEX, "hdseedid", /*optional=*/true, "The Hash160 of the HD seed."},
410
22.1k
                        {RPCResult::Type::STR_HEX, "hdmasterfingerprint", /*optional=*/true, "The fingerprint of the master key."},
411
22.1k
                        {RPCResult::Type::ARR, "labels", "Array of labels associated with the address. Currently limited to one label but returned\n"
412
22.1k
                            "as an array to keep the API stable if multiple labels are enabled in the future.",
413
22.1k
                        {
414
22.1k
                            {RPCResult::Type::STR, "label name", "Label name (defaults to \"\")."},
415
22.1k
                        }},
416
22.1k
                    }
417
22.1k
                },
418
22.1k
                RPCExamples{
419
22.1k
                    HelpExampleCli("getaddressinfo", "\"" + EXAMPLE_ADDRESS[0] + "\"") +
420
22.1k
                    HelpExampleRpc("getaddressinfo", "\"" + EXAMPLE_ADDRESS[0] + "\"")
421
22.1k
                },
422
22.1k
        [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
423
22.1k
{
424
0
    const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
425
0
    if (!pwallet) return UniValue::VNULL;
  Branch (425:9): [True: 0, False: 0]
426
427
0
    LOCK(pwallet->cs_wallet);
428
429
0
    std::string error_msg;
430
0
    CTxDestination dest = DecodeDestination(request.params[0].get_str(), error_msg);
431
432
    // Make sure the destination is valid
433
0
    if (!IsValidDestination(dest)) {
  Branch (433:9): [True: 0, False: 0]
434
        // Set generic error message in case 'DecodeDestination' didn't set it
435
0
        if (error_msg.empty()) error_msg = "Invalid address";
  Branch (435:13): [True: 0, False: 0]
436
437
0
        throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, error_msg);
438
0
    }
439
440
0
    UniValue ret(UniValue::VOBJ);
441
442
0
    std::string currentAddress = EncodeDestination(dest);
443
0
    ret.pushKV("address", currentAddress);
444
445
0
    CScript scriptPubKey = GetScriptForDestination(dest);
446
0
    ret.pushKV("scriptPubKey", HexStr(scriptPubKey));
447
448
0
    std::unique_ptr<SigningProvider> provider = pwallet->GetSolvingProvider(scriptPubKey);
449
450
0
    isminetype mine = pwallet->IsMine(dest);
451
0
    ret.pushKV("ismine", bool(mine & ISMINE_SPENDABLE));
452
453
0
    if (provider) {
  Branch (453:9): [True: 0, False: 0]
454
0
        auto inferred = InferDescriptor(scriptPubKey, *provider);
455
0
        bool solvable = inferred->IsSolvable();
456
0
        ret.pushKV("solvable", solvable);
457
0
        if (solvable) {
  Branch (457:13): [True: 0, False: 0]
458
0
            ret.pushKV("desc", inferred->ToString());
459
0
        }
460
0
    } else {
461
0
        ret.pushKV("solvable", false);
462
0
    }
463
464
0
    const auto& spk_mans = pwallet->GetScriptPubKeyMans(scriptPubKey);
465
    // In most cases there is only one matching ScriptPubKey manager and we can't resolve ambiguity in a better way
466
0
    ScriptPubKeyMan* spk_man{nullptr};
467
0
    if (spk_mans.size()) spk_man = *spk_mans.begin();
  Branch (467:9): [True: 0, False: 0]
468
469
0
    DescriptorScriptPubKeyMan* desc_spk_man = dynamic_cast<DescriptorScriptPubKeyMan*>(spk_man);
470
0
    if (desc_spk_man) {
  Branch (470:9): [True: 0, False: 0]
471
0
        std::string desc_str;
472
0
        if (desc_spk_man->GetDescriptorString(desc_str, /*priv=*/false)) {
  Branch (472:13): [True: 0, False: 0]
473
0
            ret.pushKV("parent_desc", desc_str);
474
0
        }
475
0
    }
476
477
0
    ret.pushKV("iswatchonly", bool(mine & ISMINE_WATCH_ONLY));
478
479
0
    UniValue detail = DescribeWalletAddress(*pwallet, dest);
480
0
    ret.pushKVs(std::move(detail));
481
482
0
    ret.pushKV("ischange", ScriptIsChange(*pwallet, scriptPubKey));
483
484
0
    if (spk_man) {
  Branch (484:9): [True: 0, False: 0]
485
0
        if (const std::unique_ptr<CKeyMetadata> meta = spk_man->GetMetadata(dest)) {
  Branch (485:49): [True: 0, False: 0]
486
0
            ret.pushKV("timestamp", meta->nCreateTime);
487
0
            if (meta->has_key_origin) {
  Branch (487:17): [True: 0, False: 0]
488
                // In legacy wallets hdkeypath has always used an apostrophe for
489
                // hardened derivation. Perhaps some external tool depends on that.
490
0
                ret.pushKV("hdkeypath", WriteHDKeypath(meta->key_origin.path, /*apostrophe=*/!desc_spk_man));
491
0
                ret.pushKV("hdseedid", meta->hd_seed_id.GetHex());
492
0
                ret.pushKV("hdmasterfingerprint", HexStr(meta->key_origin.fingerprint));
493
0
            }
494
0
        }
495
0
    }
496
497
    // Return a `labels` array containing the label associated with the address,
498
    // equivalent to the `label` field above. Currently only one label can be
499
    // associated with an address, but we return an array so the API remains
500
    // stable if we allow multiple labels to be associated with an address in
501
    // the future.
502
0
    UniValue labels(UniValue::VARR);
503
0
    const auto* address_book_entry = pwallet->FindAddressBookEntry(dest);
504
0
    if (address_book_entry) {
  Branch (504:9): [True: 0, False: 0]
505
0
        labels.push_back(address_book_entry->GetLabel());
506
0
    }
507
0
    ret.pushKV("labels", std::move(labels));
508
509
0
    return ret;
510
0
},
511
22.1k
    };
512
22.1k
}
513
514
RPCHelpMan getaddressesbylabel()
515
22.1k
{
516
22.1k
    return RPCHelpMan{
517
22.1k
        "getaddressesbylabel",
518
22.1k
        "Returns the list of addresses assigned the specified label.\n",
519
22.1k
                {
520
22.1k
                    {"label", RPCArg::Type::STR, RPCArg::Optional::NO, "The label."},
521
22.1k
                },
522
22.1k
                RPCResult{
523
22.1k
                    RPCResult::Type::OBJ_DYN, "", "json object with addresses as keys",
524
22.1k
                    {
525
22.1k
                        {RPCResult::Type::OBJ, "address", "json object with information about address",
526
22.1k
                        {
527
22.1k
                            {RPCResult::Type::STR, "purpose", "Purpose of address (\"send\" for sending address, \"receive\" for receiving address)"},
528
22.1k
                        }},
529
22.1k
                    }
530
22.1k
                },
531
22.1k
                RPCExamples{
532
22.1k
                    HelpExampleCli("getaddressesbylabel", "\"tabby\"")
533
22.1k
            + HelpExampleRpc("getaddressesbylabel", "\"tabby\"")
534
22.1k
                },
535
22.1k
        [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
536
22.1k
{
537
0
    const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
538
0
    if (!pwallet) return UniValue::VNULL;
  Branch (538:9): [True: 0, False: 0]
539
540
0
    LOCK(pwallet->cs_wallet);
541
542
0
    const std::string label{LabelFromValue(request.params[0])};
543
544
    // Find all addresses that have the given label
545
0
    UniValue ret(UniValue::VOBJ);
546
0
    std::set<std::string> addresses;
547
0
    pwallet->ForEachAddrBookEntry([&](const CTxDestination& _dest, const std::string& _label, bool _is_change, const std::optional<AddressPurpose>& _purpose) {
548
0
        if (_is_change) return;
  Branch (548:13): [True: 0, False: 0]
549
0
        if (_label == label) {
  Branch (549:13): [True: 0, False: 0]
550
0
            std::string address = EncodeDestination(_dest);
551
            // CWallet::m_address_book is not expected to contain duplicate
552
            // address strings, but build a separate set as a precaution just in
553
            // case it does.
554
0
            bool unique = addresses.emplace(address).second;
555
0
            CHECK_NONFATAL(unique);
556
            // UniValue::pushKV checks if the key exists in O(N)
557
            // and since duplicate addresses are unexpected (checked with
558
            // std::set in O(log(N))), UniValue::pushKVEnd is used instead,
559
            // which currently is O(1).
560
0
            UniValue value(UniValue::VOBJ);
561
0
            value.pushKV("purpose", _purpose ? PurposeToString(*_purpose) : "unknown");
  Branch (561:37): [True: 0, False: 0]
562
0
            ret.pushKVEnd(address, std::move(value));
563
0
        }
564
0
    });
565
566
0
    if (ret.empty()) {
  Branch (566:9): [True: 0, False: 0]
567
0
        throw JSONRPCError(RPC_WALLET_INVALID_LABEL_NAME, std::string("No addresses with label " + label));
568
0
    }
569
570
0
    return ret;
571
0
},
572
22.1k
    };
573
22.1k
}
574
575
RPCHelpMan listlabels()
576
22.1k
{
577
22.1k
    return RPCHelpMan{
578
22.1k
        "listlabels",
579
22.1k
        "Returns the list of all labels, or labels that are assigned to addresses with a specific purpose.\n",
580
22.1k
                {
581
22.1k
                    {"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."},
582
22.1k
                },
583
22.1k
                RPCResult{
584
22.1k
                    RPCResult::Type::ARR, "", "",
585
22.1k
                    {
586
22.1k
                        {RPCResult::Type::STR, "label", "Label name"},
587
22.1k
                    }
588
22.1k
                },
589
22.1k
                RPCExamples{
590
22.1k
            "\nList all labels\n"
591
22.1k
            + HelpExampleCli("listlabels", "") +
592
22.1k
            "\nList labels that have receiving addresses\n"
593
22.1k
            + HelpExampleCli("listlabels", "receive") +
594
22.1k
            "\nList labels that have sending addresses\n"
595
22.1k
            + HelpExampleCli("listlabels", "send") +
596
22.1k
            "\nAs a JSON-RPC call\n"
597
22.1k
            + HelpExampleRpc("listlabels", "receive")
598
22.1k
                },
599
22.1k
        [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
600
22.1k
{
601
0
    const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
602
0
    if (!pwallet) return UniValue::VNULL;
  Branch (602:9): [True: 0, False: 0]
603
604
0
    LOCK(pwallet->cs_wallet);
605
606
0
    std::optional<AddressPurpose> purpose;
607
0
    if (!request.params[0].isNull()) {
  Branch (607:9): [True: 0, False: 0]
608
0
        std::string purpose_str = request.params[0].get_str();
609
0
        if (!purpose_str.empty()) {
  Branch (609:13): [True: 0, False: 0]
610
0
            purpose = PurposeFromString(purpose_str);
611
0
            if (!purpose) {
  Branch (611:17): [True: 0, False: 0]
612
0
                throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid 'purpose' argument, must be a known purpose string, typically 'send', or 'receive'.");
613
0
            }
614
0
        }
615
0
    }
616
617
    // Add to a set to sort by label name, then insert into Univalue array
618
0
    std::set<std::string> label_set = pwallet->ListAddrBookLabels(purpose);
619
620
0
    UniValue ret(UniValue::VARR);
621
0
    for (const std::string& name : label_set) {
  Branch (621:34): [True: 0, False: 0]
622
0
        ret.push_back(name);
623
0
    }
624
625
0
    return ret;
626
0
},
627
22.1k
    };
628
22.1k
}
629
630
631
#ifdef ENABLE_EXTERNAL_SIGNER
632
RPCHelpMan walletdisplayaddress()
633
22.1k
{
634
22.1k
    return RPCHelpMan{
635
22.1k
        "walletdisplayaddress",
636
22.1k
        "Display address on an external signer for verification.",
637
22.1k
        {
638
22.1k
            {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "bitcoin address to display"},
639
22.1k
        },
640
22.1k
        RPCResult{
641
22.1k
            RPCResult::Type::OBJ,"","",
642
22.1k
            {
643
22.1k
                {RPCResult::Type::STR, "address", "The address as confirmed by the signer"},
644
22.1k
            }
645
22.1k
        },
646
22.1k
        RPCExamples{""},
647
22.1k
        [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
648
22.1k
        {
649
0
            std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
650
0
            if (!wallet) return UniValue::VNULL;
  Branch (650:17): [True: 0, False: 0]
651
0
            CWallet* const pwallet = wallet.get();
652
653
0
            LOCK(pwallet->cs_wallet);
654
655
0
            CTxDestination dest = DecodeDestination(request.params[0].get_str());
656
657
            // Make sure the destination is valid
658
0
            if (!IsValidDestination(dest)) {
  Branch (658:17): [True: 0, False: 0]
659
0
                throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address");
660
0
            }
661
662
0
            util::Result<void> res = pwallet->DisplayAddress(dest);
663
0
            if (!res) throw JSONRPCError(RPC_MISC_ERROR, util::ErrorString(res).original);
  Branch (663:17): [True: 0, False: 0]
664
665
0
            UniValue result(UniValue::VOBJ);
666
0
            result.pushKV("address", request.params[0].get_str());
667
0
            return result;
668
0
        }
669
22.1k
    };
670
22.1k
}
671
#endif // ENABLE_EXTERNAL_SIGNER
672
} // namespace wallet