Coverage Report

Created: 2026-05-30 09:47

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.9k
{
23
11.9k
    return RPCMethod{
24
11.9k
        "getnewaddress",
25
11.9k
        "Returns a new Bitcoin address for receiving payments.\n"
26
11.9k
                "If 'label' is specified, it is added to the address book \n"
27
11.9k
                "so payments received with the address will be associated with 'label'.\n",
28
11.9k
                {
29
11.9k
                    {"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.9k
                    {"address_type", RPCArg::Type::STR, RPCArg::DefaultHint{"set by -addresstype"}, "The address type to use. Options are " + FormatAllOutputTypes() + "."},
31
11.9k
                },
32
11.9k
                RPCResult{
33
11.9k
                    RPCResult::Type::STR, "address", "The new bitcoin address"
34
11.9k
                },
35
11.9k
                RPCExamples{
36
11.9k
                    HelpExampleCli("getnewaddress", "")
37
11.9k
            + HelpExampleRpc("getnewaddress", "")
38
11.9k
                },
39
11.9k
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
40
11.9k
{
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.9k
    };
70
11.9k
}
71
72
RPCMethod getrawchangeaddress()
73
1.13k
{
74
1.13k
    return RPCMethod{
75
1.13k
        "getrawchangeaddress",
76
1.13k
        "Returns a new Bitcoin address, for receiving change.\n"
77
1.13k
                "This is for use with raw transactions, NOT normal use.\n",
78
1.13k
                {
79
1.13k
                    {"address_type", RPCArg::Type::STR, RPCArg::DefaultHint{"set by -changetype"}, "The address type to use. Options are " + FormatAllOutputTypes() + "."},
80
1.13k
                },
81
1.13k
                RPCResult{
82
1.13k
                    RPCResult::Type::STR, "address", "The address"
83
1.13k
                },
84
1.13k
                RPCExamples{
85
1.13k
                    HelpExampleCli("getrawchangeaddress", "")
86
1.13k
            + HelpExampleRpc("getrawchangeaddress", "")
87
1.13k
                },
88
1.13k
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
89
1.13k
{
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.13k
    };
115
1.13k
}
116
117
118
RPCMethod setlabel()
119
833
{
120
833
    return RPCMethod{
121
833
        "setlabel",
122
833
        "Sets the label associated with the given address.\n",
123
833
                {
124
833
                    {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The bitcoin address to be associated with a label."},
125
833
                    {"label", RPCArg::Type::STR, RPCArg::Optional::NO, "The label to assign to the address."},
126
833
                },
127
833
                RPCResult{RPCResult::Type::NONE, "", ""},
128
833
                RPCExamples{
129
833
                    HelpExampleCli("setlabel", "\"" + EXAMPLE_ADDRESS[0] + "\" \"tabby\"")
130
833
            + HelpExampleRpc("setlabel", "\"" + EXAMPLE_ADDRESS[0] + "\", \"tabby\"")
131
833
                },
132
833
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
133
833
{
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
833
    };
155
833
}
156
157
RPCMethod listaddressgroupings()
158
819
{
159
819
    return RPCMethod{
160
819
        "listaddressgroupings",
161
819
        "Lists groups of addresses which have had their common ownership\n"
162
819
                "made public by common use as inputs or as the resulting change\n"
163
819
                "in past transactions\n",
164
819
                {},
165
819
                RPCResult{
166
819
                    RPCResult::Type::ARR, "", "",
167
819
                    {
168
819
                        {RPCResult::Type::ARR, "", "",
169
819
                        {
170
819
                            {RPCResult::Type::ARR_FIXED, "", "",
171
819
                            {
172
819
                                {RPCResult::Type::STR, "address", "The bitcoin address"},
173
819
                                {RPCResult::Type::STR_AMOUNT, "amount", "The amount in " + CURRENCY_UNIT},
174
819
                                {RPCResult::Type::STR, "label", /*optional=*/true, "The label"},
175
819
                            }},
176
819
                        }},
177
819
                    }
178
819
                },
179
819
                RPCExamples{
180
819
                    HelpExampleCli("listaddressgroupings", "")
181
819
            + HelpExampleRpc("listaddressgroupings", "")
182
819
                },
183
819
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
184
819
{
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
819
    };
216
819
}
217
218
RPCMethod keypoolrefill()
219
827
{
220
827
    return RPCMethod{"keypoolrefill",
221
827
                "Refills each descriptor keypool in the wallet up to the specified number of new keys.\n"
222
827
                "By default, descriptor wallets have 4 active ranged descriptors (" + FormatAllOutputTypes() + "), each with " + util::ToString(DEFAULT_KEYPOOL_SIZE) + " entries.\n" +
223
827
        HELP_REQUIRING_PASSPHRASE,
224
827
                {
225
827
                    {"newsize", RPCArg::Type::NUM, RPCArg::DefaultHint{strprintf("%u, or as set by -keypool", DEFAULT_KEYPOOL_SIZE)}, "The new keypool size"},
226
827
                },
227
827
                RPCResult{RPCResult::Type::NONE, "", ""},
228
827
                RPCExamples{
229
827
                    HelpExampleCli("keypoolrefill", "")
230
827
            + HelpExampleRpc("keypoolrefill", "")
231
827
                },
232
827
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
233
827
{
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
827
    };
258
827
}
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
124
    {
268
        // Always present: script type and redeemscript
269
124
        std::vector<std::vector<unsigned char>> solutions_data;
270
124
        TxoutType which_type = Solver(subscript, solutions_data);
271
124
        obj.pushKV("script", GetTxnOutputType(which_type));
272
124
        obj.pushKV("hex", HexStr(subscript));
273
274
124
        CTxDestination embedded;
275
124
        if (ExtractDestination(subscript, embedded)) {
276
            // Only when the script corresponds to an address.
277
106
            UniValue subobj(UniValue::VOBJ);
278
106
            UniValue detail = DescribeAddress(embedded);
279
106
            subobj.pushKVs(std::move(detail));
280
106
            UniValue wallet_detail = std::visit(*this, embedded);
281
106
            subobj.pushKVs(std::move(wallet_detail));
282
106
            subobj.pushKV("address", EncodeDestination(embedded));
283
106
            subobj.pushKV("scriptPubKey", HexStr(subscript));
284
            // Always report the pubkey at the top level, so that `getnewaddress()['pubkey']` always works.
285
106
            if (subobj.exists("pubkey")) obj.pushKV("pubkey", subobj["pubkey"]);
286
106
            obj.pushKV("embedded", std::move(subobj));
287
106
        } 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
124
    }
298
299
743
    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
115
    {
306
115
        CKeyID keyID{ToKeyID(pkhash)};
307
115
        UniValue obj(UniValue::VOBJ);
308
115
        CPubKey vchPubKey;
309
115
        if (provider && provider->GetPubKey(keyID, vchPubKey)) {
310
111
            obj.pushKV("pubkey", HexStr(vchPubKey));
311
111
            obj.pushKV("iscompressed", vchPubKey.IsCompressed());
312
111
        }
313
115
        return obj;
314
115
    }
315
316
    // NOLINTNEXTLINE(misc-no-recursion)
317
    UniValue operator()(const ScriptHash& scripthash) const
318
104
    {
319
104
        UniValue obj(UniValue::VOBJ);
320
104
        CScript subscript;
321
104
        if (provider && provider->GetCScript(ToScriptID(scripthash), subscript)) {
322
102
            ProcessSubScript(subscript, obj);
323
102
        }
324
104
        return obj;
325
104
    }
326
327
    UniValue operator()(const WitnessV0KeyHash& id) const
328
484
    {
329
484
        UniValue obj(UniValue::VOBJ);
330
484
        CPubKey pubkey;
331
484
        if (provider && provider->GetPubKey(ToKeyID(id), pubkey)) {
332
422
            obj.pushKV("pubkey", HexStr(pubkey));
333
422
        }
334
484
        return obj;
335
484
    }
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
107
    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
743
{
358
743
    UniValue ret(UniValue::VOBJ);
359
743
    UniValue detail = DescribeAddress(dest);
360
743
    CScript script = GetScriptForDestination(dest);
361
743
    std::unique_ptr<SigningProvider> provider = nullptr;
362
743
    provider = wallet.GetSolvingProvider(script);
363
743
    ret.pushKVs(std::move(detail));
364
743
    ret.pushKVs(std::visit(DescribeWalletAddressVisitor(provider.get()), dest));
365
743
    return ret;
366
743
}
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
748
    const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
426
748
    if (!pwallet) return UniValue::VNULL;
427
428
748
    LOCK(pwallet->cs_wallet);
429
430
748
    std::string error_msg;
431
748
    CTxDestination dest = DecodeDestination(request.params[0].get_str(), error_msg);
432
433
    // Make sure the destination is valid
434
748
    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
743
    UniValue ret(UniValue::VOBJ);
442
443
743
    std::string currentAddress = EncodeDestination(dest);
444
743
    ret.pushKV("address", currentAddress);
445
446
743
    CScript scriptPubKey = GetScriptForDestination(dest);
447
743
    ret.pushKV("scriptPubKey", HexStr(scriptPubKey));
448
449
743
    std::unique_ptr<SigningProvider> provider = pwallet->GetSolvingProvider(scriptPubKey);
450
451
743
    bool mine = pwallet->IsMine(dest);
452
743
    ret.pushKV("ismine", mine);
453
454
743
    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
84
        ret.pushKV("solvable", false);
463
84
    }
464
465
743
    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
743
    ScriptPubKeyMan* spk_man{nullptr};
468
743
    if (spk_mans.size()) spk_man = *spk_mans.begin();
469
470
743
    DescriptorScriptPubKeyMan* desc_spk_man = dynamic_cast<DescriptorScriptPubKeyMan*>(spk_man);
471
743
    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
743
    ret.pushKV("iswatchonly", false);
479
480
743
    UniValue detail = DescribeWalletAddress(*pwallet, dest);
481
743
    ret.pushKVs(std::move(detail));
482
483
743
    ret.pushKV("ischange", ScriptIsChange(*pwallet, scriptPubKey));
484
485
743
    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
743
    UniValue labels(UniValue::VARR);
504
743
    const auto* address_book_entry = pwallet->FindAddressBookEntry(dest);
505
743
    if (address_book_entry) {
506
516
        labels.push_back(address_book_entry->GetLabel());
507
516
    }
508
743
    ret.pushKV("labels", std::move(labels));
509
510
743
    return ret;
511
748
},
512
1.56k
    };
513
1.56k
}
514
515
RPCMethod getaddressesbylabel()
516
856
{
517
856
    return RPCMethod{
518
856
        "getaddressesbylabel",
519
856
        "Returns the list of addresses assigned the specified label.\n",
520
856
                {
521
856
                    {"label", RPCArg::Type::STR, RPCArg::Optional::NO, "The label."},
522
856
                },
523
856
                RPCResult{
524
856
                    RPCResult::Type::OBJ_DYN, "", "json object with addresses as keys",
525
856
                    {
526
856
                        {RPCResult::Type::OBJ, "address", "json object with information about address",
527
856
                        {
528
856
                            {RPCResult::Type::STR, "purpose", "Purpose of address (\"send\" for sending address, \"receive\" for receiving address)"},
529
856
                        }},
530
856
                    }
531
856
                },
532
856
                RPCExamples{
533
856
                    HelpExampleCli("getaddressesbylabel", "\"tabby\"")
534
856
            + HelpExampleRpc("getaddressesbylabel", "\"tabby\"")
535
856
                },
536
856
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
537
856
{
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
856
    };
574
856
}
575
576
RPCMethod listlabels()
577
857
{
578
857
    return RPCMethod{
579
857
        "listlabels",
580
857
        "Returns the list of all labels, or labels that are assigned to addresses with a specific purpose.\n",
581
857
                {
582
857
                    {"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
857
                },
584
857
                RPCResult{
585
857
                    RPCResult::Type::ARR, "", "",
586
857
                    {
587
857
                        {RPCResult::Type::STR, "label", "Label name"},
588
857
                    }
589
857
                },
590
857
                RPCExamples{
591
857
            "\nList all labels\n"
592
857
            + HelpExampleCli("listlabels", "") +
593
857
            "\nList labels that have receiving addresses\n"
594
857
            + HelpExampleCli("listlabels", "receive") +
595
857
            "\nList labels that have sending addresses\n"
596
857
            + HelpExampleCli("listlabels", "send") +
597
857
            "\nAs a JSON-RPC call\n"
598
857
            + HelpExampleRpc("listlabels", "receive")
599
857
                },
600
857
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
601
857
{
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
857
    };
629
857
}
630
631
632
#ifdef ENABLE_EXTERNAL_SIGNER
633
RPCMethod walletdisplayaddress()
634
822
{
635
822
    return RPCMethod{
636
822
        "walletdisplayaddress",
637
822
        "Display address on an external signer for verification.",
638
822
        {
639
822
            {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "bitcoin address to display"},
640
822
        },
641
822
        RPCResult{
642
822
            RPCResult::Type::OBJ,"","",
643
822
            {
644
822
                {RPCResult::Type::STR, "address", "The address as confirmed by the signer"},
645
822
            }
646
822
        },
647
822
        RPCExamples{""},
648
822
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
649
822
        {
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
822
    };
671
822
}
672
#endif // ENABLE_EXTERNAL_SIGNER
673
} // namespace wallet