Coverage Report

Created: 2026-07-14 18:13

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