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