Coverage Report

Created: 2026-04-29 19:21

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