Coverage Report

Created: 2026-06-16 16:41

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/tmp/bitcoin/src/wallet/rpc/coins.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 <core_io.h>
6
#include <hash.h>
7
#include <key_io.h>
8
#include <rpc/util.h>
9
#include <script/script.h>
10
#include <util/moneystr.h>
11
#include <wallet/coincontrol.h>
12
#include <wallet/receive.h>
13
#include <wallet/rpc/util.h>
14
#include <wallet/spend.h>
15
#include <wallet/wallet.h>
16
17
#include <univalue.h>
18
19
20
namespace wallet {
21
static CAmount GetReceived(const CWallet& wallet, const UniValue& params, bool by_label) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet)
22
44
{
23
44
    std::vector<CTxDestination> addresses;
24
44
    if (by_label) {
25
        // Get the set of addresses assigned to label
26
25
        addresses = wallet.ListAddrBookAddresses(CWallet::AddrBookFilter{LabelFromValue(params[0])});
27
25
        if (addresses.empty()) throw JSONRPCError(RPC_WALLET_ERROR, "Label not found in wallet");
28
25
    } else {
29
        // Get the address
30
19
        CTxDestination dest = DecodeDestination(params[0].get_str());
31
19
        if (!IsValidDestination(dest)) {
32
1
            throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address");
33
1
        }
34
18
        addresses.emplace_back(dest);
35
18
    }
36
37
    // Filter by own scripts only
38
42
    std::set<CScript> output_scripts;
39
61
    for (const auto& address : addresses) {
40
61
        auto output_script{GetScriptForDestination(address)};
41
61
        if (wallet.IsMine(output_script)) {
42
60
            output_scripts.insert(output_script);
43
60
        }
44
61
    }
45
46
42
    if (output_scripts.empty()) {
47
1
        throw JSONRPCError(RPC_WALLET_ERROR, "Address not found in wallet");
48
1
    }
49
50
    // Minimum confirmations
51
41
    int min_depth = 1;
52
41
    if (!params[1].isNull())
53
3
        min_depth = params[1].getInt<int>();
54
55
41
    const bool include_immature_coinbase{params[2].isNull() ? false : params[2].get_bool()};
56
57
    // Tally
58
41
    CAmount amount = 0;
59
3.39k
    for (const auto& [_, wtx] : wallet.mapWallet) {
60
3.39k
        int depth{wallet.GetTxDepthInMainChain(wtx)};
61
3.39k
        if (depth < min_depth
62
            // Coinbase with less than 1 confirmation is no longer in the main chain
63
3.39k
            || (wtx.IsCoinBase() && (depth < 1))
64
3.39k
            || (wallet.IsTxImmatureCoinBase(wtx) && !include_immature_coinbase))
65
2.36k
        {
66
2.36k
            continue;
67
2.36k
        }
68
69
2.03k
        for (const CTxOut& txout : wtx.tx->vout) {
70
2.03k
            if (output_scripts.contains(txout.scriptPubKey)) {
71
50
                amount += txout.nValue;
72
50
            }
73
2.03k
        }
74
1.02k
    }
75
76
41
    return amount;
77
42
}
78
79
80
RPCMethod getreceivedbyaddress()
81
832
{
82
832
    return RPCMethod{
83
832
        "getreceivedbyaddress",
84
832
        "Returns the total amount received by the given address in transactions with at least minconf confirmations.\n",
85
832
                {
86
832
                    {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The bitcoin address for transactions."},
87
832
                    {"minconf", RPCArg::Type::NUM, RPCArg::Default{1}, "Only include transactions confirmed at least this many times."},
88
832
                    {"include_immature_coinbase", RPCArg::Type::BOOL, RPCArg::Default{false}, "Include immature coinbase transactions."},
89
832
                },
90
832
                RPCResult{
91
832
                    RPCResult::Type::STR_AMOUNT, "amount", "The total amount in " + CURRENCY_UNIT + " received at this address."
92
832
                },
93
832
                RPCExamples{
94
832
            "\nThe amount from transactions with at least 1 confirmation\n"
95
832
            + HelpExampleCli("getreceivedbyaddress", "\"" + EXAMPLE_ADDRESS[0] + "\"") +
96
832
            "\nThe amount including unconfirmed transactions, zero confirmations\n"
97
832
            + HelpExampleCli("getreceivedbyaddress", "\"" + EXAMPLE_ADDRESS[0] + "\" 0") +
98
832
            "\nThe amount with at least 6 confirmations\n"
99
832
            + HelpExampleCli("getreceivedbyaddress", "\"" + EXAMPLE_ADDRESS[0] + "\" 6") +
100
832
            "\nThe amount with at least 6 confirmations including immature coinbase outputs\n"
101
832
            + HelpExampleCli("getreceivedbyaddress", "\"" + EXAMPLE_ADDRESS[0] + "\" 6 true") +
102
832
            "\nAs a JSON-RPC call\n"
103
832
            + HelpExampleRpc("getreceivedbyaddress", "\"" + EXAMPLE_ADDRESS[0] + "\", 6")
104
832
                },
105
832
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
106
832
{
107
19
    const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
108
19
    if (!pwallet) return UniValue::VNULL;
109
110
    // Make sure the results are valid at least up to the most recent block
111
    // the user could have gotten from another RPC command prior to now
112
19
    pwallet->BlockUntilSyncedToCurrentChain();
113
114
19
    LOCK(pwallet->cs_wallet);
115
116
19
    return ValueFromAmount(GetReceived(*pwallet, request.params, /*by_label=*/false));
117
19
},
118
832
    };
119
832
}
120
121
122
RPCMethod getreceivedbylabel()
123
838
{
124
838
    return RPCMethod{
125
838
        "getreceivedbylabel",
126
838
        "Returns the total amount received by addresses with <label> in transactions with at least [minconf] confirmations.\n",
127
838
                {
128
838
                    {"label", RPCArg::Type::STR, RPCArg::Optional::NO, "The selected label, may be the default label using \"\"."},
129
838
                    {"minconf", RPCArg::Type::NUM, RPCArg::Default{1}, "Only include transactions confirmed at least this many times."},
130
838
                    {"include_immature_coinbase", RPCArg::Type::BOOL, RPCArg::Default{false}, "Include immature coinbase transactions."},
131
838
                },
132
838
                RPCResult{
133
838
                    RPCResult::Type::STR_AMOUNT, "amount", "The total amount in " + CURRENCY_UNIT + " received for this label."
134
838
                },
135
838
                RPCExamples{
136
838
            "\nAmount received by the default label with at least 1 confirmation\n"
137
838
            + HelpExampleCli("getreceivedbylabel", "\"\"") +
138
838
            "\nAmount received at the tabby label including unconfirmed amounts with zero confirmations\n"
139
838
            + HelpExampleCli("getreceivedbylabel", "\"tabby\" 0") +
140
838
            "\nThe amount with at least 6 confirmations\n"
141
838
            + HelpExampleCli("getreceivedbylabel", "\"tabby\" 6") +
142
838
            "\nThe amount with at least 6 confirmations including immature coinbase outputs\n"
143
838
            + HelpExampleCli("getreceivedbylabel", "\"tabby\" 6 true") +
144
838
            "\nAs a JSON-RPC call\n"
145
838
            + HelpExampleRpc("getreceivedbylabel", "\"tabby\", 6, true")
146
838
                },
147
838
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
148
838
{
149
25
    const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
150
25
    if (!pwallet) return UniValue::VNULL;
151
152
    // Make sure the results are valid at least up to the most recent block
153
    // the user could have gotten from another RPC command prior to now
154
25
    pwallet->BlockUntilSyncedToCurrentChain();
155
156
25
    LOCK(pwallet->cs_wallet);
157
158
25
    return ValueFromAmount(GetReceived(*pwallet, request.params, /*by_label=*/true));
159
25
},
160
838
    };
161
838
}
162
163
164
RPCMethod getbalance()
165
1.31k
{
166
1.31k
    return RPCMethod{
167
1.31k
        "getbalance",
168
1.31k
        "Returns the total available balance.\n"
169
1.31k
                "The available balance is what the wallet considers currently spendable, and is\n"
170
1.31k
                "thus affected by options which limit spendability such as -spendzeroconfchange.\n",
171
1.31k
                {
172
1.31k
                    {"dummy", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "Remains for backward compatibility. Must be excluded or set to \"*\"."},
173
1.31k
                    {"minconf", RPCArg::Type::NUM, RPCArg::Default{0}, "Only include transactions confirmed at least this many times."},
174
1.31k
                    {"include_watchonly", RPCArg::Type::BOOL, RPCArg::Default{false}, "No longer used"},
175
1.31k
                    {"avoid_reuse", RPCArg::Type::BOOL, RPCArg::Default{true}, "(only available if avoid_reuse wallet flag is set) Do not include balance in dirty outputs; addresses are considered dirty if they have previously been used in a transaction."},
176
1.31k
                },
177
1.31k
                RPCResult{
178
1.31k
                    RPCResult::Type::STR_AMOUNT, "amount", "The total amount in " + CURRENCY_UNIT + " received for this wallet."
179
1.31k
                },
180
1.31k
                RPCExamples{
181
1.31k
            "\nThe total amount in the wallet with 0 or more confirmations\n"
182
1.31k
            + HelpExampleCli("getbalance", "") +
183
1.31k
            "\nThe total amount in the wallet with at least 6 confirmations\n"
184
1.31k
            + HelpExampleCli("getbalance", "\"*\" 6") +
185
1.31k
            "\nAs a JSON-RPC call\n"
186
1.31k
            + HelpExampleRpc("getbalance", "\"*\", 6")
187
1.31k
                },
188
1.31k
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
189
1.31k
{
190
500
    const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
191
500
    if (!pwallet) return UniValue::VNULL;
192
193
    // Make sure the results are valid at least up to the most recent block
194
    // the user could have gotten from another RPC command prior to now
195
500
    pwallet->BlockUntilSyncedToCurrentChain();
196
197
500
    LOCK(pwallet->cs_wallet);
198
199
500
    if (self.MaybeArg<std::string_view>("dummy").value_or("*") != "*") {
200
1
        throw JSONRPCError(RPC_METHOD_DEPRECATED, "dummy first argument must be excluded or set to \"*\".");
201
1
    }
202
203
499
    const auto min_depth{self.Arg<int>("minconf")};
204
205
499
    bool avoid_reuse = GetAvoidReuseFlag(*pwallet, request.params[3]);
206
207
499
    const auto bal = GetBalance(*pwallet, min_depth, avoid_reuse);
208
209
499
    return ValueFromAmount(bal.m_mine_trusted);
210
500
},
211
1.31k
    };
212
1.31k
}
213
214
RPCMethod lockunspent()
215
848
{
216
848
    return RPCMethod{
217
848
        "lockunspent",
218
848
        "Updates list of temporarily unspendable outputs.\n"
219
848
                "Temporarily lock (unlock=false) or unlock (unlock=true) specified transaction outputs.\n"
220
848
                "If no transaction outputs are specified when unlocking then all current locked transaction outputs are unlocked.\n"
221
848
                "A locked transaction output will not be chosen by automatic coin selection, when spending bitcoins.\n"
222
848
                "Manually selected coins are automatically unlocked.\n"
223
848
                "Locks are stored in memory only, unless persistent=true, in which case they will be written to the\n"
224
848
                "wallet database and loaded on node start. Unwritten (persistent=false) locks are always cleared\n"
225
848
                "(by virtue of process exit) when a node stops or fails. Unlocking will clear both persistent and not.\n"
226
848
                "Also see the listunspent call\n",
227
848
                {
228
848
                    {"unlock", RPCArg::Type::BOOL, RPCArg::Optional::NO, "Whether to unlock (true) or lock (false) the specified transactions"},
229
848
                    {"transactions", RPCArg::Type::ARR, RPCArg::Default{UniValue::VARR}, "The transaction outputs and within each, the txid (string) vout (numeric).",
230
848
                        {
231
848
                            {"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "",
232
848
                                {
233
848
                                    {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id"},
234
848
                                    {"vout", RPCArg::Type::NUM, RPCArg::Optional::NO, "The output number"},
235
848
                                },
236
848
                            },
237
848
                        },
238
848
                    },
239
848
                    {"persistent", RPCArg::Type::BOOL, RPCArg::Default{false}, "Whether to write/erase this lock in the wallet database, or keep the change in memory only. Ignored for unlocking."},
240
848
                },
241
848
                RPCResult{
242
848
                    RPCResult::Type::BOOL, "", "Whether the command was successful or not"
243
848
                },
244
848
                RPCExamples{
245
848
            "\nList the unspent transactions\n"
246
848
            + HelpExampleCli("listunspent", "") +
247
848
            "\nLock an unspent transaction\n"
248
848
            + HelpExampleCli("lockunspent", "false \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"") +
249
848
            "\nList the locked transactions\n"
250
848
            + HelpExampleCli("listlockunspent", "") +
251
848
            "\nUnlock the transaction again\n"
252
848
            + HelpExampleCli("lockunspent", "true \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"") +
253
848
            "\nLock the transaction persistently in the wallet database\n"
254
848
            + HelpExampleCli("lockunspent", "false \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\" true") +
255
848
            "\nAs a JSON-RPC call\n"
256
848
            + HelpExampleRpc("lockunspent", "false, \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"")
257
848
                },
258
848
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
259
848
{
260
35
    std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
261
35
    if (!pwallet) return UniValue::VNULL;
262
263
    // Make sure the results are valid at least up to the most recent block
264
    // the user could have gotten from another RPC command prior to now
265
35
    pwallet->BlockUntilSyncedToCurrentChain();
266
267
35
    LOCK(pwallet->cs_wallet);
268
269
35
    bool fUnlock = request.params[0].get_bool();
270
271
35
    const bool persistent{request.params[2].isNull() ? false : request.params[2].get_bool()};
272
273
35
    if (request.params[1].isNull()) {
274
4
        if (fUnlock) {
275
4
            if (!pwallet->UnlockAllCoins())
276
0
                throw JSONRPCError(RPC_WALLET_ERROR, "Unlocking coins failed");
277
4
        }
278
4
        return true;
279
4
    }
280
281
31
    const UniValue& output_params = request.params[1].get_array();
282
283
    // Create and validate the COutPoints first.
284
285
31
    std::vector<COutPoint> outputs;
286
31
    outputs.reserve(output_params.size());
287
288
99
    for (unsigned int idx = 0; idx < output_params.size(); idx++) {
289
75
        const UniValue& o = output_params[idx].get_obj();
290
291
75
        RPCTypeCheckObj(o,
292
75
            {
293
75
                {"txid", UniValueType(UniValue::VSTR)},
294
75
                {"vout", UniValueType(UniValue::VNUM)},
295
75
            });
296
297
75
        const Txid txid = Txid::FromUint256(ParseHashO(o, "txid"));
298
75
        const int nOutput = o.find_value("vout").getInt<int>();
299
75
        if (nOutput < 0) {
300
0
            throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout cannot be negative");
301
0
        }
302
303
75
        const COutPoint outpt(txid, nOutput);
304
305
75
        const auto it = pwallet->mapWallet.find(outpt.hash);
306
75
        if (it == pwallet->mapWallet.end()) {
307
1
            throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, unknown transaction");
308
1
        }
309
310
74
        const CWalletTx& trans = it->second;
311
312
74
        if (outpt.n >= trans.tx->vout.size()) {
313
1
            throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout index out of bounds");
314
1
        }
315
316
73
        if (pwallet->IsSpent(outpt)) {
317
1
            throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected unspent output");
318
1
        }
319
320
72
        const bool is_locked = pwallet->IsLockedCoin(outpt);
321
322
72
        if (fUnlock && !is_locked) {
323
1
            throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected locked output");
324
1
        }
325
326
71
        if (!fUnlock && is_locked && !persistent) {
327
3
            throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, output already locked");
328
3
        }
329
330
68
        outputs.push_back(outpt);
331
68
    }
332
333
    // Atomically set (un)locked status for the outputs.
334
66
    for (const COutPoint& outpt : outputs) {
335
66
        if (fUnlock) {
336
2
            if (!pwallet->UnlockCoin(outpt)) throw JSONRPCError(RPC_WALLET_ERROR, "Unlocking coin failed");
337
64
        } else {
338
64
            if (!pwallet->LockCoin(outpt, persistent)) throw JSONRPCError(RPC_WALLET_ERROR, "Locking coin failed");
339
64
        }
340
66
    }
341
342
24
    return true;
343
24
},
344
848
    };
345
848
}
346
347
RPCMethod listlockunspent()
348
822
{
349
822
    return RPCMethod{
350
822
        "listlockunspent",
351
822
        "Returns list of temporarily unspendable outputs.\n"
352
822
                "See the lockunspent call to lock and unlock transactions for spending.\n",
353
822
                {},
354
822
                RPCResult{
355
822
                    RPCResult::Type::ARR, "", "",
356
822
                    {
357
822
                        {RPCResult::Type::OBJ, "", "",
358
822
                        {
359
822
                            {RPCResult::Type::STR_HEX, "txid", "The transaction id locked"},
360
822
                            {RPCResult::Type::NUM, "vout", "The vout value"},
361
822
                        }},
362
822
                    }
363
822
                },
364
822
                RPCExamples{
365
822
            "\nList the unspent transactions\n"
366
822
            + HelpExampleCli("listunspent", "") +
367
822
            "\nLock an unspent transaction\n"
368
822
            + HelpExampleCli("lockunspent", "false \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"") +
369
822
            "\nList the locked transactions\n"
370
822
            + HelpExampleCli("listlockunspent", "") +
371
822
            "\nUnlock the transaction again\n"
372
822
            + HelpExampleCli("lockunspent", "true \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"") +
373
822
            "\nAs a JSON-RPC call\n"
374
822
            + HelpExampleRpc("listlockunspent", "")
375
822
                },
376
822
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
377
822
{
378
9
    const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
379
9
    if (!pwallet) return UniValue::VNULL;
380
381
9
    LOCK(pwallet->cs_wallet);
382
383
9
    std::vector<COutPoint> vOutpts;
384
9
    pwallet->ListLockedCoins(vOutpts);
385
386
9
    UniValue ret(UniValue::VARR);
387
388
9
    for (const COutPoint& outpt : vOutpts) {
389
4
        UniValue o(UniValue::VOBJ);
390
391
4
        o.pushKV("txid", outpt.hash.GetHex());
392
4
        o.pushKV("vout", outpt.n);
393
4
        ret.push_back(std::move(o));
394
4
    }
395
396
9
    return ret;
397
9
},
398
822
    };
399
822
}
400
401
RPCMethod getbalances()
402
1.46k
{
403
1.46k
    return RPCMethod{
404
1.46k
        "getbalances",
405
1.46k
        "Returns an object with all balances in " + CURRENCY_UNIT + ".\n",
406
1.46k
        {},
407
1.46k
        RPCResult{
408
1.46k
            RPCResult::Type::OBJ, "", "",
409
1.46k
            {
410
1.46k
                {RPCResult::Type::OBJ, "mine", "balances from outputs that the wallet can sign",
411
1.46k
                {
412
1.46k
                    {RPCResult::Type::STR_AMOUNT, "trusted", "trusted balance (outputs created by the wallet or confirmed outputs)"},
413
1.46k
                    {RPCResult::Type::STR_AMOUNT, "untrusted_pending", "untrusted pending balance (outputs created by others that are in the mempool)"},
414
1.46k
                    {RPCResult::Type::STR_AMOUNT, "immature", "balance from immature coinbase outputs"},
415
1.46k
                    {RPCResult::Type::STR_AMOUNT, "nonmempool", "sum of coins that are spent by transactions not in the mempool (usually an over-estimate due to not accounting for change or spends that conflict with each other)"},
416
1.46k
                    {RPCResult::Type::STR_AMOUNT, "used", /*optional=*/true, "(only present if avoid_reuse is set) balance from coins sent to addresses that were previously spent from (potentially privacy violating)"},
417
1.46k
                }},
418
1.46k
                RESULT_LAST_PROCESSED_BLOCK,
419
1.46k
            }
420
1.46k
            },
421
1.46k
        RPCExamples{
422
1.46k
            HelpExampleCli("getbalances", "") +
423
1.46k
            HelpExampleRpc("getbalances", "")},
424
1.46k
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
425
1.46k
{
426
650
    const std::shared_ptr<const CWallet> rpc_wallet = GetWalletForJSONRPCRequest(request);
427
650
    if (!rpc_wallet) return UniValue::VNULL;
428
650
    const CWallet& wallet = *rpc_wallet;
429
430
    // Make sure the results are valid at least up to the most recent block
431
    // the user could have gotten from another RPC command prior to now
432
650
    wallet.BlockUntilSyncedToCurrentChain();
433
434
650
    LOCK(wallet.cs_wallet);
435
436
650
    const auto bal = GetBalance(wallet, /*min_depth=*/0, /*avoid_reuse=*/true, /*include_nonmempool=*/true);
437
438
650
    UniValue balances{UniValue::VOBJ};
439
650
    {
440
650
        UniValue balances_mine{UniValue::VOBJ};
441
650
        balances_mine.pushKV("trusted", ValueFromAmount(bal.m_mine_trusted));
442
650
        balances_mine.pushKV("untrusted_pending", ValueFromAmount(bal.m_mine_untrusted_pending));
443
650
        balances_mine.pushKV("immature", ValueFromAmount(bal.m_mine_immature));
444
650
        balances_mine.pushKV("nonmempool", ValueFromAmount(bal.m_mine_nonmempool));
445
650
        if (wallet.IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE)) {
446
11
            balances_mine.pushKV("used", ValueFromAmount(bal.m_mine_used));
447
11
        }
448
650
        balances.pushKV("mine", std::move(balances_mine));
449
650
    }
450
650
    AppendLastProcessedBlock(balances, wallet);
451
650
    return balances;
452
650
},
453
1.46k
    };
454
1.46k
}
455
456
RPCMethod listunspent()
457
1.26k
{
458
1.26k
    return RPCMethod{
459
1.26k
        "listunspent",
460
1.26k
        "Returns array of unspent transaction outputs\n"
461
1.26k
                "with between minconf and maxconf (inclusive) confirmations.\n"
462
1.26k
                "Optionally filter to only include txouts paid to specified addresses.\n",
463
1.26k
                {
464
1.26k
                    {"minconf", RPCArg::Type::NUM, RPCArg::Default{1}, "The minimum confirmations to filter"},
465
1.26k
                    {"maxconf", RPCArg::Type::NUM, RPCArg::Default{9999999}, "The maximum confirmations to filter"},
466
1.26k
                    {"addresses", RPCArg::Type::ARR, RPCArg::Default{UniValue::VARR}, "The bitcoin addresses to filter",
467
1.26k
                        {
468
1.26k
                            {"address", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "bitcoin address"},
469
1.26k
                        },
470
1.26k
                    },
471
1.26k
                    {"include_unsafe", RPCArg::Type::BOOL, RPCArg::Default{true}, "Include outputs that are not safe to spend\n"
472
1.26k
                              "See description of \"safe\" attribute below."},
473
1.26k
                    {"query_options", RPCArg::Type::OBJ_NAMED_PARAMS, RPCArg::Optional::OMITTED, "",
474
1.26k
                        {
475
1.26k
                            {"minimumAmount", RPCArg::Type::AMOUNT, RPCArg::Default{FormatMoney(0)}, "Minimum value of each UTXO in " + CURRENCY_UNIT + ""},
476
1.26k
                            {"maximumAmount", RPCArg::Type::AMOUNT, RPCArg::DefaultHint{"unlimited"}, "Maximum value of each UTXO in " + CURRENCY_UNIT + ""},
477
1.26k
                            {"maximumCount", RPCArg::Type::NUM, RPCArg::DefaultHint{"unlimited"}, "Maximum number of UTXOs"},
478
1.26k
                            {"minimumSumAmount", RPCArg::Type::AMOUNT, RPCArg::DefaultHint{"unlimited"}, "Minimum sum value of all UTXOs in " + CURRENCY_UNIT + ""},
479
1.26k
                            {"include_immature_coinbase", RPCArg::Type::BOOL, RPCArg::Default{false}, "Include immature coinbase UTXOs"}
480
1.26k
                        },
481
1.26k
                        RPCArgOptions{.oneline_description="query_options"}},
482
1.26k
                },
483
1.26k
                RPCResult{
484
1.26k
                    RPCResult::Type::ARR, "", "",
485
1.26k
                    {
486
1.26k
                        {RPCResult::Type::OBJ, "", "",
487
1.26k
                        {
488
1.26k
                            {RPCResult::Type::STR_HEX, "txid", "the transaction id"},
489
1.26k
                            {RPCResult::Type::NUM, "vout", "the vout value"},
490
1.26k
                            {RPCResult::Type::STR, "address", /*optional=*/true, "the bitcoin address"},
491
1.26k
                            {RPCResult::Type::STR, "label", /*optional=*/true, "The associated label, or \"\" for the default label"},
492
1.26k
                            {RPCResult::Type::STR, "scriptPubKey", "the output script"},
493
1.26k
                            {RPCResult::Type::STR_AMOUNT, "amount", "the transaction output amount in " + CURRENCY_UNIT},
494
1.26k
                            {RPCResult::Type::NUM, "confirmations", "The number of confirmations"},
495
1.26k
                            {RPCResult::Type::NUM, "ancestorcount", /*optional=*/true, "The number of in-mempool ancestor transactions, including this one (if transaction is in the mempool)"},
496
1.26k
                            {RPCResult::Type::NUM, "ancestorsize", /*optional=*/true, "The virtual transaction size of in-mempool ancestors, including this one (if transaction is in the mempool)"},
497
1.26k
                            {RPCResult::Type::STR_AMOUNT, "ancestorfees", /*optional=*/true, "The total fees of in-mempool ancestors (including this one) with fee deltas used for mining priority in " + CURRENCY_ATOM + " (if transaction is in the mempool)"},
498
1.26k
                            {RPCResult::Type::STR_HEX, "redeemScript", /*optional=*/true, "The redeem script if the output script is P2SH"},
499
1.26k
                            {RPCResult::Type::STR, "witnessScript", /*optional=*/true, "witness script if the output script is P2WSH or P2SH-P2WSH"},
500
1.26k
                            {RPCResult::Type::BOOL, "spendable", "(DEPRECATED) Always true"},
501
1.26k
                            {RPCResult::Type::BOOL, "solvable", "Whether we know how to spend this output, ignoring the lack of keys"},
502
1.26k
                            {RPCResult::Type::BOOL, "reused", /*optional=*/true, "(only present if avoid_reuse is set) Whether this output is reused/dirty (sent to an address that was previously spent from)"},
503
1.26k
                            {RPCResult::Type::STR, "desc", /*optional=*/true, "(only when solvable) A descriptor for spending this output"},
504
1.26k
                            {RPCResult::Type::ARR, "parent_descs", /*optional=*/false, "List of parent descriptors for the output script of this coin.", {
505
1.26k
                                {RPCResult::Type::STR, "desc", "The descriptor string."},
506
1.26k
                            }},
507
1.26k
                            {RPCResult::Type::BOOL, "safe", "Whether this output is considered safe to spend. Unconfirmed transactions\n"
508
1.26k
                                                            "from outside keys and unconfirmed replacement transactions are considered unsafe\n"
509
1.26k
                                                            "and are not eligible for spending by fundrawtransaction and sendtoaddress."},
510
1.26k
                        }},
511
1.26k
                    }
512
1.26k
                },
513
1.26k
                RPCExamples{
514
1.26k
                    HelpExampleCli("listunspent", "")
515
1.26k
            + HelpExampleCli("listunspent", "6 9999999 \"[\\\"" + EXAMPLE_ADDRESS[0] + "\\\",\\\"" + EXAMPLE_ADDRESS[1] + "\\\"]\"")
516
1.26k
            + HelpExampleRpc("listunspent", "6, 9999999 \"[\\\"" + EXAMPLE_ADDRESS[0] + "\\\",\\\"" + EXAMPLE_ADDRESS[1] + "\\\"]\"")
517
1.26k
            + HelpExampleCli("listunspent", "6 9999999 '[]' true '{ \"minimumAmount\": 0.005 }'")
518
1.26k
            + HelpExampleRpc("listunspent", "6, 9999999, [] , true, { \"minimumAmount\": 0.005 } ")
519
1.26k
                },
520
1.26k
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
521
1.26k
{
522
447
    const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
523
447
    if (!pwallet) return UniValue::VNULL;
524
525
447
    int nMinDepth = 1;
526
447
    if (!request.params[0].isNull()) {
527
91
        nMinDepth = request.params[0].getInt<int>();
528
91
    }
529
530
447
    int nMaxDepth = 9999999;
531
447
    if (!request.params[1].isNull()) {
532
4
        nMaxDepth = request.params[1].getInt<int>();
533
4
    }
534
535
447
    std::set<CTxDestination> destinations;
536
447
    if (!request.params[2].isNull()) {
537
39
        UniValue inputs = request.params[2].get_array();
538
78
        for (unsigned int idx = 0; idx < inputs.size(); idx++) {
539
39
            const UniValue& input = inputs[idx];
540
39
            CTxDestination dest = DecodeDestination(input.get_str());
541
39
            if (!IsValidDestination(dest)) {
542
0
                throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Bitcoin address: ") + input.get_str());
543
0
            }
544
39
            if (!destinations.insert(dest).second) {
545
0
                throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Invalid parameter, duplicated address: ") + input.get_str());
546
0
            }
547
39
        }
548
39
    }
549
550
447
    bool include_unsafe = true;
551
447
    if (!request.params[3].isNull()) {
552
5
        include_unsafe = request.params[3].get_bool();
553
5
    }
554
555
447
    CoinFilterParams filter_coins;
556
447
    filter_coins.min_amount = 0;
557
558
447
    if (!request.params[4].isNull()) {
559
132
        const UniValue& options = request.params[4].get_obj();
560
561
132
        RPCTypeCheckObj(options,
562
132
            {
563
132
                {"minimumAmount", UniValueType()},
564
132
                {"maximumAmount", UniValueType()},
565
132
                {"minimumSumAmount", UniValueType()},
566
132
                {"maximumCount", UniValueType(UniValue::VNUM)},
567
132
                {"include_immature_coinbase", UniValueType(UniValue::VBOOL)}
568
132
            },
569
132
            true, true);
570
571
132
        if (options.exists("minimumAmount"))
572
128
            filter_coins.min_amount = AmountFromValue(options["minimumAmount"]);
573
574
132
        if (options.exists("maximumAmount"))
575
0
            filter_coins.max_amount = AmountFromValue(options["maximumAmount"]);
576
577
132
        if (options.exists("minimumSumAmount"))
578
0
            filter_coins.min_sum_amount = AmountFromValue(options["minimumSumAmount"]);
579
580
132
        if (options.exists("maximumCount"))
581
0
            filter_coins.max_count = options["maximumCount"].getInt<int64_t>();
582
583
132
        if (options.exists("include_immature_coinbase")) {
584
4
            filter_coins.include_immature_coinbase = options["include_immature_coinbase"].get_bool();
585
4
        }
586
132
    }
587
588
    // Make sure the results are valid at least up to the most recent block
589
    // the user could have gotten from another RPC command prior to now
590
447
    pwallet->BlockUntilSyncedToCurrentChain();
591
592
447
    UniValue results(UniValue::VARR);
593
447
    std::vector<COutput> vecOutputs;
594
447
    {
595
447
        CCoinControl cctl;
596
447
        cctl.m_avoid_address_reuse = false;
597
447
        cctl.m_min_depth = nMinDepth;
598
447
        cctl.m_max_depth = nMaxDepth;
599
447
        cctl.m_include_unsafe_inputs = include_unsafe;
600
447
        filter_coins.check_version_trucness = false;
601
447
        LOCK(pwallet->cs_wallet);
602
447
        vecOutputs = AvailableCoins(*pwallet, &cctl, /*feerate=*/std::nullopt, filter_coins).All();
603
447
    }
604
605
447
    LOCK(pwallet->cs_wallet);
606
607
447
    const bool avoid_reuse = pwallet->IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE);
608
609
35.0k
    for (const COutput& out : vecOutputs) {
610
35.0k
        CTxDestination address;
611
35.0k
        const CScript& scriptPubKey = out.txout.scriptPubKey;
612
35.0k
        bool fValidAddress = ExtractDestination(scriptPubKey, address);
613
35.0k
        bool reused = avoid_reuse && pwallet->IsSpentKey(scriptPubKey);
614
615
35.0k
        if (destinations.size() && (!fValidAddress || !destinations.contains(address)))
616
206
            continue;
617
618
34.8k
        UniValue entry(UniValue::VOBJ);
619
34.8k
        entry.pushKV("txid", out.outpoint.hash.GetHex());
620
34.8k
        entry.pushKV("vout", out.outpoint.n);
621
622
34.8k
        if (fValidAddress) {
623
34.8k
            entry.pushKV("address", EncodeDestination(address));
624
625
34.8k
            const auto* address_book_entry = pwallet->FindAddressBookEntry(address);
626
34.8k
            if (address_book_entry) {
627
34.5k
                entry.pushKV("label", address_book_entry->GetLabel());
628
34.5k
            }
629
630
34.8k
            std::unique_ptr<SigningProvider> provider = pwallet->GetSolvingProvider(scriptPubKey);
631
34.8k
            if (provider) {
632
34.8k
                if (scriptPubKey.IsPayToScriptHash()) {
633
6.78k
                    const CScriptID hash = ToScriptID(std::get<ScriptHash>(address));
634
6.78k
                    CScript redeemScript;
635
6.78k
                    if (provider->GetCScript(hash, redeemScript)) {
636
6.78k
                        entry.pushKV("redeemScript", HexStr(redeemScript));
637
                        // Now check if the redeemScript is actually a P2WSH script
638
6.78k
                        CTxDestination witness_destination;
639
6.78k
                        if (redeemScript.IsPayToWitnessScriptHash()) {
640
4
                            bool extracted = ExtractDestination(redeemScript, witness_destination);
641
4
                            CHECK_NONFATAL(extracted);
642
                            // Also return the witness script
643
4
                            const WitnessV0ScriptHash& whash = std::get<WitnessV0ScriptHash>(witness_destination);
644
4
                            CScriptID id{RIPEMD160(whash)};
645
4
                            CScript witnessScript;
646
4
                            if (provider->GetCScript(id, witnessScript)) {
647
4
                                entry.pushKV("witnessScript", HexStr(witnessScript));
648
4
                            }
649
4
                        }
650
6.78k
                    }
651
28.0k
                } else if (scriptPubKey.IsPayToWitnessScriptHash()) {
652
30
                    const WitnessV0ScriptHash& whash = std::get<WitnessV0ScriptHash>(address);
653
30
                    CScriptID id{RIPEMD160(whash)};
654
30
                    CScript witnessScript;
655
30
                    if (provider->GetCScript(id, witnessScript)) {
656
30
                        entry.pushKV("witnessScript", HexStr(witnessScript));
657
30
                    }
658
30
                }
659
34.8k
            }
660
34.8k
        }
661
662
34.8k
        entry.pushKV("scriptPubKey", HexStr(scriptPubKey));
663
34.8k
        entry.pushKV("amount", ValueFromAmount(out.txout.nValue));
664
34.8k
        entry.pushKV("confirmations", out.depth);
665
34.8k
        if (!out.depth) {
666
75
            size_t ancestor_count, unused_cluster_count, ancestor_size;
667
75
            CAmount ancestor_fees;
668
75
            pwallet->chain().getTransactionAncestry(out.outpoint.hash, ancestor_count, unused_cluster_count, &ancestor_size, &ancestor_fees);
669
75
            if (ancestor_count) {
670
75
                entry.pushKV("ancestorcount", ancestor_count);
671
75
                entry.pushKV("ancestorsize", ancestor_size);
672
75
                entry.pushKV("ancestorfees", ancestor_fees);
673
75
            }
674
75
        }
675
34.8k
        entry.pushKV("spendable", true); // Any coins we list are always spendable
676
34.8k
        entry.pushKV("solvable", out.solvable);
677
34.8k
        if (out.solvable) {
678
34.8k
            std::unique_ptr<SigningProvider> provider = pwallet->GetSolvingProvider(scriptPubKey);
679
34.8k
            if (provider) {
680
34.8k
                auto descriptor = InferDescriptor(scriptPubKey, *provider);
681
34.8k
                entry.pushKV("desc", descriptor->ToString());
682
34.8k
            }
683
34.8k
        }
684
34.8k
        PushParentDescriptors(*pwallet, scriptPubKey, entry);
685
34.8k
        if (avoid_reuse) entry.pushKV("reused", reused);
686
34.8k
        entry.pushKV("safe", out.safe);
687
34.8k
        results.push_back(std::move(entry));
688
34.8k
    }
689
690
447
    return results;
691
447
},
692
1.26k
    };
693
1.26k
}
694
} // namespace wallet