Coverage Report

Created: 2026-08-14 20:23

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