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/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
830
{
82
830
    return RPCMethod{
83
830
        "getreceivedbyaddress",
84
830
        "Returns the total amount received by the given address in transactions with at least minconf confirmations.\n",
85
830
                {
86
830
                    {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The bitcoin address for transactions."},
87
830
                    {"minconf", RPCArg::Type::NUM, RPCArg::Default{1}, "Only include transactions confirmed at least this many times."},
88
830
                    {"include_immature_coinbase", RPCArg::Type::BOOL, RPCArg::Default{false}, "Include immature coinbase transactions."},
89
830
                },
90
830
                RPCResult{
91
830
                    RPCResult::Type::STR_AMOUNT, "amount", "The total amount in " + CURRENCY_UNIT + " received at this address."
92
830
                },
93
830
                RPCExamples{
94
830
            "\nThe amount from transactions with at least 1 confirmation\n"
95
830
            + HelpExampleCli("getreceivedbyaddress", "\"" + EXAMPLE_ADDRESS[0] + "\"") +
96
830
            "\nThe amount including unconfirmed transactions, zero confirmations\n"
97
830
            + HelpExampleCli("getreceivedbyaddress", "\"" + EXAMPLE_ADDRESS[0] + "\" 0") +
98
830
            "\nThe amount with at least 6 confirmations\n"
99
830
            + HelpExampleCli("getreceivedbyaddress", "\"" + EXAMPLE_ADDRESS[0] + "\" 6") +
100
830
            "\nThe amount with at least 6 confirmations including immature coinbase outputs\n"
101
830
            + HelpExampleCli("getreceivedbyaddress", "\"" + EXAMPLE_ADDRESS[0] + "\" 6 true") +
102
830
            "\nAs a JSON-RPC call\n"
103
830
            + HelpExampleRpc("getreceivedbyaddress", "\"" + EXAMPLE_ADDRESS[0] + "\", 6")
104
830
                },
105
830
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
106
830
{
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
830
    };
119
830
}
120
121
122
RPCMethod getreceivedbylabel()
123
836
{
124
836
    return RPCMethod{
125
836
        "getreceivedbylabel",
126
836
        "Returns the total amount received by addresses with <label> in transactions with at least [minconf] confirmations.\n",
127
836
                {
128
836
                    {"label", RPCArg::Type::STR, RPCArg::Optional::NO, "The selected label, may be the default label using \"\"."},
129
836
                    {"minconf", RPCArg::Type::NUM, RPCArg::Default{1}, "Only include transactions confirmed at least this many times."},
130
836
                    {"include_immature_coinbase", RPCArg::Type::BOOL, RPCArg::Default{false}, "Include immature coinbase transactions."},
131
836
                },
132
836
                RPCResult{
133
836
                    RPCResult::Type::STR_AMOUNT, "amount", "The total amount in " + CURRENCY_UNIT + " received for this label."
134
836
                },
135
836
                RPCExamples{
136
836
            "\nAmount received by the default label with at least 1 confirmation\n"
137
836
            + HelpExampleCli("getreceivedbylabel", "\"\"") +
138
836
            "\nAmount received at the tabby label including unconfirmed amounts with zero confirmations\n"
139
836
            + HelpExampleCli("getreceivedbylabel", "\"tabby\" 0") +
140
836
            "\nThe amount with at least 6 confirmations\n"
141
836
            + HelpExampleCli("getreceivedbylabel", "\"tabby\" 6") +
142
836
            "\nThe amount with at least 6 confirmations including immature coinbase outputs\n"
143
836
            + HelpExampleCli("getreceivedbylabel", "\"tabby\" 6 true") +
144
836
            "\nAs a JSON-RPC call\n"
145
836
            + HelpExampleRpc("getreceivedbylabel", "\"tabby\", 6, true")
146
836
                },
147
836
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
148
836
{
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
836
    };
161
836
}
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
846
{
216
846
    return RPCMethod{
217
846
        "lockunspent",
218
846
        "Updates list of temporarily unspendable outputs.\n"
219
846
                "Temporarily lock (unlock=false) or unlock (unlock=true) specified transaction outputs.\n"
220
846
                "If no transaction outputs are specified when unlocking then all current locked transaction outputs are unlocked.\n"
221
846
                "A locked transaction output will not be chosen by automatic coin selection, when spending bitcoins.\n"
222
846
                "Manually selected coins are automatically unlocked.\n"
223
846
                "Locks are stored in memory only, unless persistent=true, in which case they will be written to the\n"
224
846
                "wallet database and loaded on node start. Unwritten (persistent=false) locks are always cleared\n"
225
846
                "(by virtue of process exit) when a node stops or fails. Unlocking will clear both persistent and not.\n"
226
846
                "Also see the listunspent call\n",
227
846
                {
228
846
                    {"unlock", RPCArg::Type::BOOL, RPCArg::Optional::NO, "Whether to unlock (true) or lock (false) the specified transactions"},
229
846
                    {"transactions", RPCArg::Type::ARR, RPCArg::Default{UniValue::VARR}, "The transaction outputs and within each, the txid (string) vout (numeric).",
230
846
                        {
231
846
                            {"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "",
232
846
                                {
233
846
                                    {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id"},
234
846
                                    {"vout", RPCArg::Type::NUM, RPCArg::Optional::NO, "The output number"},
235
846
                                },
236
846
                            },
237
846
                        },
238
846
                    },
239
846
                    {"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
846
                },
241
846
                RPCResult{
242
846
                    RPCResult::Type::BOOL, "", "Whether the command was successful or not"
243
846
                },
244
846
                RPCExamples{
245
846
            "\nList the unspent transactions\n"
246
846
            + HelpExampleCli("listunspent", "") +
247
846
            "\nLock an unspent transaction\n"
248
846
            + HelpExampleCli("lockunspent", "false \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"") +
249
846
            "\nList the locked transactions\n"
250
846
            + HelpExampleCli("listlockunspent", "") +
251
846
            "\nUnlock the transaction again\n"
252
846
            + HelpExampleCli("lockunspent", "true \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"") +
253
846
            "\nLock the transaction persistently in the wallet database\n"
254
846
            + HelpExampleCli("lockunspent", "false \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\" true") +
255
846
            "\nAs a JSON-RPC call\n"
256
846
            + HelpExampleRpc("lockunspent", "false, \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"")
257
846
                },
258
846
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
259
846
{
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
846
    };
345
846
}
346
347
RPCMethod listlockunspent()
348
820
{
349
820
    return RPCMethod{
350
820
        "listlockunspent",
351
820
        "Returns list of temporarily unspendable outputs.\n"
352
820
                "See the lockunspent call to lock and unlock transactions for spending.\n",
353
820
                {},
354
820
                RPCResult{
355
820
                    RPCResult::Type::ARR, "", "",
356
820
                    {
357
820
                        {RPCResult::Type::OBJ, "", "",
358
820
                        {
359
820
                            {RPCResult::Type::STR_HEX, "txid", "The transaction id locked"},
360
820
                            {RPCResult::Type::NUM, "vout", "The vout value"},
361
820
                        }},
362
820
                    }
363
820
                },
364
820
                RPCExamples{
365
820
            "\nList the unspent transactions\n"
366
820
            + HelpExampleCli("listunspent", "") +
367
820
            "\nLock an unspent transaction\n"
368
820
            + HelpExampleCli("lockunspent", "false \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"") +
369
820
            "\nList the locked transactions\n"
370
820
            + HelpExampleCli("listlockunspent", "") +
371
820
            "\nUnlock the transaction again\n"
372
820
            + HelpExampleCli("lockunspent", "true \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"") +
373
820
            "\nAs a JSON-RPC call\n"
374
820
            + HelpExampleRpc("listlockunspent", "")
375
820
                },
376
820
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
377
820
{
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
820
    };
399
820
}
400
401
RPCMethod getbalances()
402
1.47k
{
403
1.47k
    return RPCMethod{
404
1.47k
        "getbalances",
405
1.47k
        "Returns an object with all balances in " + CURRENCY_UNIT + ".\n",
406
1.47k
        {},
407
1.47k
        RPCResult{
408
1.47k
            RPCResult::Type::OBJ, "", "",
409
1.47k
            {
410
1.47k
                {RPCResult::Type::OBJ, "mine", "balances from outputs that the wallet can sign",
411
1.47k
                {
412
1.47k
                    {RPCResult::Type::STR_AMOUNT, "trusted", "trusted balance (outputs created by the wallet or confirmed outputs)"},
413
1.47k
                    {RPCResult::Type::STR_AMOUNT, "untrusted_pending", "untrusted pending balance (outputs created by others that are in the mempool)"},
414
1.47k
                    {RPCResult::Type::STR_AMOUNT, "immature", "balance from immature coinbase outputs"},
415
1.47k
                    {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.47k
                    {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.47k
                }},
418
1.47k
                RESULT_LAST_PROCESSED_BLOCK,
419
1.47k
            }
420
1.47k
            },
421
1.47k
        RPCExamples{
422
1.47k
            HelpExampleCli("getbalances", "") +
423
1.47k
            HelpExampleRpc("getbalances", "")},
424
1.47k
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
425
1.47k
{
426
659
    const std::shared_ptr<const CWallet> rpc_wallet = GetWalletForJSONRPCRequest(request);
427
659
    if (!rpc_wallet) return UniValue::VNULL;
428
659
    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
659
    wallet.BlockUntilSyncedToCurrentChain();
433
434
659
    LOCK(wallet.cs_wallet);
435
436
659
    const auto bal = GetBalance(wallet, /*min_depth=*/0, /*avoid_reuse=*/true, /*include_nonmempool=*/true);
437
438
659
    UniValue balances{UniValue::VOBJ};
439
659
    {
440
659
        UniValue balances_mine{UniValue::VOBJ};
441
659
        balances_mine.pushKV("trusted", ValueFromAmount(bal.m_mine_trusted));
442
659
        balances_mine.pushKV("untrusted_pending", ValueFromAmount(bal.m_mine_untrusted_pending));
443
659
        balances_mine.pushKV("immature", ValueFromAmount(bal.m_mine_immature));
444
659
        balances_mine.pushKV("nonmempool", ValueFromAmount(bal.m_mine_nonmempool));
445
659
        if (wallet.IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE)) {
446
11
            balances_mine.pushKV("used", ValueFromAmount(bal.m_mine_used));
447
11
        }
448
659
        balances.pushKV("mine", std::move(balances_mine));
449
659
    }
450
659
    AppendLastProcessedBlock(balances, wallet);
451
659
    return balances;
452
659
},
453
1.47k
    };
454
1.47k
}
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
452
    const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
523
452
    if (!pwallet) return UniValue::VNULL;
524
525
452
    int nMinDepth = 1;
526
452
    if (!request.params[0].isNull()) {
527
97
        nMinDepth = request.params[0].getInt<int>();
528
97
    }
529
530
452
    int nMaxDepth = 9999999;
531
452
    if (!request.params[1].isNull()) {
532
8
        nMaxDepth = request.params[1].getInt<int>();
533
8
    }
534
535
452
    std::set<CTxDestination> destinations;
536
452
    if (!request.params[2].isNull()) {
537
41
        UniValue inputs = request.params[2].get_array();
538
82
        for (unsigned int idx = 0; idx < inputs.size(); idx++) {
539
41
            const UniValue& input = inputs[idx];
540
41
            CTxDestination dest = DecodeDestination(input.get_str());
541
41
            if (!IsValidDestination(dest)) {
542
0
                throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Bitcoin address: ") + input.get_str());
543
0
            }
544
41
            if (!destinations.insert(dest).second) {
545
0
                throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Invalid parameter, duplicated address: ") + input.get_str());
546
0
            }
547
41
        }
548
41
    }
549
550
452
    bool include_unsafe = true;
551
452
    if (!request.params[3].isNull()) {
552
5
        include_unsafe = request.params[3].get_bool();
553
5
    }
554
555
452
    CoinFilterParams filter_coins;
556
452
    filter_coins.min_amount = 0;
557
558
452
    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
452
    pwallet->BlockUntilSyncedToCurrentChain();
591
592
452
    UniValue results(UniValue::VARR);
593
452
    std::vector<COutput> vecOutputs;
594
452
    {
595
452
        CCoinControl cctl;
596
452
        cctl.m_avoid_address_reuse = false;
597
452
        cctl.m_min_depth = nMinDepth;
598
452
        cctl.m_max_depth = nMaxDepth;
599
452
        cctl.m_include_unsafe_inputs = include_unsafe;
600
452
        filter_coins.check_version_trucness = false;
601
452
        LOCK(pwallet->cs_wallet);
602
452
        vecOutputs = AvailableCoins(*pwallet, &cctl, /*feerate=*/std::nullopt, filter_coins).All();
603
452
    }
604
605
452
    LOCK(pwallet->cs_wallet);
606
607
452
    const bool avoid_reuse = pwallet->IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE);
608
609
34.4k
    for (const COutput& out : vecOutputs) {
610
34.4k
        CTxDestination address;
611
34.4k
        const CScript& scriptPubKey = out.txout.scriptPubKey;
612
34.4k
        bool fValidAddress = ExtractDestination(scriptPubKey, address);
613
34.4k
        bool reused = avoid_reuse && pwallet->IsSpentKey(scriptPubKey);
614
615
34.4k
        if (destinations.size() && (!fValidAddress || !destinations.contains(address)))
616
212
            continue;
617
618
34.1k
        UniValue entry(UniValue::VOBJ);
619
34.1k
        entry.pushKV("txid", out.outpoint.hash.GetHex());
620
34.1k
        entry.pushKV("vout", out.outpoint.n);
621
622
34.1k
        if (fValidAddress) {
623
34.1k
            entry.pushKV("address", EncodeDestination(address));
624
625
34.1k
            const auto* address_book_entry = pwallet->FindAddressBookEntry(address);
626
34.1k
            if (address_book_entry) {
627
33.9k
                entry.pushKV("label", address_book_entry->GetLabel());
628
33.9k
            }
629
630
34.1k
            std::unique_ptr<SigningProvider> provider = pwallet->GetSolvingProvider(scriptPubKey);
631
34.1k
            if (provider) {
632
34.1k
                if (scriptPubKey.IsPayToScriptHash()) {
633
10.2k
                    const CScriptID hash = ToScriptID(std::get<ScriptHash>(address));
634
10.2k
                    CScript redeemScript;
635
10.2k
                    if (provider->GetCScript(hash, redeemScript)) {
636
10.2k
                        entry.pushKV("redeemScript", HexStr(redeemScript));
637
                        // Now check if the redeemScript is actually a P2WSH script
638
10.2k
                        CTxDestination witness_destination;
639
10.2k
                        if (redeemScript.IsPayToWitnessScriptHash()) {
640
2
                            bool extracted = ExtractDestination(redeemScript, witness_destination);
641
2
                            CHECK_NONFATAL(extracted);
642
                            // Also return the witness script
643
2
                            const WitnessV0ScriptHash& whash = std::get<WitnessV0ScriptHash>(witness_destination);
644
2
                            CScriptID id{RIPEMD160(whash)};
645
2
                            CScript witnessScript;
646
2
                            if (provider->GetCScript(id, witnessScript)) {
647
2
                                entry.pushKV("witnessScript", HexStr(witnessScript));
648
2
                            }
649
2
                        }
650
10.2k
                    }
651
23.9k
                } 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.1k
            }
660
34.1k
        }
661
662
34.1k
        entry.pushKV("scriptPubKey", HexStr(scriptPubKey));
663
34.1k
        entry.pushKV("amount", ValueFromAmount(out.txout.nValue));
664
34.1k
        entry.pushKV("confirmations", out.depth);
665
34.1k
        if (!out.depth) {
666
94
            size_t ancestor_count, unused_cluster_count, ancestor_size;
667
94
            CAmount ancestor_fees;
668
94
            pwallet->chain().getTransactionAncestry(out.outpoint.hash, ancestor_count, unused_cluster_count, &ancestor_size, &ancestor_fees);
669
94
            if (ancestor_count) {
670
94
                entry.pushKV("ancestorcount", ancestor_count);
671
94
                entry.pushKV("ancestorsize", ancestor_size);
672
94
                entry.pushKV("ancestorfees", ancestor_fees);
673
94
            }
674
94
        }
675
34.1k
        entry.pushKV("spendable", true); // Any coins we list are always spendable
676
34.1k
        entry.pushKV("solvable", out.solvable);
677
34.1k
        if (out.solvable) {
678
34.1k
            std::unique_ptr<SigningProvider> provider = pwallet->GetSolvingProvider(scriptPubKey);
679
34.1k
            if (provider) {
680
34.1k
                auto descriptor = InferDescriptor(scriptPubKey, *provider);
681
34.1k
                entry.pushKV("desc", descriptor->ToString());
682
34.1k
            }
683
34.1k
        }
684
34.1k
        PushParentDescriptors(*pwallet, scriptPubKey, entry);
685
34.1k
        if (avoid_reuse) entry.pushKV("reused", reused);
686
34.1k
        entry.pushKV("safe", out.safe);
687
34.1k
        results.push_back(std::move(entry));
688
34.1k
    }
689
690
452
    return results;
691
452
},
692
1.26k
    };
693
1.26k
}
694
} // namespace wallet