Coverage Report

Created: 2025-06-10 13:21

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