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