Coverage Report

Created: 2026-07-14 18:13

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